]> Dogcows Code - chaz/openbox/blob - openbox/mainloop.c
this includes a number of things since my magnificent return....
[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 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, "How are you gentlemen? All your base are"
422 " belong to us. (Signal=%d)\n", sig);
423
424 /* die with a core dump */
425 abort();
426 }
427
428 for (it = all_loops; it; it = g_slist_next(it)) {
429 ObMainLoop *loop = it->data;
430 loop->signal_fired = TRUE;
431 loop->signals_fired[sig]++;
432 }
433 }
434
435 void ob_main_loop_signal_add(ObMainLoop *loop,
436 gint signal,
437 ObMainLoopSignalHandler handler,
438 gpointer data,
439 GDestroyNotify notify)
440 {
441 ObMainLoopSignalHandlerType *h;
442
443 g_return_if_fail(signal < NUM_SIGNALS);
444
445 h = g_new(ObMainLoopSignalHandlerType, 1);
446 h->loop = loop;
447 h->signal = signal;
448 h->func = handler;
449 h->data = data;
450 h->destroy = notify;
451 loop->signal_handlers[h->signal] =
452 g_slist_prepend(loop->signal_handlers[h->signal], h);
453
454 if (!all_signals[signal].installed) {
455 struct sigaction action;
456 sigset_t sigset;
457
458 sigemptyset(&sigset);
459 action.sa_handler = sighandler;
460 action.sa_mask = sigset;
461 action.sa_flags = SA_NOCLDSTOP;
462
463 sigaction(signal, &action, &all_signals[signal].oldact);
464 }
465
466 all_signals[signal].installed++;
467 }
468
469 void ob_main_loop_signal_remove(ObMainLoop *loop,
470 ObMainLoopSignalHandler handler)
471 {
472 guint i;
473 GSList *it, *next;
474
475 for (i = 0; i < NUM_SIGNALS; ++i) {
476 for (it = loop->signal_handlers[i]; it; it = next) {
477 ObMainLoopSignalHandlerType *h = it->data;
478
479 next = g_slist_next(it);
480
481 if (h->func == handler) {
482 g_assert(all_signals[h->signal].installed > 0);
483
484 all_signals[h->signal].installed--;
485 if (!all_signals[h->signal].installed) {
486 sigaction(h->signal, &all_signals[h->signal].oldact, NULL);
487 }
488
489 loop->signal_handlers[i] =
490 g_slist_delete_link(loop->signal_handlers[i], it);
491 if (h->destroy) h->destroy(h->data);
492
493 g_free(h);
494 }
495 }
496 }
497
498 }
499
500 /*** FILE DESCRIPTOR WATCHERS ***/
501
502 static void max_fd_func(gpointer key, gpointer value, gpointer data)
503 {
504 ObMainLoop *loop = data;
505
506 /* key is the fd */
507 loop->fd_max = MAX(loop->fd_max, *(gint*)key);
508 }
509
510 static void calc_max_fd(ObMainLoop *loop)
511 {
512 loop->fd_max = loop->fd_x;
513
514 g_hash_table_foreach(loop->fd_handlers, max_fd_func, loop);
515 }
516
517 void ob_main_loop_fd_add(ObMainLoop *loop,
518 gint fd,
519 ObMainLoopFdHandler handler,
520 gpointer data,
521 GDestroyNotify notify)
522 {
523 ObMainLoopFdHandlerType *h;
524
525 h = g_new(ObMainLoopFdHandlerType, 1);
526 h->loop = loop;
527 h->fd = fd;
528 h->func = handler;
529 h->data = data;
530 h->destroy = notify;
531
532 g_hash_table_replace(loop->fd_handlers, &h->fd, h);
533 FD_SET(h->fd, &loop->fd_set);
534 calc_max_fd(loop);
535 }
536
537 static void fd_handler_destroy(gpointer data)
538 {
539 ObMainLoopFdHandlerType *h = data;
540
541 FD_CLR(h->fd, &h->loop->fd_set);
542
543 if (h->destroy)
544 h->destroy(h->data);
545 }
546
547 void ob_main_loop_fd_remove(ObMainLoop *loop,
548 gint fd)
549 {
550 g_hash_table_remove(loop->fd_handlers, &fd);
551 }
552
553 /*** TIMEOUTS ***/
554
555 #define NEAREST_TIMEOUT(loop) \
556 (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
557
558 static glong timecompare(GTimeVal *a, GTimeVal *b)
559 {
560 glong r;
561
562 if ((r = b->tv_sec - a->tv_sec)) return r;
563 return b->tv_usec - a->tv_usec;
564
565 }
566
567 static void insert_timer(ObMainLoop *loop, ObMainLoopTimer *ins)
568 {
569 GSList *it;
570 for (it = loop->timers; it; it = g_slist_next(it)) {
571 ObMainLoopTimer *t = it->data;
572 if (timecompare(&ins->timeout, &t->timeout) >= 0) {
573 loop->timers = g_slist_insert_before(loop->timers, it, ins);
574 break;
575 }
576 }
577 if (it == NULL) /* didnt fit anywhere in the list */
578 loop->timers = g_slist_append(loop->timers, ins);
579 }
580
581 void ob_main_loop_timeout_add(ObMainLoop *loop,
582 gulong microseconds,
583 GSourceFunc handler,
584 gpointer data,
585 GDestroyNotify notify)
586 {
587 ObMainLoopTimer *t = g_new(ObMainLoopTimer, 1);
588 t->delay = microseconds;
589 t->func = handler;
590 t->data = data;
591 t->destroy = notify;
592 t->del_me = FALSE;
593 g_get_current_time(&loop->now);
594 t->last = t->timeout = loop->now;
595 g_time_val_add(&t->timeout, t->delay);
596
597 insert_timer(loop, t);
598 }
599
600 void ob_main_loop_timeout_remove(ObMainLoop *loop,
601 GSourceFunc handler)
602 {
603 GSList *it;
604
605 for (it = loop->timers; it; it = g_slist_next(it)) {
606 ObMainLoopTimer *t = it->data;
607 if (t->func == handler)
608 t->del_me = TRUE;
609 }
610 }
611
612 void ob_main_loop_timeout_remove_data(ObMainLoop *loop, GSourceFunc handler,
613 gpointer data, gboolean cancel_dest)
614 {
615 GSList *it;
616
617 for (it = loop->timers; it; it = g_slist_next(it)) {
618 ObMainLoopTimer *t = it->data;
619 if (t->func == handler && t->data == data) {
620 t->del_me = TRUE;
621 if (cancel_dest)
622 t->destroy = NULL;
623 }
624 }
625 }
626
627 /* find the time to wait for the nearest timeout */
628 static gboolean nearest_timeout_wait(ObMainLoop *loop, GTimeVal *tm)
629 {
630 if (loop->timers == NULL)
631 return FALSE;
632
633 tm->tv_sec = NEAREST_TIMEOUT(loop).tv_sec - loop->now.tv_sec;
634 tm->tv_usec = NEAREST_TIMEOUT(loop).tv_usec - loop->now.tv_usec;
635
636 while (tm->tv_usec < 0) {
637 tm->tv_usec += G_USEC_PER_SEC;
638 tm->tv_sec--;
639 }
640 tm->tv_sec += tm->tv_usec / G_USEC_PER_SEC;
641 tm->tv_usec %= G_USEC_PER_SEC;
642 if (tm->tv_sec < 0)
643 tm->tv_sec = 0;
644
645 return TRUE;
646 }
647
648 static void timer_dispatch(ObMainLoop *loop, GTimeVal **wait)
649 {
650 GSList *it, *next;
651
652 gboolean fired = FALSE;
653
654 g_get_current_time(&loop->now);
655
656 for (it = loop->timers; it; it = next) {
657 ObMainLoopTimer *curr;
658
659 next = g_slist_next(it);
660
661 curr = it->data;
662
663 /* since timer_stop doesn't actually free the timer, we have to do our
664 real freeing in here.
665 */
666 if (curr->del_me) {
667 /* delete the top */
668 loop->timers = g_slist_delete_link(loop->timers, it);
669 if (curr->destroy)
670 curr->destroy(curr->data);
671 g_free(curr);
672 continue;
673 }
674
675 /* the queue is sorted, so if this timer shouldn't fire, none are
676 ready */
677 if (timecompare(&NEAREST_TIMEOUT(loop), &loop->now) < 0)
678 break;
679
680 /* we set the last fired time to delay msec after the previous firing,
681 then re-insert. timers maintain their order and may trigger more
682 than once if they've waited more than one delay's worth of time.
683 */
684 loop->timers = g_slist_delete_link(loop->timers, it);
685 g_time_val_add(&curr->last, curr->delay);
686 if (curr->func(curr->data)) {
687 g_time_val_add(&curr->timeout, curr->delay);
688 insert_timer(loop, curr);
689 } else {
690 if (curr->destroy)
691 curr->destroy(curr->data);
692 g_free(curr);
693 }
694
695 fired = TRUE;
696 }
697
698 if (fired) {
699 /* if at least one timer fires, then don't wait on X events, as there
700 may already be some in the queue from the timer callbacks.
701 */
702 loop->ret_wait.tv_sec = loop->ret_wait.tv_usec = 0;
703 *wait = &loop->ret_wait;
704 } else if (nearest_timeout_wait(loop, &loop->ret_wait))
705 *wait = &loop->ret_wait;
706 else
707 *wait = NULL;
708 }
This page took 0.061035 seconds and 4 git commands to generate.