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