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