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