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