]> Dogcows Code - chaz/openbox/blob - openbox/mainloop.c
make move and resize resist care about the dock
[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 guint i;
220
221 /* grab all the signals that cause core dumps */
222 for (i = 0; i < NUM_CORE_SIGNALS; ++i) {
223 if (all_signals[core_signals[i]].installed) {
224 sigaction(core_signals[i],
225 &all_signals[core_signals[i]].oldact, NULL);
226 all_signals[core_signals[i]].installed--;
227 }
228 }
229 }
230
231 g_free(loop);
232 }
233 }
234
235 static void fd_handle_foreach(gpointer key,
236 gpointer value,
237 gpointer data)
238 {
239 ObMainLoopFdHandlerType *h = value;
240 fd_set *set = data;
241
242 if (FD_ISSET(h->fd, set))
243 h->func(h->fd, h->data);
244 }
245
246 void ob_main_loop_run(ObMainLoop *loop)
247 {
248 XEvent e;
249 struct timeval *wait;
250 fd_set selset;
251 GSList *it;
252
253 loop->run = TRUE;
254 loop->running = TRUE;
255
256 while (loop->run) {
257 if (loop->signal_fired) {
258 guint i;
259 sigset_t oldset;
260
261 /* block signals so that we can do this without the data changing
262 on us */
263 sigprocmask(SIG_SETMASK, &all_signals_set, &oldset);
264
265 for (i = 0; i < NUM_SIGNALS; ++i) {
266 while (loop->signals_fired[i]) {
267 for (it = loop->signal_handlers[i];
268 it; it = g_slist_next(it)) {
269 ObMainLoopSignalHandlerType *h = it->data;
270 h->func(i, h->data);
271 }
272 loop->signals_fired[i]--;
273 }
274 }
275 loop->signal_fired = FALSE;
276
277 sigprocmask(SIG_SETMASK, &oldset, NULL);
278 } else if (XPending(loop->display)) {
279 do {
280 XNextEvent(loop->display, &e);
281
282 for (it = loop->x_handlers; it; it = g_slist_next(it)) {
283 ObMainLoopXHandlerType *h = it->data;
284 h->func(&e, h->data);
285 }
286 } while (XPending(loop->display) && loop->run);
287 } else {
288 /* this only runs if there were no x events received */
289
290 timer_dispatch(loop, (GTimeVal**)&wait);
291
292 selset = loop->fd_set;
293 /* there is a small race condition here. if a signal occurs
294 between this if() and the select() then we will not process
295 the signal until 'wait' expires. possible solutions include
296 using GStaticMutex, and having the signal handler set 'wait'
297 to 0 */
298 if (!loop->signal_fired)
299 select(loop->fd_max + 1, &selset, NULL, NULL, wait);
300
301 /* handle the X events with highest prioirity */
302 if (FD_ISSET(loop->fd_x, &selset))
303 continue;
304
305 g_hash_table_foreach(loop->fd_handlers,
306 fd_handle_foreach, &selset);
307 }
308 }
309
310 loop->running = FALSE;
311 }
312
313 void ob_main_loop_exit(ObMainLoop *loop)
314 {
315 loop->run = FALSE;
316 }
317
318 /*** XEVENT WATCHERS ***/
319
320 void ob_main_loop_x_add(ObMainLoop *loop,
321 ObMainLoopXHandler handler,
322 gpointer data,
323 GDestroyNotify notify)
324 {
325 ObMainLoopXHandlerType *h;
326
327 h = g_new(ObMainLoopXHandlerType, 1);
328 h->loop = loop;
329 h->func = handler;
330 h->data = data;
331 h->destroy = notify;
332 loop->x_handlers = g_slist_prepend(loop->x_handlers, h);
333 }
334
335 void ob_main_loop_x_remove(ObMainLoop *loop,
336 ObMainLoopXHandler handler)
337 {
338 GSList *it, *next;
339
340 for (it = loop->x_handlers; it; it = next) {
341 ObMainLoopXHandlerType *h = it->data;
342 next = g_slist_next(it);
343 if (h->func == handler) {
344 loop->x_handlers = g_slist_delete_link(loop->x_handlers, it);
345 if (h->destroy) h->destroy(h->data);
346 g_free(h);
347 }
348 }
349 }
350
351 /*** SIGNAL WATCHERS ***/
352
353 static void sighandler(gint sig)
354 {
355 GSList *it;
356 guint i;
357
358 g_return_if_fail(sig < NUM_SIGNALS);
359
360 for (i = 0; i < NUM_CORE_SIGNALS; ++i)
361 if (sig == core_signals[i]) {
362 /* XXX special case for signals that default to core dump.
363 but throw some helpful output here... */
364
365 fprintf(stderr, "How are you gentlemen? All your base are"
366 " belong to us. (Openbox received signal %d)\n", sig);
367
368 /* die with a core dump */
369 abort();
370 }
371
372 for (it = all_loops; it; it = g_slist_next(it)) {
373 ObMainLoop *loop = it->data;
374 loop->signal_fired = TRUE;
375 loop->signals_fired[sig]++;
376 }
377 }
378
379 void ob_main_loop_signal_add(ObMainLoop *loop,
380 gint signal,
381 ObMainLoopSignalHandler handler,
382 gpointer data,
383 GDestroyNotify notify)
384 {
385 ObMainLoopSignalHandlerType *h;
386
387 g_return_if_fail(signal < NUM_SIGNALS);
388
389 h = g_new(ObMainLoopSignalHandlerType, 1);
390 h->loop = loop;
391 h->signal = signal;
392 h->func = handler;
393 h->data = data;
394 h->destroy = notify;
395 loop->signal_handlers[h->signal] =
396 g_slist_prepend(loop->signal_handlers[h->signal], h);
397
398 if (!all_signals[signal].installed) {
399 struct sigaction action;
400 sigset_t sigset;
401
402 sigemptyset(&sigset);
403 action.sa_handler = sighandler;
404 action.sa_mask = sigset;
405 action.sa_flags = SA_NOCLDSTOP;
406
407 sigaction(signal, &action, &all_signals[signal].oldact);
408 }
409
410 all_signals[signal].installed++;
411 }
412
413 void ob_main_loop_signal_remove(ObMainLoop *loop,
414 ObMainLoopSignalHandler handler)
415 {
416 guint i;
417 GSList *it, *next;
418
419 for (i = 0; i < NUM_SIGNALS; ++i) {
420 for (it = loop->signal_handlers[i]; it; it = next) {
421 ObMainLoopSignalHandlerType *h = it->data;
422
423 next = g_slist_next(it);
424
425 if (h->func == handler) {
426 g_assert(all_signals[h->signal].installed > 0);
427
428 all_signals[h->signal].installed--;
429 if (!all_signals[h->signal].installed) {
430 sigaction(h->signal, &all_signals[h->signal].oldact, NULL);
431 }
432
433 loop->signal_handlers[i] =
434 g_slist_delete_link(loop->signal_handlers[i], it);
435 if (h->destroy) h->destroy(h->data);
436
437 g_free(h);
438 }
439 }
440 }
441
442 }
443
444 /*** FILE DESCRIPTOR WATCHERS ***/
445
446 static void max_fd_func(gpointer key, gpointer value, gpointer data)
447 {
448 ObMainLoop *loop = data;
449
450 /* key is the fd */
451 loop->fd_max = MAX(loop->fd_max, *(gint*)key);
452 }
453
454 static void calc_max_fd(ObMainLoop *loop)
455 {
456 loop->fd_max = loop->fd_x;
457
458 g_hash_table_foreach(loop->fd_handlers, max_fd_func, loop);
459 }
460
461 void ob_main_loop_fd_add(ObMainLoop *loop,
462 gint fd,
463 ObMainLoopFdHandler handler,
464 gpointer data,
465 GDestroyNotify notify)
466 {
467 ObMainLoopFdHandlerType *h;
468
469 h = g_new(ObMainLoopFdHandlerType, 1);
470 h->loop = loop;
471 h->fd = fd;
472 h->func = handler;
473 h->data = data;
474 h->destroy = notify;
475
476 g_hash_table_replace(loop->fd_handlers, &h->fd, h);
477 FD_SET(h->fd, &loop->fd_set);
478 calc_max_fd(loop);
479 }
480
481 static void fd_handler_destroy(gpointer data)
482 {
483 ObMainLoopFdHandlerType *h = data;
484
485 FD_CLR(h->fd, &h->loop->fd_set);
486
487 if (h->destroy)
488 h->destroy(h->data);
489 }
490
491 void ob_main_loop_fd_remove(ObMainLoop *loop,
492 gint fd)
493 {
494 g_hash_table_remove(loop->fd_handlers, &fd);
495 }
496
497 /*** TIMEOUTS ***/
498
499 #define NEAREST_TIMEOUT(loop) \
500 (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
501
502 static glong timecompare(GTimeVal *a, GTimeVal *b)
503 {
504 glong r;
505
506 if ((r = b->tv_sec - a->tv_sec)) return r;
507 return b->tv_usec - a->tv_usec;
508
509 }
510
511 static void insert_timer(ObMainLoop *loop, ObMainLoopTimer *ins)
512 {
513 GSList *it;
514 for (it = loop->timers; it; it = g_slist_next(it)) {
515 ObMainLoopTimer *t = it->data;
516 if (timecompare(&ins->timeout, &t->timeout) >= 0) {
517 loop->timers = g_slist_insert_before(loop->timers, it, ins);
518 break;
519 }
520 }
521 if (it == NULL) /* didnt fit anywhere in the list */
522 loop->timers = g_slist_append(loop->timers, ins);
523 }
524
525 void ob_main_loop_timeout_add(ObMainLoop *loop,
526 gulong microseconds,
527 GSourceFunc handler,
528 gpointer data,
529 GEqualFunc cmp,
530 GDestroyNotify notify)
531 {
532 ObMainLoopTimer *t = g_new(ObMainLoopTimer, 1);
533 t->delay = microseconds;
534 t->func = handler;
535 t->data = data;
536 t->equal = cmp;
537 t->destroy = notify;
538 t->del_me = FALSE;
539 g_get_current_time(&loop->now);
540 t->last = t->timeout = loop->now;
541 g_time_val_add(&t->timeout, t->delay);
542
543 insert_timer(loop, t);
544 }
545
546 void ob_main_loop_timeout_remove(ObMainLoop *loop,
547 GSourceFunc handler)
548 {
549 GSList *it;
550
551 for (it = loop->timers; it; it = g_slist_next(it)) {
552 ObMainLoopTimer *t = it->data;
553 if (t->func == handler)
554 t->del_me = TRUE;
555 }
556 }
557
558 void ob_main_loop_timeout_remove_data(ObMainLoop *loop, GSourceFunc handler,
559 gpointer data, gboolean cancel_dest)
560 {
561 GSList *it;
562
563 for (it = loop->timers; it; it = g_slist_next(it)) {
564 ObMainLoopTimer *t = it->data;
565 if (t->func == handler && t->equal(t->data, data)) {
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.062532 seconds and 4 git commands to generate.