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