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