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