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