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