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