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