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