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