]> Dogcows Code - chaz/openbox/blob - openbox/mainloop.c
queue up ObActions and run them when idle
[chaz/openbox] / 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
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <sys/select.h>
25 #include <signal.h>
26
27 typedef struct _ObMainLoopTimer ObMainLoopTimer;
28 typedef struct _ObMainLoopSignal ObMainLoopSignal;
29 typedef struct _ObMainLoopSignalHandlerType ObMainLoopSignalHandlerType;
30 typedef struct _ObMainLoopXHandlerType ObMainLoopXHandlerType;
31 typedef struct _ObMainLoopFdHandlerType ObMainLoopFdHandlerType;
32
33 /* this should be more than the number of possible signals on any
34 architecture... */
35 #define NUM_SIGNALS 99
36
37 /* all created ObMainLoops. Used by the signal handler to pass along signals */
38 static GSList *all_loops;
39
40 /* signals are global to all loops */
41 struct {
42 guint installed; /* a ref count */
43 struct sigaction oldact;
44 } all_signals[NUM_SIGNALS];
45
46 /* a set of all possible signals */
47 sigset_t all_signals_set;
48
49 /* signals which cause a core dump, these can't be used for callbacks */
50 static gint core_signals[] =
51 {
52 SIGABRT,
53 SIGSEGV,
54 SIGFPE,
55 SIGILL,
56 SIGQUIT,
57 SIGTRAP,
58 SIGSYS,
59 SIGBUS,
60 SIGXCPU,
61 SIGXFSZ
62 };
63 #define NUM_CORE_SIGNALS (sizeof(core_signals) / sizeof(core_signals[0]))
64
65 static void sighandler(gint sig);
66 static void timer_dispatch(ObMainLoop *loop, GTimeVal **wait);
67 static void fd_handler_destroy(gpointer data);
68
69 struct _ObMainLoop
70 {
71 Display *display;
72
73 gboolean run; /* do keep running */
74 gboolean running; /* is still running */
75
76 GSList *x_handlers;
77
78 gint fd_x; /* The X fd is a special case! */
79 gint fd_max;
80 GHashTable *fd_handlers;
81 fd_set fd_set;
82
83 GSList *timers;
84 GTimeVal now;
85 GTimeVal ret_wait;
86
87 gboolean signal_fired;
88 guint signals_fired[NUM_SIGNALS];
89 GSList *signal_handlers[NUM_SIGNALS];
90
91 GQueue *action_queue;
92 };
93
94 struct _ObMainLoopTimer
95 {
96 gulong delay;
97 GSourceFunc func;
98 gpointer data;
99 GDestroyNotify destroy;
100
101 /* The timer needs to be freed */
102 gboolean del_me;
103 /* The time the last fire should've been at */
104 GTimeVal last;
105 /* When this timer will next trigger */
106 GTimeVal timeout;
107 };
108
109 struct _ObMainLoopSignalHandlerType
110 {
111 ObMainLoop *loop;
112 gint signal;
113 gpointer data;
114 ObMainLoopSignalHandler func;
115 GDestroyNotify destroy;
116 };
117
118 struct _ObMainLoopXHandlerType
119 {
120 ObMainLoop *loop;
121 gpointer data;
122 ObMainLoopXHandler func;
123 GDestroyNotify destroy;
124 };
125
126 struct _ObMainLoopFdHandlerType
127 {
128 ObMainLoop *loop;
129 gint fd;
130 gpointer data;
131 ObMainLoopFdHandler func;
132 GDestroyNotify destroy;
133 };
134
135 ObMainLoop *ob_main_loop_new(Display *display)
136 {
137 ObMainLoop *loop;
138
139 loop = g_new0(ObMainLoop, 1);
140 loop->display = display;
141 loop->fd_x = ConnectionNumber(display);
142 FD_ZERO(&loop->fd_set);
143 FD_SET(loop->fd_x, &loop->fd_set);
144 loop->fd_max = loop->fd_x;
145
146 loop->fd_handlers = g_hash_table_new_full(g_int_hash, g_int_equal,
147 NULL, fd_handler_destroy);
148
149 g_get_current_time(&loop->now);
150
151 /* only do this if we're the first loop created */
152 if (!all_loops) {
153 guint i;
154 struct sigaction action;
155 sigset_t sigset;
156
157 /* initialize the all_signals_set */
158 sigfillset(&all_signals_set);
159
160 sigemptyset(&sigset);
161 action.sa_handler = sighandler;
162 action.sa_mask = sigset;
163 action.sa_flags = SA_NOCLDSTOP;
164
165 /* grab all the signals that cause core dumps */
166 for (i = 0; i < NUM_CORE_SIGNALS; ++i) {
167 /* SIGABRT is curiously not grabbed here!! that's because when we
168 get one of the core_signals, we use abort() to dump the core.
169 And having the abort() only go back to our signal handler again
170 is less than optimal */
171 if (core_signals[i] != SIGABRT) {
172 sigaction(core_signals[i], &action,
173 &all_signals[core_signals[i]].oldact);
174 all_signals[core_signals[i]].installed++;
175 }
176 }
177 }
178
179 all_loops = g_slist_prepend(all_loops, loop);
180
181 loop->action_queue = g_queue_new();
182
183 return loop;
184 }
185
186 void ob_main_loop_destroy(ObMainLoop *loop)
187 {
188 guint i;
189 GSList *it, *next;
190
191 if (loop) {
192 g_assert(loop->running == FALSE);
193
194 for (it = loop->x_handlers; it; it = next) {
195 ObMainLoopXHandlerType *h = it->data;
196 next = g_slist_next(it);
197 ob_main_loop_x_remove(loop, h->func);
198 }
199
200 g_hash_table_destroy(loop->fd_handlers);
201
202 for (it = loop->timers; it; it = g_slist_next(it)) {
203 ObMainLoopTimer *t = it->data;
204 if (t->destroy) t->destroy(t->data);
205 g_free(t);
206 }
207 g_slist_free(loop->timers);
208 loop->timers = NULL;
209
210 for (i = 0; i < NUM_SIGNALS; ++i)
211 for (it = loop->signal_handlers[i]; it; it = next) {
212 ObMainLoopSignalHandlerType *h = it->data;
213 next = g_slist_next(it);
214 ob_main_loop_signal_remove(loop, h->func);
215 }
216
217 all_loops = g_slist_remove(all_loops, loop);
218
219 /* only do this if we're the last loop destroyed */
220 if (!all_loops) {
221 guint i;
222
223 /* grab all the signals that cause core dumps */
224 for (i = 0; i < NUM_CORE_SIGNALS; ++i) {
225 if (all_signals[core_signals[i]].installed) {
226 sigaction(core_signals[i],
227 &all_signals[core_signals[i]].oldact, NULL);
228 all_signals[core_signals[i]].installed--;
229 }
230 }
231 }
232
233 g_queue_free(loop->action_queue);
234
235 g_free(loop);
236 }
237 }
238
239 static void fd_handle_foreach(gpointer key,
240 gpointer value,
241 gpointer data)
242 {
243 ObMainLoopFdHandlerType *h = value;
244 fd_set *set = data;
245
246 if (FD_ISSET(h->fd, set))
247 h->func(h->fd, h->data);
248 }
249
250 void ob_main_loop_queue_action(ObMainLoop *loop, ObAction *act)
251 {
252 g_queue_push_tail(loop->action_queue, action_copy(act));
253 }
254
255 void ob_main_loop_run(ObMainLoop *loop)
256 {
257 XEvent e;
258 struct timeval *wait;
259 fd_set selset;
260 GSList *it;
261 ObAction *act;
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 ObMainLoopSignalHandlerType *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 (XPending(loop->display)) {
289 do {
290 XNextEvent(loop->display, &e);
291
292 for (it = loop->x_handlers; it; it = g_slist_next(it)) {
293 ObMainLoopXHandlerType *h = it->data;
294 h->func(&e, h->data);
295 }
296 } while (XPending(loop->display));
297 } else if ((act = g_queue_pop_head(loop->action_queue))) {
298 /* only fire off one action at a time, then go back for more
299 X events, since the action might cause some X events (like
300 FocusIn :) */
301 act->func(&act->data);
302 action_unref(act);
303 } else {
304 /* this only runs if there were no x events received */
305
306 timer_dispatch(loop, (GTimeVal**)&wait);
307
308 selset = loop->fd_set;
309 /* there is a small race condition here. if a signal occurs
310 between this if() and the select() then we will not process
311 the signal until 'wait' expires. possible solutions include
312 using GStaticMutex, and having the signal handler set 'wait'
313 to 0 */
314 if (!loop->signal_fired)
315 select(loop->fd_max + 1, &selset, NULL, NULL, wait);
316
317 /* handle the X events with highest prioirity */
318 if (FD_ISSET(loop->fd_x, &selset))
319 continue;
320
321 g_hash_table_foreach(loop->fd_handlers,
322 fd_handle_foreach, &selset);
323 }
324 }
325
326 loop->running = FALSE;
327 }
328
329 void ob_main_loop_exit(ObMainLoop *loop)
330 {
331 loop->run = FALSE;
332 }
333
334 /*** XEVENT WATCHERS ***/
335
336 void ob_main_loop_x_add(ObMainLoop *loop,
337 ObMainLoopXHandler handler,
338 gpointer data,
339 GDestroyNotify notify)
340 {
341 ObMainLoopXHandlerType *h;
342
343 h = g_new(ObMainLoopXHandlerType, 1);
344 h->loop = loop;
345 h->func = handler;
346 h->data = data;
347 h->destroy = notify;
348 loop->x_handlers = g_slist_prepend(loop->x_handlers, h);
349 }
350
351 void ob_main_loop_x_remove(ObMainLoop *loop,
352 ObMainLoopXHandler handler)
353 {
354 GSList *it, *next;
355
356 for (it = loop->x_handlers; it; it = next) {
357 ObMainLoopXHandlerType *h = it->data;
358 next = g_slist_next(it);
359 if (h->func == handler) {
360 loop->x_handlers = g_slist_delete_link(loop->x_handlers, it);
361 if (h->destroy) h->destroy(h->data);
362 g_free(h);
363 }
364 }
365 }
366
367 /*** SIGNAL WATCHERS ***/
368
369 static void sighandler(gint sig)
370 {
371 GSList *it;
372 guint i;
373
374 g_return_if_fail(sig < NUM_SIGNALS);
375
376 for (i = 0; i < NUM_CORE_SIGNALS; ++i)
377 if (sig == core_signals[i]) {
378 /* XXX special case for signals that default to core dump.
379 but throw some helpful output here... */
380
381 fprintf(stderr, "Fuck yah. Core dump. (Signal=%d)\n", sig);
382
383 /* die with a core dump */
384 abort();
385 }
386
387 for (it = all_loops; it; it = g_slist_next(it)) {
388 ObMainLoop *loop = it->data;
389 loop->signal_fired = TRUE;
390 loop->signals_fired[sig]++;
391 }
392 }
393
394 void ob_main_loop_signal_add(ObMainLoop *loop,
395 gint signal,
396 ObMainLoopSignalHandler handler,
397 gpointer data,
398 GDestroyNotify notify)
399 {
400 ObMainLoopSignalHandlerType *h;
401
402 g_return_if_fail(signal < NUM_SIGNALS);
403
404 h = g_new(ObMainLoopSignalHandlerType, 1);
405 h->loop = loop;
406 h->signal = signal;
407 h->func = handler;
408 h->data = data;
409 h->destroy = notify;
410 loop->signal_handlers[h->signal] =
411 g_slist_prepend(loop->signal_handlers[h->signal], h);
412
413 if (!all_signals[signal].installed) {
414 struct sigaction action;
415 sigset_t sigset;
416
417 sigemptyset(&sigset);
418 action.sa_handler = sighandler;
419 action.sa_mask = sigset;
420 action.sa_flags = SA_NOCLDSTOP;
421
422 sigaction(signal, &action, &all_signals[signal].oldact);
423 }
424
425 all_signals[signal].installed++;
426 }
427
428 void ob_main_loop_signal_remove(ObMainLoop *loop,
429 ObMainLoopSignalHandler handler)
430 {
431 guint i;
432 GSList *it, *next;
433
434 for (i = 0; i < NUM_SIGNALS; ++i) {
435 for (it = loop->signal_handlers[i]; it; it = next) {
436 ObMainLoopSignalHandlerType *h = it->data;
437
438 next = g_slist_next(it);
439
440 if (h->func == handler) {
441 g_assert(all_signals[h->signal].installed > 0);
442
443 all_signals[h->signal].installed--;
444 if (!all_signals[h->signal].installed) {
445 sigaction(h->signal, &all_signals[h->signal].oldact, NULL);
446 }
447
448 loop->signal_handlers[i] =
449 g_slist_delete_link(loop->signal_handlers[i], it);
450 if (h->destroy) h->destroy(h->data);
451
452 g_free(h);
453 }
454 }
455 }
456
457 }
458
459 /*** FILE DESCRIPTOR WATCHERS ***/
460
461 static void max_fd_func(gpointer key, gpointer value, gpointer data)
462 {
463 ObMainLoop *loop = data;
464
465 /* key is the fd */
466 loop->fd_max = MAX(loop->fd_max, *(gint*)key);
467 }
468
469 static void calc_max_fd(ObMainLoop *loop)
470 {
471 loop->fd_max = loop->fd_x;
472
473 g_hash_table_foreach(loop->fd_handlers, max_fd_func, loop);
474 }
475
476 void ob_main_loop_fd_add(ObMainLoop *loop,
477 gint fd,
478 ObMainLoopFdHandler handler,
479 gpointer data,
480 GDestroyNotify notify)
481 {
482 ObMainLoopFdHandlerType *h;
483
484 h = g_new(ObMainLoopFdHandlerType, 1);
485 h->loop = loop;
486 h->fd = fd;
487 h->func = handler;
488 h->data = data;
489 h->destroy = notify;
490
491 g_hash_table_replace(loop->fd_handlers, &h->fd, h);
492 FD_SET(h->fd, &loop->fd_set);
493 calc_max_fd(loop);
494 }
495
496 static void fd_handler_destroy(gpointer data)
497 {
498 ObMainLoopFdHandlerType *h = data;
499
500 FD_CLR(h->fd, &h->loop->fd_set);
501
502 if (h->destroy)
503 h->destroy(h->data);
504 }
505
506 void ob_main_loop_fd_remove(ObMainLoop *loop,
507 gint fd)
508 {
509 g_hash_table_remove(loop->fd_handlers, &fd);
510 }
511
512 /*** TIMEOUTS ***/
513
514 #define NEAREST_TIMEOUT(loop) \
515 (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
516
517 static long timecompare(GTimeVal *a, GTimeVal *b)
518 {
519 long r;
520
521 if ((r = b->tv_sec - a->tv_sec)) return r;
522 return b->tv_usec - a->tv_usec;
523
524 }
525
526 static void insert_timer(ObMainLoop *loop, ObMainLoopTimer *ins)
527 {
528 GSList *it;
529 for (it = loop->timers; it; it = g_slist_next(it)) {
530 ObMainLoopTimer *t = it->data;
531 if (timecompare(&ins->timeout, &t->timeout) >= 0) {
532 loop->timers = g_slist_insert_before(loop->timers, it, ins);
533 break;
534 }
535 }
536 if (it == NULL) /* didnt fit anywhere in the list */
537 loop->timers = g_slist_append(loop->timers, ins);
538 }
539
540 void ob_main_loop_timeout_add(ObMainLoop *loop,
541 gulong microseconds,
542 GSourceFunc handler,
543 gpointer data,
544 GDestroyNotify notify)
545 {
546 ObMainLoopTimer *t = g_new(ObMainLoopTimer, 1);
547 t->delay = microseconds;
548 t->func = handler;
549 t->data = data;
550 t->destroy = notify;
551 t->del_me = FALSE;
552 g_get_current_time(&loop->now);
553 t->last = t->timeout = loop->now;
554 g_time_val_add(&t->timeout, t->delay);
555
556 insert_timer(loop, t);
557 }
558
559 void ob_main_loop_timeout_remove(ObMainLoop *loop,
560 GSourceFunc handler)
561 {
562 GSList *it;
563
564 for (it = loop->timers; it; it = g_slist_next(it)) {
565 ObMainLoopTimer *t = it->data;
566 if (t->func == handler)
567 t->del_me = TRUE;
568 }
569 }
570
571 void ob_main_loop_timeout_remove_data(ObMainLoop *loop,
572 GSourceFunc handler,
573 gpointer data)
574 {
575 GSList *it;
576
577 for (it = loop->timers; it; it = g_slist_next(it)) {
578 ObMainLoopTimer *t = it->data;
579 if (t->func == handler && t->data == data)
580 t->del_me = TRUE;
581 }
582 }
583
584 /* find the time to wait for the nearest timeout */
585 static gboolean nearest_timeout_wait(ObMainLoop *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(ObMainLoop *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 ObMainLoopTimer *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 g_free(curr);
627 continue;
628 }
629
630 /* the queue is sorted, so if this timer shouldn't fire, none are
631 ready */
632 if (timecompare(&NEAREST_TIMEOUT(loop), &loop->now) < 0)
633 break;
634
635 /* we set the last fired time to delay msec after the previous firing,
636 then re-insert. timers maintain their order and may trigger more
637 than once if they've waited more than one delay's worth of time.
638 */
639 loop->timers = g_slist_delete_link(loop->timers, it);
640 g_time_val_add(&curr->last, curr->delay);
641 if (curr->func(curr->data)) {
642 g_time_val_add(&curr->timeout, curr->delay);
643 insert_timer(loop, curr);
644 } else {
645 if (curr->destroy)
646 curr->destroy(curr->data);
647 g_free(curr);
648 }
649
650 fired = TRUE;
651 }
652
653 if (fired) {
654 /* if at least one timer fires, then don't wait on X events, as there
655 may already be some in the queue from the timer callbacks.
656 */
657 loop->ret_wait.tv_sec = loop->ret_wait.tv_usec = 0;
658 *wait = &loop->ret_wait;
659 } else if (nearest_timeout_wait(loop, &loop->ret_wait))
660 *wait = &loop->ret_wait;
661 else
662 *wait = NULL;
663 }
This page took 0.06876 seconds and 5 git commands to generate.