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