1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 mainloop.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
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.
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.
17 See the COPYING file for a copy of the GNU General Public License.
25 #include <sys/select.h>
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
;
34 /* this should be more than the number of possible signals on any
36 #define NUM_SIGNALS 99
38 /* all created ObMainLoops. Used by the signal handler to pass along signals */
39 static GSList
*all_loops
;
41 /* signals are global to all loops */
43 guint installed
; /* a ref count */
44 struct sigaction oldact
;
45 } all_signals
[NUM_SIGNALS
];
47 /* a set of all possible signals */
48 static sigset_t all_signals_set
;
50 /* signals which cause a core dump, these can't be used for callbacks */
51 static gint core_signals
[] =
64 #define NUM_CORE_SIGNALS (sizeof(core_signals) / sizeof(core_signals[0]))
66 static void sighandler(gint sig
);
67 static void timer_dispatch(ObMainLoop
*loop
, GTimeVal
**wait
);
68 static void fd_handler_destroy(gpointer data
);
74 gboolean run
; /* do keep running */
75 gboolean running
; /* is still running */
79 gint fd_x
; /* The X fd is a special case! */
81 GHashTable
*fd_handlers
;
88 gboolean signal_fired
;
89 guint signals_fired
[NUM_SIGNALS
];
90 GSList
*signal_handlers
[NUM_SIGNALS
];
93 struct _ObMainLoopTimer
99 GDestroyNotify destroy
;
101 /* The timer needs to be freed */
103 /* The time the last fire should've been at */
105 /* When this timer will next trigger */
108 /* Only allow a timer's function to fire once per run through the list,
109 so that it doesn't get locked in there forever */
113 struct _ObMainLoopSignalHandlerType
118 ObMainLoopSignalHandler func
;
119 GDestroyNotify destroy
;
122 struct _ObMainLoopXHandlerType
126 ObMainLoopXHandler func
;
127 GDestroyNotify destroy
;
130 struct _ObMainLoopFdHandlerType
135 ObMainLoopFdHandler func
;
136 GDestroyNotify destroy
;
139 ObMainLoop
*ob_main_loop_new(Display
*display
)
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
;
150 loop
->fd_handlers
= g_hash_table_new_full(g_int_hash
, g_int_equal
,
151 NULL
, fd_handler_destroy
);
153 g_get_current_time(&loop
->now
);
155 /* only do this if we're the first loop created */
158 struct sigaction action
;
161 /* initialize the all_signals_set */
162 sigfillset(&all_signals_set
);
164 sigemptyset(&sigset
);
165 action
.sa_handler
= sighandler
;
166 action
.sa_mask
= sigset
;
167 action
.sa_flags
= SA_NOCLDSTOP
;
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
++;
183 all_loops
= g_slist_prepend(all_loops
, loop
);
188 void ob_main_loop_destroy(ObMainLoop
*loop
)
194 g_assert(loop
->running
== FALSE
);
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
);
202 g_hash_table_destroy(loop
->fd_handlers
);
204 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
205 ObMainLoopTimer
*t
= it
->data
;
206 if (t
->destroy
) t
->destroy(t
->data
);
209 g_slist_free(loop
->timers
);
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
);
219 all_loops
= g_slist_remove(all_loops
, loop
);
221 /* only do this if we're the last loop destroyed */
223 /* grab all the signals that cause core dumps */
224 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
) {
225 if (all_signals
[core_signals
[i
]].installed
) {
226 sigaction(core_signals
[i
],
227 &all_signals
[core_signals
[i
]].oldact
, NULL
);
228 all_signals
[core_signals
[i
]].installed
--;
237 static void fd_handle_foreach(gpointer key
,
241 ObMainLoopFdHandlerType
*h
= value
;
244 if (FD_ISSET(h
->fd
, set
))
245 h
->func(h
->fd
, h
->data
);
248 void ob_main_loop_run(ObMainLoop
*loop
)
251 struct timeval
*wait
;
256 loop
->running
= TRUE
;
259 if (loop
->signal_fired
) {
263 /* block signals so that we can do this without the data changing
265 sigprocmask(SIG_SETMASK
, &all_signals_set
, &oldset
);
267 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
268 while (loop
->signals_fired
[i
]) {
269 for (it
= loop
->signal_handlers
[i
];
270 it
; it
= g_slist_next(it
)) {
271 ObMainLoopSignalHandlerType
*h
= it
->data
;
274 loop
->signals_fired
[i
]--;
277 loop
->signal_fired
= FALSE
;
279 sigprocmask(SIG_SETMASK
, &oldset
, NULL
);
280 } else if (XPending(loop
->display
)) {
282 XNextEvent(loop
->display
, &e
);
284 for (it
= loop
->x_handlers
; it
; it
= g_slist_next(it
)) {
285 ObMainLoopXHandlerType
*h
= it
->data
;
286 h
->func(&e
, h
->data
);
288 } while (XPending(loop
->display
) && loop
->run
);
290 /* this only runs if there were no x events received */
292 timer_dispatch(loop
, (GTimeVal
**)&wait
);
294 selset
= loop
->fd_set
;
295 /* there is a small race condition here. if a signal occurs
296 between this if() and the select() then we will not process
297 the signal until 'wait' expires. possible solutions include
298 using GStaticMutex, and having the signal handler set 'wait'
300 if (!loop
->signal_fired
)
301 select(loop
->fd_max
+ 1, &selset
, NULL
, NULL
, wait
);
303 /* handle the X events with highest prioirity */
304 if (FD_ISSET(loop
->fd_x
, &selset
))
307 g_hash_table_foreach(loop
->fd_handlers
,
308 fd_handle_foreach
, &selset
);
312 loop
->running
= FALSE
;
315 void ob_main_loop_exit(ObMainLoop
*loop
)
320 /*** XEVENT WATCHERS ***/
322 void ob_main_loop_x_add(ObMainLoop
*loop
,
323 ObMainLoopXHandler handler
,
325 GDestroyNotify notify
)
327 ObMainLoopXHandlerType
*h
;
329 h
= g_new(ObMainLoopXHandlerType
, 1);
334 loop
->x_handlers
= g_slist_prepend(loop
->x_handlers
, h
);
337 void ob_main_loop_x_remove(ObMainLoop
*loop
,
338 ObMainLoopXHandler handler
)
342 for (it
= loop
->x_handlers
; it
; it
= next
) {
343 ObMainLoopXHandlerType
*h
= it
->data
;
344 next
= g_slist_next(it
);
345 if (h
->func
== handler
) {
346 loop
->x_handlers
= g_slist_delete_link(loop
->x_handlers
, it
);
347 if (h
->destroy
) h
->destroy(h
->data
);
353 /*** SIGNAL WATCHERS ***/
355 static void sighandler(gint sig
)
360 g_return_if_fail(sig
< NUM_SIGNALS
);
362 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
)
363 if (sig
== core_signals
[i
]) {
364 /* XXX special case for signals that default to core dump.
365 but throw some helpful output here... */
367 fprintf(stderr
, "How are you gentlemen? All your base are"
368 " belong to us. (Openbox received signal %d)\n", sig
);
370 /* die with a core dump */
374 for (it
= all_loops
; it
; it
= g_slist_next(it
)) {
375 ObMainLoop
*loop
= it
->data
;
376 loop
->signal_fired
= TRUE
;
377 loop
->signals_fired
[sig
]++;
381 void ob_main_loop_signal_add(ObMainLoop
*loop
,
383 ObMainLoopSignalHandler handler
,
385 GDestroyNotify notify
)
387 ObMainLoopSignalHandlerType
*h
;
389 g_return_if_fail(signal
< NUM_SIGNALS
);
391 h
= g_new(ObMainLoopSignalHandlerType
, 1);
397 loop
->signal_handlers
[h
->signal
] =
398 g_slist_prepend(loop
->signal_handlers
[h
->signal
], h
);
400 if (!all_signals
[signal
].installed
) {
401 struct sigaction action
;
404 sigemptyset(&sigset
);
405 action
.sa_handler
= sighandler
;
406 action
.sa_mask
= sigset
;
407 action
.sa_flags
= SA_NOCLDSTOP
;
409 sigaction(signal
, &action
, &all_signals
[signal
].oldact
);
412 all_signals
[signal
].installed
++;
415 void ob_main_loop_signal_remove(ObMainLoop
*loop
,
416 ObMainLoopSignalHandler handler
)
421 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
422 for (it
= loop
->signal_handlers
[i
]; it
; it
= next
) {
423 ObMainLoopSignalHandlerType
*h
= it
->data
;
425 next
= g_slist_next(it
);
427 if (h
->func
== handler
) {
428 g_assert(all_signals
[h
->signal
].installed
> 0);
430 all_signals
[h
->signal
].installed
--;
431 if (!all_signals
[h
->signal
].installed
) {
432 sigaction(h
->signal
, &all_signals
[h
->signal
].oldact
, NULL
);
435 loop
->signal_handlers
[i
] =
436 g_slist_delete_link(loop
->signal_handlers
[i
], it
);
437 if (h
->destroy
) h
->destroy(h
->data
);
446 /*** FILE DESCRIPTOR WATCHERS ***/
448 static void max_fd_func(gpointer key
, gpointer value
, gpointer data
)
450 ObMainLoop
*loop
= data
;
453 loop
->fd_max
= MAX(loop
->fd_max
, *(gint
*)key
);
456 static void calc_max_fd(ObMainLoop
*loop
)
458 loop
->fd_max
= loop
->fd_x
;
460 g_hash_table_foreach(loop
->fd_handlers
, max_fd_func
, loop
);
463 void ob_main_loop_fd_add(ObMainLoop
*loop
,
465 ObMainLoopFdHandler handler
,
467 GDestroyNotify notify
)
469 ObMainLoopFdHandlerType
*h
;
471 h
= g_new(ObMainLoopFdHandlerType
, 1);
478 g_hash_table_replace(loop
->fd_handlers
, &h
->fd
, h
);
479 FD_SET(h
->fd
, &loop
->fd_set
);
483 static void fd_handler_destroy(gpointer data
)
485 ObMainLoopFdHandlerType
*h
= data
;
487 FD_CLR(h
->fd
, &h
->loop
->fd_set
);
493 void ob_main_loop_fd_remove(ObMainLoop
*loop
,
496 g_hash_table_remove(loop
->fd_handlers
, &fd
);
501 #define NEAREST_TIMEOUT(loop) \
502 (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
504 static glong
timecompare(GTimeVal
*a
, GTimeVal
*b
)
507 if ((r
= a
->tv_sec
- b
->tv_sec
)) return r
;
508 return a
->tv_usec
- b
->tv_usec
;
511 static void insert_timer(ObMainLoop
*loop
, ObMainLoopTimer
*ins
)
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
);
521 if (it
== NULL
) /* didnt fit anywhere in the list */
522 loop
->timers
= g_slist_append(loop
->timers
, ins
);
525 void ob_main_loop_timeout_add(ObMainLoop
*loop
,
530 GDestroyNotify notify
)
532 ObMainLoopTimer
*t
= g_new(ObMainLoopTimer
, 1);
534 g_assert(microseconds
> 0); /* if it's 0 it'll cause an infinite loop */
536 t
->delay
= microseconds
;
542 g_get_current_time(&loop
->now
);
543 t
->last
= t
->timeout
= loop
->now
;
544 g_time_val_add(&t
->timeout
, t
->delay
);
546 insert_timer(loop
, t
);
549 void ob_main_loop_timeout_remove(ObMainLoop
*loop
,
554 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
555 ObMainLoopTimer
*t
= it
->data
;
556 if (t
->func
== handler
)
561 void ob_main_loop_timeout_remove_data(ObMainLoop
*loop
, GSourceFunc handler
,
562 gpointer data
, gboolean cancel_dest
)
566 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
567 ObMainLoopTimer
*t
= it
->data
;
568 if (t
->func
== handler
&& t
->equal(t
->data
, data
)) {
576 /* find the time to wait for the nearest timeout */
577 static gboolean
nearest_timeout_wait(ObMainLoop
*loop
, GTimeVal
*tm
)
579 if (loop
->timers
== NULL
)
582 tm
->tv_sec
= NEAREST_TIMEOUT(loop
).tv_sec
- loop
->now
.tv_sec
;
583 tm
->tv_usec
= NEAREST_TIMEOUT(loop
).tv_usec
- loop
->now
.tv_usec
;
585 while (tm
->tv_usec
< 0) {
586 tm
->tv_usec
+= G_USEC_PER_SEC
;
589 tm
->tv_sec
+= tm
->tv_usec
/ G_USEC_PER_SEC
;
590 tm
->tv_usec
%= G_USEC_PER_SEC
;
597 static void timer_dispatch(ObMainLoop
*loop
, GTimeVal
**wait
)
601 gboolean fired
= FALSE
;
603 g_get_current_time(&loop
->now
);
605 for (it
= loop
->timers
; it
; it
= next
) {
606 ObMainLoopTimer
*curr
;
608 next
= g_slist_next(it
);
612 /* since timer_stop doesn't actually free the timer, we have to do our
613 real freeing in here.
617 loop
->timers
= g_slist_delete_link(loop
->timers
, it
);
619 curr
->destroy(curr
->data
);
624 /* the queue is sorted, so if this timer shouldn't fire, none are
626 if (timecompare(&NEAREST_TIMEOUT(loop
), &loop
->now
) > 0)
629 /* we set the last fired time to delay msec after the previous firing,
630 then re-insert. timers maintain their order and may trigger more
631 than once if they've waited more than one delay's worth of time.
633 loop
->timers
= g_slist_delete_link(loop
->timers
, it
);
634 g_time_val_add(&curr
->last
, curr
->delay
);
635 if (curr
->func(curr
->data
)) {
636 g_time_val_add(&curr
->timeout
, curr
->delay
);
637 insert_timer(loop
, curr
);
640 curr
->destroy(curr
->data
);
644 /* the timer queue has been shuffled, start from the beginning
645 (which is the next one to fire) */
652 /* if at least one timer fires, then don't wait on X events, as there
653 may already be some in the queue from the timer callbacks.
655 loop
->ret_wait
.tv_sec
= loop
->ret_wait
.tv_usec
= 0;
656 *wait
= &loop
->ret_wait
;
657 } else if (nearest_timeout_wait(loop
, &loop
->ret_wait
))
658 *wait
= &loop
->ret_wait
;