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