]> Dogcows Code - chaz/openbox/blob - obt/mainloop.c
Move the main loop out into the libobt
[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/util.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <sys/select.h>
26 #include <signal.h>
27
28 typedef struct _ObtMainLoopTimer ObtMainLoopTimer;
29 typedef struct _ObtMainLoopSignal ObtMainLoopSignal;
30 typedef struct _ObtMainLoopSignalHandlerType ObtMainLoopSignalHandlerType;
31 typedef struct _ObtMainLoopXHandlerType ObtMainLoopXHandlerType;
32 typedef struct _ObtMainLoopFdHandlerType ObtMainLoopFdHandlerType;
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 ObtMainLoops. Used by the signal handler to pass along
39 signals */
40 static GSList *all_loops;
41
42 /* signals are global to all loops */
43 static 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 static 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(ObtMainLoop *loop, GTimeVal **wait);
69 static void fd_handler_destroy(gpointer data);
70
71 struct _ObtMainLoop
72 {
73 gint ref;
74 Display *display;
75
76 gboolean run; /* do keep running */
77 gboolean running; /* is still running */
78
79 GSList *x_handlers;
80
81 gint fd_x; /* The X fd is a special case! */
82 gint fd_max;
83 GHashTable *fd_handlers;
84 fd_set fd_set;
85
86 GSList *timers;
87 GTimeVal now;
88 GTimeVal ret_wait;
89
90 gboolean signal_fired;
91 guint signals_fired[NUM_SIGNALS];
92 GSList *signal_handlers[NUM_SIGNALS];
93 };
94
95 struct _ObtMainLoopTimer
96 {
97 gulong delay;
98 GSourceFunc func;
99 gpointer data;
100 GEqualFunc equal;
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 /* Only allow a timer's function to fire once per run through the list,
111 so that it doesn't get locked in there forever */
112 gboolean fired;
113 };
114
115 struct _ObtMainLoopSignalHandlerType
116 {
117 ObtMainLoop *loop;
118 gint signal;
119 gpointer data;
120 ObtMainLoopSignalHandler func;
121 GDestroyNotify destroy;
122 };
123
124 struct _ObtMainLoopXHandlerType
125 {
126 ObtMainLoop *loop;
127 gpointer data;
128 ObtMainLoopXHandler func;
129 GDestroyNotify destroy;
130 };
131
132 struct _ObtMainLoopFdHandlerType
133 {
134 ObtMainLoop *loop;
135 gint fd;
136 gpointer data;
137 ObtMainLoopFdHandler func;
138 GDestroyNotify destroy;
139 };
140
141 ObtMainLoop *obt_main_loop_new(Display *display)
142 {
143 ObtMainLoop *loop;
144
145 loop = g_new0(ObtMainLoop, 1);
146 loop->ref = 1;
147 loop->display = display;
148 loop->fd_x = ConnectionNumber(display);
149 FD_ZERO(&loop->fd_set);
150 FD_SET(loop->fd_x, &loop->fd_set);
151 loop->fd_max = loop->fd_x;
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 loop->x_handlers = g_slist_prepend(loop->x_handlers, h);
343 }
344
345 void obt_main_loop_x_remove(ObtMainLoop *loop,
346 ObtMainLoopXHandler handler)
347 {
348 GSList *it, *next;
349
350 for (it = loop->x_handlers; it; it = next) {
351 ObtMainLoopXHandlerType *h = it->data;
352 next = g_slist_next(it);
353 if (h->func == handler) {
354 loop->x_handlers = g_slist_delete_link(loop->x_handlers, it);
355 if (h->destroy) h->destroy(h->data);
356 g_free(h);
357 }
358 }
359 }
360
361 /*** SIGNAL WATCHERS ***/
362
363 static void sighandler(gint sig)
364 {
365 GSList *it;
366 guint i;
367
368 g_return_if_fail(sig < NUM_SIGNALS);
369
370 for (i = 0; i < NUM_CORE_SIGNALS; ++i)
371 if (sig == core_signals[i]) {
372 /* XXX special case for signals that default to core dump.
373 but throw some helpful output here... */
374
375 fprintf(stderr, "How are you gentlemen? All your base are"
376 " belong to us. (Openbox received signal %d)\n", sig);
377
378 /* die with a core dump */
379 abort();
380 }
381
382 for (it = all_loops; it; it = g_slist_next(it)) {
383 ObtMainLoop *loop = it->data;
384 loop->signal_fired = TRUE;
385 loop->signals_fired[sig]++;
386 }
387 }
388
389 void obt_main_loop_signal_add(ObtMainLoop *loop,
390 gint signal,
391 ObtMainLoopSignalHandler handler,
392 gpointer data,
393 GDestroyNotify notify)
394 {
395 ObtMainLoopSignalHandlerType *h;
396
397 g_return_if_fail(signal < NUM_SIGNALS);
398
399 h = g_new(ObtMainLoopSignalHandlerType, 1);
400 h->loop = loop;
401 h->signal = signal;
402 h->func = handler;
403 h->data = data;
404 h->destroy = notify;
405 loop->signal_handlers[h->signal] =
406 g_slist_prepend(loop->signal_handlers[h->signal], h);
407
408 if (!all_signals[signal].installed) {
409 struct sigaction action;
410 sigset_t sigset;
411
412 sigemptyset(&sigset);
413 action.sa_handler = sighandler;
414 action.sa_mask = sigset;
415 action.sa_flags = SA_NOCLDSTOP;
416
417 sigaction(signal, &action, &all_signals[signal].oldact);
418 }
419
420 all_signals[signal].installed++;
421 }
422
423 void obt_main_loop_signal_remove(ObtMainLoop *loop,
424 ObtMainLoopSignalHandler handler)
425 {
426 guint i;
427 GSList *it, *next;
428
429 for (i = 0; i < NUM_SIGNALS; ++i) {
430 for (it = loop->signal_handlers[i]; it; it = next) {
431 ObtMainLoopSignalHandlerType *h = it->data;
432
433 next = g_slist_next(it);
434
435 if (h->func == handler) {
436 g_assert(all_signals[h->signal].installed > 0);
437
438 all_signals[h->signal].installed--;
439 if (!all_signals[h->signal].installed) {
440 sigaction(h->signal, &all_signals[h->signal].oldact, NULL);
441 }
442
443 loop->signal_handlers[i] =
444 g_slist_delete_link(loop->signal_handlers[i], it);
445 if (h->destroy) h->destroy(h->data);
446
447 g_free(h);
448 }
449 }
450 }
451
452 }
453
454 /*** FILE DESCRIPTOR WATCHERS ***/
455
456 static void max_fd_func(gpointer key, gpointer value, gpointer data)
457 {
458 ObtMainLoop *loop = data;
459
460 /* key is the fd */
461 loop->fd_max = MAX(loop->fd_max, *(gint*)key);
462 }
463
464 static void calc_max_fd(ObtMainLoop *loop)
465 {
466 loop->fd_max = loop->fd_x;
467
468 g_hash_table_foreach(loop->fd_handlers, max_fd_func, loop);
469 }
470
471 void obt_main_loop_fd_add(ObtMainLoop *loop,
472 gint fd,
473 ObtMainLoopFdHandler handler,
474 gpointer data,
475 GDestroyNotify notify)
476 {
477 ObtMainLoopFdHandlerType *h;
478
479 h = g_new(ObtMainLoopFdHandlerType, 1);
480 h->loop = loop;
481 h->fd = fd;
482 h->func = handler;
483 h->data = data;
484 h->destroy = notify;
485
486 g_hash_table_replace(loop->fd_handlers, &h->fd, h);
487 FD_SET(h->fd, &loop->fd_set);
488 calc_max_fd(loop);
489 }
490
491 static void fd_handler_destroy(gpointer data)
492 {
493 ObtMainLoopFdHandlerType *h = data;
494
495 FD_CLR(h->fd, &h->loop->fd_set);
496
497 if (h->destroy)
498 h->destroy(h->data);
499 }
500
501 void obt_main_loop_fd_remove(ObtMainLoop *loop,
502 gint fd)
503 {
504 g_hash_table_remove(loop->fd_handlers, &fd);
505 }
506
507 /*** TIMEOUTS ***/
508
509 #define NEAREST_TIMEOUT(loop) \
510 (((ObtMainLoopTimer*)(loop)->timers->data)->timeout)
511
512 static glong timecompare(GTimeVal *a, GTimeVal *b)
513 {
514 glong r;
515 if ((r = a->tv_sec - b->tv_sec)) return r;
516 return a->tv_usec - b->tv_usec;
517 }
518
519 static void insert_timer(ObtMainLoop *loop, ObtMainLoopTimer *ins)
520 {
521 GSList *it;
522 for (it = loop->timers; it; it = g_slist_next(it)) {
523 ObMainLoopTimer *t = it->data;
524 if (timecompare(&ins->timeout, &t->timeout) <= 0) {
525 loop->timers = g_slist_insert_before(loop->timers, it, ins);
526 break;
527 }
528 }
529 if (it == NULL) /* didnt fit anywhere in the list */
530 loop->timers = g_slist_append(loop->timers, ins);
531 }
532
533 void obt_main_loop_timeout_add(ObtMainLoop *loop,
534 gulong microseconds,
535 GSourceFunc handler,
536 gpointer data,
537 GEqualFunc cmp,
538 GDestroyNotify notify)
539 {
540 ObMainLoopTimer *t = g_new(ObMainLoopTimer, 1);
541
542 g_assert(microseconds > 0); /* if it's 0 it'll cause an infinite loop */
543
544 t->delay = microseconds;
545 t->func = handler;
546 t->data = data;
547 t->equal = cmp;
548 t->destroy = notify;
549 t->del_me = FALSE;
550 g_get_current_time(&loop->now);
551 t->last = t->timeout = loop->now;
552 g_time_val_add(&t->timeout, t->delay);
553
554 insert_timer(loop, t);
555 }
556
557 void obt_main_loop_timeout_remove(ObtMainLoop *loop,
558 GSourceFunc handler)
559 {
560 GSList *it;
561
562 for (it = loop->timers; it; it = g_slist_next(it)) {
563 ObtMainLoopTimer *t = it->data;
564 if (t->func == handler)
565 t->del_me = TRUE;
566 }
567 }
568
569 void obt_main_loop_timeout_remove_data(ObtMainLoop *loop, GSourceFunc handler,
570 gpointer data, gboolean cancel_dest)
571 {
572 GSList *it;
573
574 for (it = loop->timers; it; it = g_slist_next(it)) {
575 ObtMainLoopTimer *t = it->data;
576 if (t->func == handler && t->equal(t->data, data)) {
577 t->del_me = TRUE;
578 if (cancel_dest)
579 t->destroy = NULL;
580 }
581 }
582 }
583
584 /* find the time to wait for the nearest timeout */
585 static gboolean nearest_timeout_wait(ObtMainLoop *loop, GTimeVal *tm)
586 {
587 if (loop->timers == NULL)
588 return FALSE;
589
590 tm->tv_sec = NEAREST_TIMEOUT(loop).tv_sec - loop->now.tv_sec;
591 tm->tv_usec = NEAREST_TIMEOUT(loop).tv_usec - loop->now.tv_usec;
592
593 while (tm->tv_usec < 0) {
594 tm->tv_usec += G_USEC_PER_SEC;
595 tm->tv_sec--;
596 }
597 tm->tv_sec += tm->tv_usec / G_USEC_PER_SEC;
598 tm->tv_usec %= G_USEC_PER_SEC;
599 if (tm->tv_sec < 0)
600 tm->tv_sec = 0;
601
602 return TRUE;
603 }
604
605 static void timer_dispatch(ObtMainLoop *loop, GTimeVal **wait)
606 {
607 GSList *it, *next;
608
609 gboolean fired = FALSE;
610
611 g_get_current_time(&loop->now);
612
613 for (it = loop->timers; it; it = next) {
614 ObtMainLoopTimer *curr;
615
616 next = g_slist_next(it);
617
618 curr = it->data;
619
620 /* since timer_stop doesn't actually free the timer, we have to do our
621 real freeing in here.
622 */
623 if (curr->del_me) {
624 /* delete the top */
625 loop->timers = g_slist_delete_link(loop->timers, it);
626 if (curr->destroy)
627 curr->destroy(curr->data);
628 g_free(curr);
629 continue;
630 }
631
632 /* the queue is sorted, so if this timer shouldn't fire, none are
633 ready */
634 if (timecompare(&NEAREST_TIMEOUT(loop), &loop->now) > 0)
635 break;
636
637 /* we set the last fired time to delay msec after the previous firing,
638 then re-insert. timers maintain their order and may trigger more
639 than once if they've waited more than one delay's worth of time.
640 */
641 loop->timers = g_slist_delete_link(loop->timers, it);
642 g_time_val_add(&curr->last, curr->delay);
643 if (curr->func(curr->data)) {
644 g_time_val_add(&curr->timeout, curr->delay);
645 insert_timer(loop, curr);
646 } else {
647 if (curr->destroy)
648 curr->destroy(curr->data);
649 g_free(curr);
650 }
651
652 /* the timer queue has been shuffled, start from the beginning
653 (which is the next one to fire) */
654 next = loop->timers;
655
656 fired = TRUE;
657 }
658
659 if (fired) {
660 /* if at least one timer fires, then don't wait on X events, as there
661 may already be some in the queue from the timer callbacks.
662 */
663 loop->ret_wait.tv_sec = loop->ret_wait.tv_usec = 0;
664 *wait = &loop->ret_wait;
665 } else if (nearest_timeout_wait(loop, &loop->ret_wait))
666 *wait = &loop->ret_wait;
667 else
668 *wait = NULL;
669 }
This page took 0.06157 seconds and 5 git commands to generate.