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