]> Dogcows Code - chaz/openbox/blob - openbox/mainloop.c
add a means to tell the timer destroy function not to fire when for example freeing...
[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) 2004 Mikael Magnusson
5 Copyright (c) 2003 Ben 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 "mainloop.h"
21 #include "action.h"
22 #include "client.h"
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <sys/select.h>
27 #include <signal.h>
28
29 typedef struct _ObMainLoopTimer ObMainLoopTimer;
30 typedef struct _ObMainLoopSignal ObMainLoopSignal;
31 typedef struct _ObMainLoopSignalHandlerType ObMainLoopSignalHandlerType;
32 typedef struct _ObMainLoopXHandlerType ObMainLoopXHandlerType;
33 typedef struct _ObMainLoopFdHandlerType ObMainLoopFdHandlerType;
34
35 /* this should be more than the number of possible signals on any
36 architecture... */
37 #define NUM_SIGNALS 99
38
39 /* all created ObMainLoops. Used by the signal handler to pass along signals */
40 static GSList *all_loops;
41
42 /* signals are global to all loops */
43 struct {
44 guint installed; /* a ref count */
45 struct sigaction oldact;
46 } all_signals[NUM_SIGNALS];
47
48 /* a set of all possible signals */
49 sigset_t all_signals_set;
50
51 /* signals which cause a core dump, these can't be used for callbacks */
52 static gint core_signals[] =
53 {
54 SIGABRT,
55 SIGSEGV,
56 SIGFPE,
57 SIGILL,
58 SIGQUIT,
59 SIGTRAP,
60 SIGSYS,
61 SIGBUS,
62 SIGXCPU,
63 SIGXFSZ
64 };
65 #define NUM_CORE_SIGNALS (sizeof(core_signals) / sizeof(core_signals[0]))
66
67 static void sighandler(gint sig);
68 static void timer_dispatch(ObMainLoop *loop, GTimeVal **wait);
69 static void fd_handler_destroy(gpointer data);
70
71 struct _ObMainLoop
72 {
73 Display *display;
74
75 gboolean run; /* do keep running */
76 gboolean running; /* is still running */
77
78 GSList *x_handlers;
79
80 gint fd_x; /* The X fd is a special case! */
81 gint fd_max;
82 GHashTable *fd_handlers;
83 fd_set fd_set;
84
85 GSList *timers;
86 GTimeVal now;
87 GTimeVal ret_wait;
88
89 gboolean signal_fired;
90 guint signals_fired[NUM_SIGNALS];
91 GSList *signal_handlers[NUM_SIGNALS];
92
93 GSList *action_queue;
94 };
95
96 struct _ObMainLoopTimer
97 {
98 gulong delay;
99 GSourceFunc func;
100 gpointer data;
101 GDestroyNotify destroy;
102
103 /* The timer needs to be freed */
104 gboolean del_me;
105 /* The time the last fire should've been at */
106 GTimeVal last;
107 /* When this timer will next trigger */
108 GTimeVal timeout;
109 };
110
111 struct _ObMainLoopSignalHandlerType
112 {
113 ObMainLoop *loop;
114 gint signal;
115 gpointer data;
116 ObMainLoopSignalHandler func;
117 GDestroyNotify destroy;
118 };
119
120 struct _ObMainLoopXHandlerType
121 {
122 ObMainLoop *loop;
123 gpointer data;
124 ObMainLoopXHandler 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 if (loop->action_queue) {
317 /* only fire off one action at a time, then go back for more
318 X events, since the action might cause some X events (like
319 FocusIn :) */
320
321 do {
322 act = loop->action_queue->data;
323 if (act->data.any.client_action == OB_CLIENT_ACTION_ALWAYS &&
324 !act->data.any.c)
325 {
326 loop->action_queue =
327 g_slist_delete_link(loop->action_queue,
328 loop->action_queue);
329 action_unref(act);
330 act = NULL;
331 }
332 } while (!act && loop->action_queue);
333
334 if (act) {
335 act->func(&act->data);
336 loop->action_queue =
337 g_slist_delete_link(loop->action_queue,
338 loop->action_queue);
339 action_unref(act);
340 }
341 } else {
342 /* this only runs if there were no x events received */
343
344 timer_dispatch(loop, (GTimeVal**)&wait);
345
346 selset = loop->fd_set;
347 /* there is a small race condition here. if a signal occurs
348 between this if() and the select() then we will not process
349 the signal until 'wait' expires. possible solutions include
350 using GStaticMutex, and having the signal handler set 'wait'
351 to 0 */
352 if (!loop->signal_fired)
353 select(loop->fd_max + 1, &selset, NULL, NULL, wait);
354
355 /* handle the X events with highest prioirity */
356 if (FD_ISSET(loop->fd_x, &selset))
357 continue;
358
359 g_hash_table_foreach(loop->fd_handlers,
360 fd_handle_foreach, &selset);
361 }
362 }
363
364 client_remove_destructor(ob_main_loop_client_destroy);
365
366 loop->running = FALSE;
367 }
368
369 void ob_main_loop_exit(ObMainLoop *loop)
370 {
371 loop->run = FALSE;
372 }
373
374 /*** XEVENT WATCHERS ***/
375
376 void ob_main_loop_x_add(ObMainLoop *loop,
377 ObMainLoopXHandler handler,
378 gpointer data,
379 GDestroyNotify notify)
380 {
381 ObMainLoopXHandlerType *h;
382
383 h = g_new(ObMainLoopXHandlerType, 1);
384 h->loop = loop;
385 h->func = handler;
386 h->data = data;
387 h->destroy = notify;
388 loop->x_handlers = g_slist_prepend(loop->x_handlers, h);
389 }
390
391 void ob_main_loop_x_remove(ObMainLoop *loop,
392 ObMainLoopXHandler handler)
393 {
394 GSList *it, *next;
395
396 for (it = loop->x_handlers; it; it = next) {
397 ObMainLoopXHandlerType *h = it->data;
398 next = g_slist_next(it);
399 if (h->func == handler) {
400 loop->x_handlers = g_slist_delete_link(loop->x_handlers, it);
401 if (h->destroy) h->destroy(h->data);
402 g_free(h);
403 }
404 }
405 }
406
407 /*** SIGNAL WATCHERS ***/
408
409 static void sighandler(gint sig)
410 {
411 GSList *it;
412 guint i;
413
414 g_return_if_fail(sig < NUM_SIGNALS);
415
416 for (i = 0; i < NUM_CORE_SIGNALS; ++i)
417 if (sig == core_signals[i]) {
418 /* XXX special case for signals that default to core dump.
419 but throw some helpful output here... */
420
421 fprintf(stderr, "Fuck yah. Core dump. (Signal=%d)\n", sig);
422
423 /* die with a core dump */
424 abort();
425 }
426
427 for (it = all_loops; it; it = g_slist_next(it)) {
428 ObMainLoop *loop = it->data;
429 loop->signal_fired = TRUE;
430 loop->signals_fired[sig]++;
431 }
432 }
433
434 void ob_main_loop_signal_add(ObMainLoop *loop,
435 gint signal,
436 ObMainLoopSignalHandler handler,
437 gpointer data,
438 GDestroyNotify notify)
439 {
440 ObMainLoopSignalHandlerType *h;
441
442 g_return_if_fail(signal < NUM_SIGNALS);
443
444 h = g_new(ObMainLoopSignalHandlerType, 1);
445 h->loop = loop;
446 h->signal = signal;
447 h->func = handler;
448 h->data = data;
449 h->destroy = notify;
450 loop->signal_handlers[h->signal] =
451 g_slist_prepend(loop->signal_handlers[h->signal], h);
452
453 if (!all_signals[signal].installed) {
454 struct sigaction action;
455 sigset_t sigset;
456
457 sigemptyset(&sigset);
458 action.sa_handler = sighandler;
459 action.sa_mask = sigset;
460 action.sa_flags = SA_NOCLDSTOP;
461
462 sigaction(signal, &action, &all_signals[signal].oldact);
463 }
464
465 all_signals[signal].installed++;
466 }
467
468 void ob_main_loop_signal_remove(ObMainLoop *loop,
469 ObMainLoopSignalHandler handler)
470 {
471 guint i;
472 GSList *it, *next;
473
474 for (i = 0; i < NUM_SIGNALS; ++i) {
475 for (it = loop->signal_handlers[i]; it; it = next) {
476 ObMainLoopSignalHandlerType *h = it->data;
477
478 next = g_slist_next(it);
479
480 if (h->func == handler) {
481 g_assert(all_signals[h->signal].installed > 0);
482
483 all_signals[h->signal].installed--;
484 if (!all_signals[h->signal].installed) {
485 sigaction(h->signal, &all_signals[h->signal].oldact, NULL);
486 }
487
488 loop->signal_handlers[i] =
489 g_slist_delete_link(loop->signal_handlers[i], it);
490 if (h->destroy) h->destroy(h->data);
491
492 g_free(h);
493 }
494 }
495 }
496
497 }
498
499 /*** FILE DESCRIPTOR WATCHERS ***/
500
501 static void max_fd_func(gpointer key, gpointer value, gpointer data)
502 {
503 ObMainLoop *loop = data;
504
505 /* key is the fd */
506 loop->fd_max = MAX(loop->fd_max, *(gint*)key);
507 }
508
509 static void calc_max_fd(ObMainLoop *loop)
510 {
511 loop->fd_max = loop->fd_x;
512
513 g_hash_table_foreach(loop->fd_handlers, max_fd_func, loop);
514 }
515
516 void ob_main_loop_fd_add(ObMainLoop *loop,
517 gint fd,
518 ObMainLoopFdHandler handler,
519 gpointer data,
520 GDestroyNotify notify)
521 {
522 ObMainLoopFdHandlerType *h;
523
524 h = g_new(ObMainLoopFdHandlerType, 1);
525 h->loop = loop;
526 h->fd = fd;
527 h->func = handler;
528 h->data = data;
529 h->destroy = notify;
530
531 g_hash_table_replace(loop->fd_handlers, &h->fd, h);
532 FD_SET(h->fd, &loop->fd_set);
533 calc_max_fd(loop);
534 }
535
536 static void fd_handler_destroy(gpointer data)
537 {
538 ObMainLoopFdHandlerType *h = data;
539
540 FD_CLR(h->fd, &h->loop->fd_set);
541
542 if (h->destroy)
543 h->destroy(h->data);
544 }
545
546 void ob_main_loop_fd_remove(ObMainLoop *loop,
547 gint fd)
548 {
549 g_hash_table_remove(loop->fd_handlers, &fd);
550 }
551
552 /*** TIMEOUTS ***/
553
554 #define NEAREST_TIMEOUT(loop) \
555 (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
556
557 static glong timecompare(GTimeVal *a, GTimeVal *b)
558 {
559 glong r;
560
561 if ((r = b->tv_sec - a->tv_sec)) return r;
562 return b->tv_usec - a->tv_usec;
563
564 }
565
566 static void insert_timer(ObMainLoop *loop, ObMainLoopTimer *ins)
567 {
568 GSList *it;
569 for (it = loop->timers; it; it = g_slist_next(it)) {
570 ObMainLoopTimer *t = it->data;
571 if (timecompare(&ins->timeout, &t->timeout) >= 0) {
572 loop->timers = g_slist_insert_before(loop->timers, it, ins);
573 break;
574 }
575 }
576 if (it == NULL) /* didnt fit anywhere in the list */
577 loop->timers = g_slist_append(loop->timers, ins);
578 }
579
580 void ob_main_loop_timeout_add(ObMainLoop *loop,
581 gulong microseconds,
582 GSourceFunc handler,
583 gpointer data,
584 GDestroyNotify notify)
585 {
586 ObMainLoopTimer *t = g_new(ObMainLoopTimer, 1);
587 t->delay = microseconds;
588 t->func = handler;
589 t->data = data;
590 t->destroy = notify;
591 t->del_me = FALSE;
592 g_get_current_time(&loop->now);
593 t->last = t->timeout = loop->now;
594 g_time_val_add(&t->timeout, t->delay);
595
596 insert_timer(loop, t);
597 }
598
599 void ob_main_loop_timeout_remove(ObMainLoop *loop,
600 GSourceFunc handler)
601 {
602 GSList *it;
603
604 for (it = loop->timers; it; it = g_slist_next(it)) {
605 ObMainLoopTimer *t = it->data;
606 if (t->func == handler)
607 t->del_me = TRUE;
608 }
609 }
610
611 void ob_main_loop_timeout_remove_data(ObMainLoop *loop, GSourceFunc handler,
612 gpointer data, gboolean cancel_dest)
613 {
614 GSList *it;
615
616 for (it = loop->timers; it; it = g_slist_next(it)) {
617 ObMainLoopTimer *t = it->data;
618 if (t->func == handler && t->data == data) {
619 t->del_me = TRUE;
620 if (cancel_dest)
621 t->destroy = NULL;
622 }
623 }
624 }
625
626 /* find the time to wait for the nearest timeout */
627 static gboolean nearest_timeout_wait(ObMainLoop *loop, GTimeVal *tm)
628 {
629 if (loop->timers == NULL)
630 return FALSE;
631
632 tm->tv_sec = NEAREST_TIMEOUT(loop).tv_sec - loop->now.tv_sec;
633 tm->tv_usec = NEAREST_TIMEOUT(loop).tv_usec - loop->now.tv_usec;
634
635 while (tm->tv_usec < 0) {
636 tm->tv_usec += G_USEC_PER_SEC;
637 tm->tv_sec--;
638 }
639 tm->tv_sec += tm->tv_usec / G_USEC_PER_SEC;
640 tm->tv_usec %= G_USEC_PER_SEC;
641 if (tm->tv_sec < 0)
642 tm->tv_sec = 0;
643
644 return TRUE;
645 }
646
647 static void timer_dispatch(ObMainLoop *loop, GTimeVal **wait)
648 {
649 GSList *it, *next;
650
651 gboolean fired = FALSE;
652
653 g_get_current_time(&loop->now);
654
655 for (it = loop->timers; it; it = next) {
656 ObMainLoopTimer *curr;
657
658 next = g_slist_next(it);
659
660 curr = it->data;
661
662 /* since timer_stop doesn't actually free the timer, we have to do our
663 real freeing in here.
664 */
665 if (curr->del_me) {
666 /* delete the top */
667 loop->timers = g_slist_delete_link(loop->timers, it);
668 if (curr->destroy)
669 curr->destroy(curr->data);
670 g_free(curr);
671 continue;
672 }
673
674 /* the queue is sorted, so if this timer shouldn't fire, none are
675 ready */
676 if (timecompare(&NEAREST_TIMEOUT(loop), &loop->now) < 0)
677 break;
678
679 /* we set the last fired time to delay msec after the previous firing,
680 then re-insert. timers maintain their order and may trigger more
681 than once if they've waited more than one delay's worth of time.
682 */
683 loop->timers = g_slist_delete_link(loop->timers, it);
684 g_time_val_add(&curr->last, curr->delay);
685 if (curr->func(curr->data)) {
686 g_time_val_add(&curr->timeout, curr->delay);
687 insert_timer(loop, curr);
688 } else {
689 if (curr->destroy)
690 curr->destroy(curr->data);
691 g_free(curr);
692 }
693
694 fired = TRUE;
695 }
696
697 if (fired) {
698 /* if at least one timer fires, then don't wait on X events, as there
699 may already be some in the queue from the timer callbacks.
700 */
701 loop->ret_wait.tv_sec = loop->ret_wait.tv_usec = 0;
702 *wait = &loop->ret_wait;
703 } else if (nearest_timeout_wait(loop, &loop->ret_wait))
704 *wait = &loop->ret_wait;
705 else
706 *wait = NULL;
707 }
This page took 0.066536 seconds and 4 git commands to generate.