]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/mainloop.c
consistant glib type usage
[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) 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 {
317             for (it = loop->x_handlers; it; it = g_slist_next(it)) {
318                 ObMainLoopXHandlerType *h = it->data;
319                 if (h->done_func)
320                     h->done_func(h->data);
321             }
322
323             if (loop->action_queue) {
324                 /* only fire off one action at a time, then go back for more
325                    X events, since the action might cause some X events (like
326                    FocusIn :) */
327
328                 do {
329                     act = loop->action_queue->data;
330                     if (act->data.any.client_action ==
331                         OB_CLIENT_ACTION_ALWAYS &&
332                         !act->data.any.c)
333                     {
334                         loop->action_queue =
335                             g_slist_delete_link(loop->action_queue,
336                                                 loop->action_queue);
337                         action_unref(act);
338                         act = NULL;
339                     }
340                 } while (!act && loop->action_queue);
341
342                 if  (act) {
343                     act->func(&act->data);
344                     loop->action_queue =
345                         g_slist_delete_link(loop->action_queue,
346                                             loop->action_queue);
347                     action_unref(act);
348                 }
349             } else {
350                 /* this only runs if there were no x events received */
351
352                 timer_dispatch(loop, (GTimeVal**)&wait);
353
354                 selset = loop->fd_set;
355                 /* there is a small race condition here. if a signal occurs
356                    between this if() and the select() then we will not process
357                    the signal until 'wait' expires. possible solutions include
358                    using GStaticMutex, and having the signal handler set 'wait'
359                    to 0 */
360                 if (!loop->signal_fired)
361                     select(loop->fd_max + 1, &selset, NULL, NULL, wait);
362
363                 /* handle the X events with highest prioirity */
364                 if (FD_ISSET(loop->fd_x, &selset))
365                     continue;
366
367                 g_hash_table_foreach(loop->fd_handlers,
368                                      fd_handle_foreach, &selset);
369             }
370         }
371     }
372
373     client_remove_destructor(ob_main_loop_client_destroy);
374
375     loop->running = FALSE;
376 }
377
378 void ob_main_loop_exit(ObMainLoop *loop)
379 {
380     loop->run = FALSE;
381 }
382
383 /*** XEVENT WATCHERS ***/
384
385 void ob_main_loop_x_add(ObMainLoop *loop,
386                         ObMainLoopXHandler handler,
387                         ObMainLoopXDoneHandler done_handler,
388                         gpointer data,
389                         GDestroyNotify notify)
390 {
391     ObMainLoopXHandlerType *h;
392
393     h = g_new(ObMainLoopXHandlerType, 1);
394     h->loop = loop;
395     h->func = handler;
396     h->done_func = done_handler;
397     h->data = data;
398     h->destroy = notify;
399     loop->x_handlers = g_slist_prepend(loop->x_handlers, h);
400 }
401
402 void ob_main_loop_x_remove(ObMainLoop *loop,
403                            ObMainLoopXHandler handler)
404 {
405     GSList *it, *next;
406
407     for (it = loop->x_handlers; it; it = next) {
408         ObMainLoopXHandlerType *h = it->data;
409         next = g_slist_next(it);
410         if (h->func == handler) {
411             loop->x_handlers = g_slist_delete_link(loop->x_handlers, it);
412             if (h->destroy) h->destroy(h->data);
413             g_free(h);
414         }
415     }
416 }
417
418 /*** SIGNAL WATCHERS ***/
419
420 static void sighandler(gint sig)
421 {
422     GSList *it;
423     guint i;
424
425     g_return_if_fail(sig < NUM_SIGNALS);
426
427     for (i = 0; i < NUM_CORE_SIGNALS; ++i)
428         if (sig == core_signals[i]) {
429             /* XXX special case for signals that default to core dump.
430                but throw some helpful output here... */
431
432             fprintf(stderr, "Fuck yah. Core dump. (Signal=%d)\n", sig);
433
434             /* die with a core dump */
435             abort();
436         }
437
438     for (it = all_loops; it; it = g_slist_next(it)) {
439         ObMainLoop *loop = it->data;
440         loop->signal_fired = TRUE;
441         loop->signals_fired[sig]++;
442     }
443 }
444
445 void ob_main_loop_signal_add(ObMainLoop *loop,
446                              gint signal,
447                              ObMainLoopSignalHandler handler,
448                              gpointer data,
449                              GDestroyNotify notify)
450 {
451     ObMainLoopSignalHandlerType *h;
452
453     g_return_if_fail(signal < NUM_SIGNALS);
454
455     h = g_new(ObMainLoopSignalHandlerType, 1);
456     h->loop = loop;
457     h->signal = signal;
458     h->func = handler;
459     h->data = data;
460     h->destroy = notify;
461     loop->signal_handlers[h->signal] =
462         g_slist_prepend(loop->signal_handlers[h->signal], h);
463
464     if (!all_signals[signal].installed) {
465         struct sigaction action;
466         sigset_t sigset;
467
468         sigemptyset(&sigset);
469         action.sa_handler = sighandler;
470         action.sa_mask = sigset;
471         action.sa_flags = SA_NOCLDSTOP;
472
473         sigaction(signal, &action, &all_signals[signal].oldact);
474     }
475
476     all_signals[signal].installed++;
477 }
478
479 void ob_main_loop_signal_remove(ObMainLoop *loop,
480                                 ObMainLoopSignalHandler handler)
481 {
482     guint i;
483     GSList *it, *next;
484
485     for (i = 0; i < NUM_SIGNALS; ++i) {
486         for (it = loop->signal_handlers[i]; it; it = next) {
487             ObMainLoopSignalHandlerType *h = it->data;
488
489             next = g_slist_next(it);
490
491             if (h->func == handler) {
492                 g_assert(all_signals[h->signal].installed > 0);
493
494                 all_signals[h->signal].installed--;
495                 if (!all_signals[h->signal].installed) {
496                     sigaction(h->signal, &all_signals[h->signal].oldact, NULL);
497                 }
498
499                 loop->signal_handlers[i] =
500                     g_slist_delete_link(loop->signal_handlers[i], it);
501                 if (h->destroy) h->destroy(h->data);
502
503                 g_free(h);
504             }
505         }
506     }
507
508 }
509
510 /*** FILE DESCRIPTOR WATCHERS ***/
511
512 static void max_fd_func(gpointer key, gpointer value, gpointer data)
513 {
514     ObMainLoop *loop = data;
515
516     /* key is the fd */
517     loop->fd_max = MAX(loop->fd_max, *(gint*)key);
518 }
519
520 static void calc_max_fd(ObMainLoop *loop)
521 {
522     loop->fd_max = loop->fd_x;
523
524     g_hash_table_foreach(loop->fd_handlers, max_fd_func, loop);
525 }
526
527 void ob_main_loop_fd_add(ObMainLoop *loop,
528                          gint fd,
529                          ObMainLoopFdHandler handler,
530                          gpointer data,
531                          GDestroyNotify notify)
532 {
533     ObMainLoopFdHandlerType *h;
534
535     h = g_new(ObMainLoopFdHandlerType, 1);
536     h->loop = loop;
537     h->fd = fd;
538     h->func = handler;
539     h->data = data;
540     h->destroy = notify;
541
542     g_hash_table_replace(loop->fd_handlers, &h->fd, h);
543     FD_SET(h->fd, &loop->fd_set);
544     calc_max_fd(loop);
545 }
546
547 static void fd_handler_destroy(gpointer data)
548 {
549     ObMainLoopFdHandlerType *h = data;
550
551     FD_CLR(h->fd, &h->loop->fd_set);
552
553     if (h->destroy)
554         h->destroy(h->data);
555 }
556
557 void ob_main_loop_fd_remove(ObMainLoop *loop,
558                             gint fd)
559 {
560     g_hash_table_remove(loop->fd_handlers, &fd);
561 }
562
563 /*** TIMEOUTS ***/
564
565 #define NEAREST_TIMEOUT(loop) \
566     (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
567
568 static glong timecompare(GTimeVal *a, GTimeVal *b)
569 {
570     glong r;
571
572     if ((r = b->tv_sec - a->tv_sec)) return r;
573     return b->tv_usec - a->tv_usec;
574     
575 }
576
577 static void insert_timer(ObMainLoop *loop, ObMainLoopTimer *ins)
578 {
579     GSList *it;
580     for (it = loop->timers; it; it = g_slist_next(it)) {
581         ObMainLoopTimer *t = it->data;
582         if (timecompare(&ins->timeout, &t->timeout) >= 0) {
583             loop->timers = g_slist_insert_before(loop->timers, it, ins);
584             break;
585         }
586     }
587     if (it == NULL) /* didnt fit anywhere in the list */
588         loop->timers = g_slist_append(loop->timers, ins);
589 }
590
591 void ob_main_loop_timeout_add(ObMainLoop *loop,
592                               gulong microseconds,
593                               GSourceFunc handler,
594                               gpointer data,
595                               GDestroyNotify notify)
596 {
597     ObMainLoopTimer *t = g_new(ObMainLoopTimer, 1);
598     t->delay = microseconds;
599     t->func = handler;
600     t->data = data;
601     t->destroy = notify;
602     t->del_me = FALSE;
603     g_get_current_time(&loop->now);
604     t->last = t->timeout = loop->now;
605     g_time_val_add(&t->timeout, t->delay);
606
607     insert_timer(loop, t);
608 }
609
610 void ob_main_loop_timeout_remove(ObMainLoop *loop,
611                                  GSourceFunc handler)
612 {
613     GSList *it;
614
615     for (it = loop->timers; it; it = g_slist_next(it)) {
616         ObMainLoopTimer *t = it->data;
617         if (t->func == handler)
618             t->del_me = TRUE;
619     }
620 }
621
622 void ob_main_loop_timeout_remove_data(ObMainLoop *loop,
623                                       GSourceFunc handler,
624                                       gpointer data)
625 {
626     GSList *it;
627
628     for (it = loop->timers; it; it = g_slist_next(it)) {
629         ObMainLoopTimer *t = it->data;
630         if (t->func == handler && t->data == data)
631             t->del_me = TRUE;
632     }
633 }
634
635 /* find the time to wait for the nearest timeout */
636 static gboolean nearest_timeout_wait(ObMainLoop *loop, GTimeVal *tm)
637 {
638   if (loop->timers == NULL)
639     return FALSE;
640
641   tm->tv_sec = NEAREST_TIMEOUT(loop).tv_sec - loop->now.tv_sec;
642   tm->tv_usec = NEAREST_TIMEOUT(loop).tv_usec - loop->now.tv_usec;
643
644   while (tm->tv_usec < 0) {
645     tm->tv_usec += G_USEC_PER_SEC;
646     tm->tv_sec--;
647   }
648   tm->tv_sec += tm->tv_usec / G_USEC_PER_SEC;
649   tm->tv_usec %= G_USEC_PER_SEC;
650   if (tm->tv_sec < 0)
651     tm->tv_sec = 0;
652
653   return TRUE;
654 }
655
656 static void timer_dispatch(ObMainLoop *loop, GTimeVal **wait)
657 {
658     GSList *it, *next;
659
660     gboolean fired = FALSE;
661
662     g_get_current_time(&loop->now);
663
664     for (it = loop->timers; it; it = next) {
665         ObMainLoopTimer *curr;
666         
667         next = g_slist_next(it);
668
669         curr = it->data;
670
671         /* since timer_stop doesn't actually free the timer, we have to do our
672            real freeing in here.
673         */
674         if (curr->del_me) {
675             /* delete the top */
676             loop->timers = g_slist_delete_link(loop->timers, it); 
677             if (curr->destroy)
678                 curr->destroy(curr->data);
679             g_free(curr);
680             continue;
681         }
682         
683         /* the queue is sorted, so if this timer shouldn't fire, none are 
684            ready */
685         if (timecompare(&NEAREST_TIMEOUT(loop), &loop->now) < 0)
686             break;
687
688         /* we set the last fired time to delay msec after the previous firing,
689            then re-insert.  timers maintain their order and may trigger more
690            than once if they've waited more than one delay's worth of time.
691         */
692         loop->timers = g_slist_delete_link(loop->timers, it);
693         g_time_val_add(&curr->last, curr->delay);
694         if (curr->func(curr->data)) {
695             g_time_val_add(&curr->timeout, curr->delay);
696             insert_timer(loop, curr);
697         } else {
698             if (curr->destroy)
699                 curr->destroy(curr->data);
700             g_free(curr);
701         }
702
703         fired = TRUE;
704     }
705
706     if (fired) {
707         /* if at least one timer fires, then don't wait on X events, as there
708            may already be some in the queue from the timer callbacks.
709         */
710         loop->ret_wait.tv_sec = loop->ret_wait.tv_usec = 0;
711         *wait = &loop->ret_wait;
712     } else if (nearest_timeout_wait(loop, &loop->ret_wait))
713         *wait = &loop->ret_wait;
714     else
715         *wait = NULL;
716 }