]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/mainloop.c
add a slight delay to the focus/desktop switch dialogs. so if you hit the key really...
[mikachu/openbox.git] / openbox / mainloop.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    mainloop.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "mainloop.h"
21 #include "action.h"
22 #include "client.h"
23 #include "event.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <sys/select.h>
28 #include <signal.h>
29
30 typedef struct _ObMainLoopTimer             ObMainLoopTimer;
31 typedef struct _ObMainLoopSignal            ObMainLoopSignal;
32 typedef struct _ObMainLoopSignalHandlerType ObMainLoopSignalHandlerType;
33 typedef struct _ObMainLoopXHandlerType      ObMainLoopXHandlerType;
34 typedef struct _ObMainLoopFdHandlerType     ObMainLoopFdHandlerType;
35
36 /* this should be more than the number of possible signals on any
37    architecture... */
38 #define NUM_SIGNALS 99
39
40 /* all created ObMainLoops. Used by the signal handler to pass along signals */
41 static GSList *all_loops;
42
43 /* signals are global to all loops */
44 struct {
45     guint installed; /* a ref count */
46     struct sigaction oldact;
47 } all_signals[NUM_SIGNALS];
48
49 /* a set of all possible signals */
50 sigset_t all_signals_set;
51
52 /* signals which cause a core dump, these can't be used for callbacks */
53 static gint core_signals[] =
54 {
55     SIGABRT,
56     SIGSEGV,
57     SIGFPE,
58     SIGILL,
59     SIGQUIT,
60     SIGTRAP,
61     SIGSYS,
62     SIGBUS,
63     SIGXCPU,
64     SIGXFSZ
65 };
66 #define NUM_CORE_SIGNALS (sizeof(core_signals) / sizeof(core_signals[0]))
67
68 static void sighandler(gint sig);
69 static void timer_dispatch(ObMainLoop *loop, GTimeVal **wait);
70 static void fd_handler_destroy(gpointer data);
71
72 struct _ObMainLoop
73 {
74     Display *display;
75
76     gboolean run;     /* do keep running */
77     gboolean running; /* is still running */
78
79     GSList *x_handlers;
80
81     gint fd_x; /* The X fd is a special case! */
82     gint fd_max;
83     GHashTable *fd_handlers;
84     fd_set fd_set;
85
86     GSList *timers;
87     GTimeVal now;
88     GTimeVal ret_wait;
89
90     gboolean signal_fired;
91     guint signals_fired[NUM_SIGNALS];
92     GSList *signal_handlers[NUM_SIGNALS];
93
94     GSList *action_queue;
95 };
96
97 struct _ObMainLoopTimer
98 {
99     gulong delay;
100     GSourceFunc func;
101     gpointer data;
102     GEqualFunc equal;
103     GDestroyNotify destroy;
104
105     /* The timer needs to be freed */
106     gboolean del_me;
107     /* The time the last fire should've been at */
108     GTimeVal last;
109     /* When this timer will next trigger */
110     GTimeVal timeout;
111 };
112
113 struct _ObMainLoopSignalHandlerType
114 {
115     ObMainLoop *loop;
116     gint signal;
117     gpointer data;
118     ObMainLoopSignalHandler func;
119     GDestroyNotify destroy;
120 };
121
122 struct _ObMainLoopXHandlerType
123 {
124     ObMainLoop *loop;
125     gpointer data;
126     ObMainLoopXHandler func;
127     GDestroyNotify destroy;
128 };
129
130 struct _ObMainLoopFdHandlerType
131 {
132     ObMainLoop *loop;
133     gint fd;
134     gpointer data;
135     ObMainLoopFdHandler func;
136     GDestroyNotify destroy;
137 };
138
139 ObMainLoop *ob_main_loop_new(Display *display)
140 {
141     ObMainLoop *loop;
142
143     loop = g_new0(ObMainLoop, 1);
144     loop->display = display;
145     loop->fd_x = ConnectionNumber(display);
146     FD_ZERO(&loop->fd_set);
147     FD_SET(loop->fd_x, &loop->fd_set);
148     loop->fd_max = loop->fd_x;
149
150     loop->fd_handlers = g_hash_table_new_full(g_int_hash, g_int_equal,
151                                               NULL, fd_handler_destroy);
152
153     g_get_current_time(&loop->now);
154
155     /* only do this if we're the first loop created */
156     if (!all_loops) {
157         guint i;
158         struct sigaction action;
159         sigset_t sigset;
160
161         /* initialize the all_signals_set */
162         sigfillset(&all_signals_set);
163
164         sigemptyset(&sigset);
165         action.sa_handler = sighandler;
166         action.sa_mask = sigset;
167         action.sa_flags = SA_NOCLDSTOP;
168
169         /* grab all the signals that cause core dumps */
170         for (i = 0; i < NUM_CORE_SIGNALS; ++i) {
171             /* SIGABRT is curiously not grabbed here!! that's because when we
172                get one of the core_signals, we use abort() to dump the core.
173                And having the abort() only go back to our signal handler again
174                is less than optimal */
175             if (core_signals[i] != SIGABRT) {
176                 sigaction(core_signals[i], &action,
177                           &all_signals[core_signals[i]].oldact);
178                 all_signals[core_signals[i]].installed++;
179             }
180         }
181     }
182
183     all_loops = g_slist_prepend(all_loops, loop);
184
185     loop->action_queue = NULL;
186
187     return loop;
188 }
189
190 void ob_main_loop_destroy(ObMainLoop *loop)
191 {
192     guint i;
193     GSList *it, *next;
194
195     if (loop) {
196         g_assert(loop->running == FALSE);
197
198         for (it = loop->x_handlers; it; it = next) {
199             ObMainLoopXHandlerType *h = it->data;
200             next = g_slist_next(it);
201             ob_main_loop_x_remove(loop, h->func);
202         }
203
204         g_hash_table_destroy(loop->fd_handlers);
205
206         for (it = loop->timers; it; it = g_slist_next(it)) {
207             ObMainLoopTimer *t = it->data;
208             if (t->destroy) t->destroy(t->data);
209             g_free(t);
210         }
211         g_slist_free(loop->timers);
212         loop->timers = NULL;
213
214         for (i = 0; i < NUM_SIGNALS; ++i)
215             for (it = loop->signal_handlers[i]; it; it = next) {
216                 ObMainLoopSignalHandlerType *h = it->data;
217                 next = g_slist_next(it);
218                 ob_main_loop_signal_remove(loop, h->func);
219             }
220
221         all_loops = g_slist_remove(all_loops, loop);
222
223         /* only do this if we're the last loop destroyed */
224         if (!all_loops) {
225             guint i;
226
227             /* grab all the signals that cause core dumps */
228             for (i = 0; i < NUM_CORE_SIGNALS; ++i) {
229                 if (all_signals[core_signals[i]].installed) {
230                     sigaction(core_signals[i],
231                               &all_signals[core_signals[i]].oldact, NULL);
232                     all_signals[core_signals[i]].installed--;
233                 }
234             }
235         }
236
237         for (it = loop->action_queue; it; it = g_slist_next(it))
238             action_unref(it->data);
239         g_slist_free(loop->action_queue);
240
241         g_free(loop);
242     }
243 }
244
245 static void fd_handle_foreach(gpointer key,
246                               gpointer value,
247                               gpointer data)
248 {
249     ObMainLoopFdHandlerType *h = value;
250     fd_set *set = data;
251
252     if (FD_ISSET(h->fd, set))
253         h->func(h->fd, h->data);
254 }
255
256 void ob_main_loop_queue_action(ObMainLoop *loop, ObAction *act)
257 {
258     loop->action_queue = g_slist_append(loop->action_queue, action_copy(act));
259 }
260
261 static void ob_main_loop_client_destroy(ObClient *client, gpointer data)
262 {
263     ObMainLoop *loop = data;
264     GSList *it;
265
266     for (it = loop->action_queue; it; it = g_slist_next(it)) {
267         ObAction *act = it->data;
268
269         if (act->data.any.c == client)
270             act->data.any.c = NULL;
271     }
272 }
273
274 void ob_main_loop_run(ObMainLoop *loop)
275 {
276     XEvent e;
277     struct timeval *wait;
278     fd_set selset;
279     GSList *it;
280     ObAction *act;
281
282     loop->run = TRUE;
283     loop->running = TRUE;
284
285     client_add_destructor(ob_main_loop_client_destroy, loop);
286
287     while (loop->run) {
288         if (loop->signal_fired) {
289             guint i;
290             sigset_t oldset;
291
292             /* block signals so that we can do this without the data changing
293                on us */
294             sigprocmask(SIG_SETMASK, &all_signals_set, &oldset);
295
296             for (i = 0; i < NUM_SIGNALS; ++i) {
297                 while (loop->signals_fired[i]) {
298                     for (it = loop->signal_handlers[i];
299                             it; it = g_slist_next(it)) {
300                         ObMainLoopSignalHandlerType *h = it->data;
301                         h->func(i, h->data);
302                     }
303                     loop->signals_fired[i]--;
304                 }
305             }
306             loop->signal_fired = FALSE;
307
308             sigprocmask(SIG_SETMASK, &oldset, NULL);
309         } else if (XPending(loop->display)) {
310             do {
311                 XNextEvent(loop->display, &e);
312
313                 for (it = loop->x_handlers; it; it = g_slist_next(it)) {
314                     ObMainLoopXHandlerType *h = it->data;
315                     h->func(&e, h->data);
316                 }
317             } while (XPending(loop->display));
318         } else if (loop->action_queue) {
319             /* only fire off one action at a time, then go back for more
320                X events, since the action might cause some X events (like
321                FocusIn :) */
322
323             do {
324                 act = loop->action_queue->data;
325                 if (act->data.any.client_action == OB_CLIENT_ACTION_ALWAYS &&
326                     !act->data.any.c)
327                 {
328                     loop->action_queue =
329                         g_slist_delete_link(loop->action_queue,
330                                             loop->action_queue);
331                     action_unref(act);
332                     act = NULL;
333                 }
334             } while (!act && loop->action_queue);
335
336             if  (act) {
337                 event_curtime = act->data.any.time;
338                 act->func(&act->data);
339                 event_curtime = CurrentTime;
340                 loop->action_queue =
341                     g_slist_delete_link(loop->action_queue,
342                                         loop->action_queue);
343                 action_unref(act);
344             }
345         } else {
346             /* this only runs if there were no x events received */
347
348             timer_dispatch(loop, (GTimeVal**)&wait);
349
350             selset = loop->fd_set;
351             /* there is a small race condition here. if a signal occurs
352                between this if() and the select() then we will not process
353                the signal until 'wait' expires. possible solutions include
354                using GStaticMutex, and having the signal handler set 'wait'
355                to 0 */
356             if (!loop->signal_fired)
357                 select(loop->fd_max + 1, &selset, NULL, NULL, wait);
358
359             /* handle the X events with highest prioirity */
360             if (FD_ISSET(loop->fd_x, &selset))
361                 continue;
362
363             g_hash_table_foreach(loop->fd_handlers,
364                                  fd_handle_foreach, &selset);
365         }
366     }
367
368     client_remove_destructor(ob_main_loop_client_destroy);
369
370     loop->running = FALSE;
371 }
372
373 void ob_main_loop_exit(ObMainLoop *loop)
374 {
375     loop->run = FALSE;
376 }
377
378 /*** XEVENT WATCHERS ***/
379
380 void ob_main_loop_x_add(ObMainLoop *loop,
381                         ObMainLoopXHandler handler,
382                         gpointer data,
383                         GDestroyNotify notify)
384 {
385     ObMainLoopXHandlerType *h;
386
387     h = g_new(ObMainLoopXHandlerType, 1);
388     h->loop = loop;
389     h->func = handler;
390     h->data = data;
391     h->destroy = notify;
392     loop->x_handlers = g_slist_prepend(loop->x_handlers, h);
393 }
394
395 void ob_main_loop_x_remove(ObMainLoop *loop,
396                            ObMainLoopXHandler handler)
397 {
398     GSList *it, *next;
399
400     for (it = loop->x_handlers; it; it = next) {
401         ObMainLoopXHandlerType *h = it->data;
402         next = g_slist_next(it);
403         if (h->func == handler) {
404             loop->x_handlers = g_slist_delete_link(loop->x_handlers, it);
405             if (h->destroy) h->destroy(h->data);
406             g_free(h);
407         }
408     }
409 }
410
411 /*** SIGNAL WATCHERS ***/
412
413 static void sighandler(gint sig)
414 {
415     GSList *it;
416     guint i;
417
418     g_return_if_fail(sig < NUM_SIGNALS);
419
420     for (i = 0; i < NUM_CORE_SIGNALS; ++i)
421         if (sig == core_signals[i]) {
422             /* XXX special case for signals that default to core dump.
423                but throw some helpful output here... */
424
425             fprintf(stderr, "How are you gentlemen? All your base are"
426                     " belong to us. (Openbox received signal %d)\n", sig);
427
428             /* die with a core dump */
429             abort();
430         }
431
432     for (it = all_loops; it; it = g_slist_next(it)) {
433         ObMainLoop *loop = it->data;
434         loop->signal_fired = TRUE;
435         loop->signals_fired[sig]++;
436     }
437 }
438
439 void ob_main_loop_signal_add(ObMainLoop *loop,
440                              gint signal,
441                              ObMainLoopSignalHandler handler,
442                              gpointer data,
443                              GDestroyNotify notify)
444 {
445     ObMainLoopSignalHandlerType *h;
446
447     g_return_if_fail(signal < NUM_SIGNALS);
448
449     h = g_new(ObMainLoopSignalHandlerType, 1);
450     h->loop = loop;
451     h->signal = signal;
452     h->func = handler;
453     h->data = data;
454     h->destroy = notify;
455     loop->signal_handlers[h->signal] =
456         g_slist_prepend(loop->signal_handlers[h->signal], h);
457
458     if (!all_signals[signal].installed) {
459         struct sigaction action;
460         sigset_t sigset;
461
462         sigemptyset(&sigset);
463         action.sa_handler = sighandler;
464         action.sa_mask = sigset;
465         action.sa_flags = SA_NOCLDSTOP;
466
467         sigaction(signal, &action, &all_signals[signal].oldact);
468     }
469
470     all_signals[signal].installed++;
471 }
472
473 void ob_main_loop_signal_remove(ObMainLoop *loop,
474                                 ObMainLoopSignalHandler handler)
475 {
476     guint i;
477     GSList *it, *next;
478
479     for (i = 0; i < NUM_SIGNALS; ++i) {
480         for (it = loop->signal_handlers[i]; it; it = next) {
481             ObMainLoopSignalHandlerType *h = it->data;
482
483             next = g_slist_next(it);
484
485             if (h->func == handler) {
486                 g_assert(all_signals[h->signal].installed > 0);
487
488                 all_signals[h->signal].installed--;
489                 if (!all_signals[h->signal].installed) {
490                     sigaction(h->signal, &all_signals[h->signal].oldact, NULL);
491                 }
492
493                 loop->signal_handlers[i] =
494                     g_slist_delete_link(loop->signal_handlers[i], it);
495                 if (h->destroy) h->destroy(h->data);
496
497                 g_free(h);
498             }
499         }
500     }
501
502 }
503
504 /*** FILE DESCRIPTOR WATCHERS ***/
505
506 static void max_fd_func(gpointer key, gpointer value, gpointer data)
507 {
508     ObMainLoop *loop = data;
509
510     /* key is the fd */
511     loop->fd_max = MAX(loop->fd_max, *(gint*)key);
512 }
513
514 static void calc_max_fd(ObMainLoop *loop)
515 {
516     loop->fd_max = loop->fd_x;
517
518     g_hash_table_foreach(loop->fd_handlers, max_fd_func, loop);
519 }
520
521 void ob_main_loop_fd_add(ObMainLoop *loop,
522                          gint fd,
523                          ObMainLoopFdHandler handler,
524                          gpointer data,
525                          GDestroyNotify notify)
526 {
527     ObMainLoopFdHandlerType *h;
528
529     h = g_new(ObMainLoopFdHandlerType, 1);
530     h->loop = loop;
531     h->fd = fd;
532     h->func = handler;
533     h->data = data;
534     h->destroy = notify;
535
536     g_hash_table_replace(loop->fd_handlers, &h->fd, h);
537     FD_SET(h->fd, &loop->fd_set);
538     calc_max_fd(loop);
539 }
540
541 static void fd_handler_destroy(gpointer data)
542 {
543     ObMainLoopFdHandlerType *h = data;
544
545     FD_CLR(h->fd, &h->loop->fd_set);
546
547     if (h->destroy)
548         h->destroy(h->data);
549 }
550
551 void ob_main_loop_fd_remove(ObMainLoop *loop,
552                             gint fd)
553 {
554     g_hash_table_remove(loop->fd_handlers, &fd);
555 }
556
557 /*** TIMEOUTS ***/
558
559 #define NEAREST_TIMEOUT(loop) \
560     (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
561
562 static glong timecompare(GTimeVal *a, GTimeVal *b)
563 {
564     glong r;
565
566     if ((r = b->tv_sec - a->tv_sec)) return r;
567     return b->tv_usec - a->tv_usec;
568     
569 }
570
571 static void insert_timer(ObMainLoop *loop, ObMainLoopTimer *ins)
572 {
573     GSList *it;
574     for (it = loop->timers; it; it = g_slist_next(it)) {
575         ObMainLoopTimer *t = it->data;
576         if (timecompare(&ins->timeout, &t->timeout) >= 0) {
577             loop->timers = g_slist_insert_before(loop->timers, it, ins);
578             break;
579         }
580     }
581     if (it == NULL) /* didnt fit anywhere in the list */
582         loop->timers = g_slist_append(loop->timers, ins);
583 }
584
585 void ob_main_loop_timeout_add(ObMainLoop *loop,
586                               gulong microseconds,
587                               GSourceFunc handler,
588                               gpointer data,
589                               GEqualFunc cmp,
590                               GDestroyNotify notify)
591 {
592     ObMainLoopTimer *t = g_new(ObMainLoopTimer, 1);
593     t->delay = microseconds;
594     t->func = handler;
595     t->data = data;
596     t->equal = cmp;
597     t->destroy = notify;
598     t->del_me = FALSE;
599     g_get_current_time(&loop->now);
600     t->last = t->timeout = loop->now;
601     g_time_val_add(&t->timeout, t->delay);
602
603     insert_timer(loop, t);
604 }
605
606 void ob_main_loop_timeout_remove(ObMainLoop *loop,
607                                  GSourceFunc handler)
608 {
609     GSList *it;
610
611     for (it = loop->timers; it; it = g_slist_next(it)) {
612         ObMainLoopTimer *t = it->data;
613         if (t->func == handler)
614             t->del_me = TRUE;
615     }
616 }
617
618 void ob_main_loop_timeout_remove_data(ObMainLoop *loop, GSourceFunc handler,
619                                       gpointer data, gboolean cancel_dest)
620 {
621     GSList *it;
622
623     for (it = loop->timers; it; it = g_slist_next(it)) {
624         ObMainLoopTimer *t = it->data;
625         if (t->func == handler && t->equal(t->data, data)) {
626             t->del_me = TRUE;
627             if (cancel_dest)
628                 t->destroy = NULL;
629         }
630     }
631 }
632
633 /* find the time to wait for the nearest timeout */
634 static gboolean nearest_timeout_wait(ObMainLoop *loop, GTimeVal *tm)
635 {
636   if (loop->timers == NULL)
637     return FALSE;
638
639   tm->tv_sec = NEAREST_TIMEOUT(loop).tv_sec - loop->now.tv_sec;
640   tm->tv_usec = NEAREST_TIMEOUT(loop).tv_usec - loop->now.tv_usec;
641
642   while (tm->tv_usec < 0) {
643     tm->tv_usec += G_USEC_PER_SEC;
644     tm->tv_sec--;
645   }
646   tm->tv_sec += tm->tv_usec / G_USEC_PER_SEC;
647   tm->tv_usec %= G_USEC_PER_SEC;
648   if (tm->tv_sec < 0)
649     tm->tv_sec = 0;
650
651   return TRUE;
652 }
653
654 static void timer_dispatch(ObMainLoop *loop, GTimeVal **wait)
655 {
656     GSList *it, *next;
657
658     gboolean fired = FALSE;
659
660     g_get_current_time(&loop->now);
661
662     for (it = loop->timers; it; it = next) {
663         ObMainLoopTimer *curr;
664         
665         next = g_slist_next(it);
666
667         curr = it->data;
668
669         /* since timer_stop doesn't actually free the timer, we have to do our
670            real freeing in here.
671         */
672         if (curr->del_me) {
673             /* delete the top */
674             loop->timers = g_slist_delete_link(loop->timers, it); 
675             if (curr->destroy)
676                 curr->destroy(curr->data);
677             g_free(curr);
678             continue;
679         }
680
681         /* the queue is sorted, so if this timer shouldn't fire, none are 
682            ready */
683         if (timecompare(&NEAREST_TIMEOUT(loop), &loop->now) < 0)
684             break;
685
686         /* we set the last fired time to delay msec after the previous firing,
687            then re-insert.  timers maintain their order and may trigger more
688            than once if they've waited more than one delay's worth of time.
689         */
690         loop->timers = g_slist_delete_link(loop->timers, it);
691         g_time_val_add(&curr->last, curr->delay);
692         if (curr->func(curr->data)) {
693             g_time_val_add(&curr->timeout, curr->delay);
694             insert_timer(loop, curr);
695         } else {
696             if (curr->destroy)
697                 curr->destroy(curr->data);
698             g_free(curr);
699         }
700
701         fired = TRUE;
702     }
703
704     if (fired) {
705         /* if at least one timer fires, then don't wait on X events, as there
706            may already be some in the queue from the timer callbacks.
707         */
708         loop->ret_wait.tv_sec = loop->ret_wait.tv_usec = 0;
709         *wait = &loop->ret_wait;
710     } else if (nearest_timeout_wait(loop, &loop->ret_wait))
711         *wait = &loop->ret_wait;
712     else
713         *wait = NULL;
714 }