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