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