]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/mainloop.c
nothing to see here, move along
[mikachu/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) 2004        Mikael Magnusson
5    Copyright (c) 2003        Ben 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 "mainloop.h"
21 #include "action.h"
22 #include "client.h"
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <sys/select.h>
27 #include <signal.h>
28
29 typedef struct _ObMainLoopTimer             ObMainLoopTimer;
30 typedef struct _ObMainLoopSignal            ObMainLoopSignal;
31 typedef struct _ObMainLoopSignalHandlerType ObMainLoopSignalHandlerType;
32 typedef struct _ObMainLoopXHandlerType      ObMainLoopXHandlerType;
33 typedef struct _ObMainLoopFdHandlerType     ObMainLoopFdHandlerType;
34
35 /* this should be more than the number of possible signals on any
36    architecture... */
37 #define NUM_SIGNALS 99
38
39 /* all created ObMainLoops. Used by the signal handler to pass along signals */
40 static GSList *all_loops;
41
42 /* signals are global to all loops */
43 struct {
44     guint installed; /* a ref count */
45     struct sigaction oldact;
46 } all_signals[NUM_SIGNALS];
47
48 /* a set of all possible signals */
49 sigset_t all_signals_set;
50
51 /* signals which cause a core dump, these can't be used for callbacks */
52 static gint core_signals[] =
53 {
54     SIGABRT,
55     SIGSEGV,
56     SIGFPE,
57     SIGILL,
58     SIGQUIT,
59     SIGTRAP,
60     SIGSYS,
61     SIGBUS,
62     SIGXCPU,
63     SIGXFSZ
64 };
65 #define NUM_CORE_SIGNALS (sizeof(core_signals) / sizeof(core_signals[0]))
66
67 static void sighandler(gint sig);
68 static void timer_dispatch(ObMainLoop *loop, GTimeVal **wait);
69 static void fd_handler_destroy(gpointer data);
70
71 struct _ObMainLoop
72 {
73     Display *display;
74
75     gboolean run;     /* do keep running */
76     gboolean running; /* is still running */
77
78     GSList *x_handlers;
79
80     gint fd_x; /* The X fd is a special case! */
81     gint fd_max;
82     GHashTable *fd_handlers;
83     fd_set fd_set;
84
85     GSList *timers;
86     GTimeVal now;
87     GTimeVal ret_wait;
88
89     gboolean signal_fired;
90     guint signals_fired[NUM_SIGNALS];
91     GSList *signal_handlers[NUM_SIGNALS];
92
93     GSList *action_queue;
94 };
95
96 struct _ObMainLoopTimer
97 {
98     gulong delay;
99     GSourceFunc func;
100     gpointer data;
101     GDestroyNotify destroy;
102
103     /* The timer needs to be freed */
104     gboolean del_me;
105     /* The time the last fire should've been at */
106     GTimeVal last;
107     /* When this timer will next trigger */
108     GTimeVal timeout;
109 };
110
111 struct _ObMainLoopSignalHandlerType
112 {
113     ObMainLoop *loop;
114     gint signal;
115     gpointer data;
116     ObMainLoopSignalHandler func;
117     GDestroyNotify destroy;
118 };
119
120 struct _ObMainLoopXHandlerType
121 {
122     ObMainLoop *loop;
123     gpointer data;
124     ObMainLoopXHandler 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             timer_dispatch(loop, (GTimeVal**)&wait);
345
346             selset = loop->fd_set;
347             /* there is a small race condition here. if a signal occurs
348                between this if() and the select() then we will not process
349                the signal until 'wait' expires. possible solutions include
350                using GStaticMutex, and having the signal handler set 'wait'
351                to 0 */
352             if (!loop->signal_fired)
353                 select(loop->fd_max + 1, &selset, NULL, NULL, wait);
354
355             /* handle the X events with highest prioirity */
356             if (FD_ISSET(loop->fd_x, &selset))
357                 continue;
358
359             g_hash_table_foreach(loop->fd_handlers,
360                                  fd_handle_foreach, &selset);
361         }
362     }
363
364     client_remove_destructor(ob_main_loop_client_destroy);
365
366     loop->running = FALSE;
367 }
368
369 void ob_main_loop_exit(ObMainLoop *loop)
370 {
371     loop->run = FALSE;
372 }
373
374 /*** XEVENT WATCHERS ***/
375
376 void ob_main_loop_x_add(ObMainLoop *loop,
377                         ObMainLoopXHandler handler,
378                         gpointer data,
379                         GDestroyNotify notify)
380 {
381     ObMainLoopXHandlerType *h;
382
383     h = g_new(ObMainLoopXHandlerType, 1);
384     h->loop = loop;
385     h->func = handler;
386     h->data = data;
387     h->destroy = notify;
388     loop->x_handlers = g_slist_prepend(loop->x_handlers, h);
389 }
390
391 void ob_main_loop_x_remove(ObMainLoop *loop,
392                            ObMainLoopXHandler handler)
393 {
394     GSList *it, *next;
395
396     for (it = loop->x_handlers; it; it = next) {
397         ObMainLoopXHandlerType *h = it->data;
398         next = g_slist_next(it);
399         if (h->func == handler) {
400             loop->x_handlers = g_slist_delete_link(loop->x_handlers, it);
401             if (h->destroy) h->destroy(h->data);
402             g_free(h);
403         }
404     }
405 }
406
407 /*** SIGNAL WATCHERS ***/
408
409 static void sighandler(gint sig)
410 {
411     GSList *it;
412     guint i;
413
414     g_return_if_fail(sig < NUM_SIGNALS);
415
416     for (i = 0; i < NUM_CORE_SIGNALS; ++i)
417         if (sig == core_signals[i]) {
418             /* XXX special case for signals that default to core dump.
419                but throw some helpful output here... */
420
421             fprintf(stderr, "Fuck yah. Core dump. (Signal=%d)\n", sig);
422
423             /* die with a core dump */
424             abort();
425         }
426
427     for (it = all_loops; it; it = g_slist_next(it)) {
428         ObMainLoop *loop = it->data;
429         loop->signal_fired = TRUE;
430         loop->signals_fired[sig]++;
431     }
432 }
433
434 void ob_main_loop_signal_add(ObMainLoop *loop,
435                              gint signal,
436                              ObMainLoopSignalHandler handler,
437                              gpointer data,
438                              GDestroyNotify notify)
439 {
440     ObMainLoopSignalHandlerType *h;
441
442     g_return_if_fail(signal < NUM_SIGNALS);
443
444     h = g_new(ObMainLoopSignalHandlerType, 1);
445     h->loop = loop;
446     h->signal = signal;
447     h->func = handler;
448     h->data = data;
449     h->destroy = notify;
450     loop->signal_handlers[h->signal] =
451         g_slist_prepend(loop->signal_handlers[h->signal], h);
452
453     if (!all_signals[signal].installed) {
454         struct sigaction action;
455         sigset_t sigset;
456
457         sigemptyset(&sigset);
458         action.sa_handler = sighandler;
459         action.sa_mask = sigset;
460         action.sa_flags = SA_NOCLDSTOP;
461
462         sigaction(signal, &action, &all_signals[signal].oldact);
463     }
464
465     all_signals[signal].installed++;
466 }
467
468 void ob_main_loop_signal_remove(ObMainLoop *loop,
469                                 ObMainLoopSignalHandler handler)
470 {
471     guint i;
472     GSList *it, *next;
473
474     for (i = 0; i < NUM_SIGNALS; ++i) {
475         for (it = loop->signal_handlers[i]; it; it = next) {
476             ObMainLoopSignalHandlerType *h = it->data;
477
478             next = g_slist_next(it);
479
480             if (h->func == handler) {
481                 g_assert(all_signals[h->signal].installed > 0);
482
483                 all_signals[h->signal].installed--;
484                 if (!all_signals[h->signal].installed) {
485                     sigaction(h->signal, &all_signals[h->signal].oldact, NULL);
486                 }
487
488                 loop->signal_handlers[i] =
489                     g_slist_delete_link(loop->signal_handlers[i], it);
490                 if (h->destroy) h->destroy(h->data);
491
492                 g_free(h);
493             }
494         }
495     }
496
497 }
498
499 /*** FILE DESCRIPTOR WATCHERS ***/
500
501 static void max_fd_func(gpointer key, gpointer value, gpointer data)
502 {
503     ObMainLoop *loop = data;
504
505     /* key is the fd */
506     loop->fd_max = MAX(loop->fd_max, *(gint*)key);
507 }
508
509 static void calc_max_fd(ObMainLoop *loop)
510 {
511     loop->fd_max = loop->fd_x;
512
513     g_hash_table_foreach(loop->fd_handlers, max_fd_func, loop);
514 }
515
516 void ob_main_loop_fd_add(ObMainLoop *loop,
517                          gint fd,
518                          ObMainLoopFdHandler handler,
519                          gpointer data,
520                          GDestroyNotify notify)
521 {
522     ObMainLoopFdHandlerType *h;
523
524     h = g_new(ObMainLoopFdHandlerType, 1);
525     h->loop = loop;
526     h->fd = fd;
527     h->func = handler;
528     h->data = data;
529     h->destroy = notify;
530
531     g_hash_table_replace(loop->fd_handlers, &h->fd, h);
532     FD_SET(h->fd, &loop->fd_set);
533     calc_max_fd(loop);
534 }
535
536 static void fd_handler_destroy(gpointer data)
537 {
538     ObMainLoopFdHandlerType *h = data;
539
540     FD_CLR(h->fd, &h->loop->fd_set);
541
542     if (h->destroy)
543         h->destroy(h->data);
544 }
545
546 void ob_main_loop_fd_remove(ObMainLoop *loop,
547                             gint fd)
548 {
549     g_hash_table_remove(loop->fd_handlers, &fd);
550 }
551
552 /*** TIMEOUTS ***/
553
554 #define NEAREST_TIMEOUT(loop) \
555     (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
556
557 static glong timecompare(GTimeVal *a, GTimeVal *b)
558 {
559     glong r;
560
561     if ((r = b->tv_sec - a->tv_sec)) return r;
562     return b->tv_usec - a->tv_usec;
563     
564 }
565
566 static void insert_timer(ObMainLoop *loop, ObMainLoopTimer *ins)
567 {
568     GSList *it;
569     for (it = loop->timers; it; it = g_slist_next(it)) {
570         ObMainLoopTimer *t = it->data;
571         if (timecompare(&ins->timeout, &t->timeout) >= 0) {
572             loop->timers = g_slist_insert_before(loop->timers, it, ins);
573             break;
574         }
575     }
576     if (it == NULL) /* didnt fit anywhere in the list */
577         loop->timers = g_slist_append(loop->timers, ins);
578 }
579
580 void ob_main_loop_timeout_add(ObMainLoop *loop,
581                               gulong microseconds,
582                               GSourceFunc handler,
583                               gpointer data,
584                               GDestroyNotify notify)
585 {
586     ObMainLoopTimer *t = g_new(ObMainLoopTimer, 1);
587     t->delay = microseconds;
588     t->func = handler;
589     t->data = data;
590     t->destroy = notify;
591     t->del_me = FALSE;
592     g_get_current_time(&loop->now);
593     t->last = t->timeout = loop->now;
594     g_time_val_add(&t->timeout, t->delay);
595
596     insert_timer(loop, t);
597 }
598
599 void ob_main_loop_timeout_remove(ObMainLoop *loop,
600                                  GSourceFunc handler)
601 {
602     GSList *it;
603
604     for (it = loop->timers; it; it = g_slist_next(it)) {
605         ObMainLoopTimer *t = it->data;
606         if (t->func == handler)
607             t->del_me = TRUE;
608     }
609 }
610
611 void ob_main_loop_timeout_remove_data(ObMainLoop *loop,
612                                       GSourceFunc handler,
613                                       gpointer data)
614 {
615     GSList *it;
616
617     for (it = loop->timers; it; it = g_slist_next(it)) {
618         ObMainLoopTimer *t = it->data;
619         if (t->func == handler && t->data == data)
620             t->del_me = TRUE;
621     }
622 }
623
624 /* find the time to wait for the nearest timeout */
625 static gboolean nearest_timeout_wait(ObMainLoop *loop, GTimeVal *tm)
626 {
627   if (loop->timers == NULL)
628     return FALSE;
629
630   tm->tv_sec = NEAREST_TIMEOUT(loop).tv_sec - loop->now.tv_sec;
631   tm->tv_usec = NEAREST_TIMEOUT(loop).tv_usec - loop->now.tv_usec;
632
633   while (tm->tv_usec < 0) {
634     tm->tv_usec += G_USEC_PER_SEC;
635     tm->tv_sec--;
636   }
637   tm->tv_sec += tm->tv_usec / G_USEC_PER_SEC;
638   tm->tv_usec %= G_USEC_PER_SEC;
639   if (tm->tv_sec < 0)
640     tm->tv_sec = 0;
641
642   return TRUE;
643 }
644
645 static void timer_dispatch(ObMainLoop *loop, GTimeVal **wait)
646 {
647     GSList *it, *next;
648
649     gboolean fired = FALSE;
650
651     g_get_current_time(&loop->now);
652
653     for (it = loop->timers; it; it = next) {
654         ObMainLoopTimer *curr;
655         
656         next = g_slist_next(it);
657
658         curr = it->data;
659
660         /* since timer_stop doesn't actually free the timer, we have to do our
661            real freeing in here.
662         */
663         if (curr->del_me) {
664             /* delete the top */
665             loop->timers = g_slist_delete_link(loop->timers, it); 
666             if (curr->destroy)
667                 curr->destroy(curr->data);
668             g_free(curr);
669             continue;
670         }
671
672         /* the queue is sorted, so if this timer shouldn't fire, none are 
673            ready */
674         if (timecompare(&NEAREST_TIMEOUT(loop), &loop->now) < 0)
675             break;
676
677         /* we set the last fired time to delay msec after the previous firing,
678            then re-insert.  timers maintain their order and may trigger more
679            than once if they've waited more than one delay's worth of time.
680         */
681         loop->timers = g_slist_delete_link(loop->timers, it);
682         g_time_val_add(&curr->last, curr->delay);
683         if (curr->func(curr->data)) {
684             g_time_val_add(&curr->timeout, curr->delay);
685             insert_timer(loop, curr);
686         } else {
687             if (curr->destroy)
688                 curr->destroy(curr->data);
689             g_free(curr);
690         }
691
692         fired = TRUE;
693     }
694
695     if (fired) {
696         /* if at least one timer fires, then don't wait on X events, as there
697            may already be some in the queue from the timer callbacks.
698         */
699         loop->ret_wait.tv_sec = loop->ret_wait.tv_usec = 0;
700         *wait = &loop->ret_wait;
701     } else if (nearest_timeout_wait(loop, &loop->ret_wait))
702         *wait = &loop->ret_wait;
703     else
704         *wait = NULL;
705 }