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 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 */
109 struct _ObMainLoopSignalHandlerType
114 ObMainLoopSignalHandler func
;
115 GDestroyNotify destroy
;
118 struct _ObMainLoopXHandlerType
122 ObMainLoopXHandler func
;
123 GDestroyNotify destroy
;
126 struct _ObMainLoopFdHandlerType
131 ObMainLoopFdHandler func
;
132 GDestroyNotify destroy
;
135 ObMainLoop
*ob_main_loop_new(Display
*display
)
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
;
146 loop
->fd_handlers
= g_hash_table_new_full(g_int_hash
, g_int_equal
,
147 NULL
, fd_handler_destroy
);
149 g_get_current_time(&loop
->now
);
151 /* only do this if we're the first loop created */
154 struct sigaction action
;
157 /* initialize the all_signals_set */
158 sigfillset(&all_signals_set
);
160 sigemptyset(&sigset
);
161 action
.sa_handler
= sighandler
;
162 action
.sa_mask
= sigset
;
163 action
.sa_flags
= SA_NOCLDSTOP
;
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
++;
179 all_loops
= g_slist_prepend(all_loops
, loop
);
184 void ob_main_loop_destroy(ObMainLoop
*loop
)
190 g_assert(loop
->running
== FALSE
);
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
);
198 g_hash_table_destroy(loop
->fd_handlers
);
200 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
201 ObMainLoopTimer
*t
= it
->data
;
202 if (t
->destroy
) t
->destroy(t
->data
);
205 g_slist_free(loop
->timers
);
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
);
215 all_loops
= g_slist_remove(all_loops
, loop
);
217 /* only do this if we're the last loop destroyed */
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
--;
235 static void fd_handle_foreach(gpointer key
,
239 ObMainLoopFdHandlerType
*h
= value
;
242 if (FD_ISSET(h
->fd
, set
))
243 h
->func(h
->fd
, h
->data
);
246 void ob_main_loop_run(ObMainLoop
*loop
)
249 struct timeval
*wait
;
254 loop
->running
= TRUE
;
257 if (loop
->signal_fired
) {
261 /* block signals so that we can do this without the data changing
263 sigprocmask(SIG_SETMASK
, &all_signals_set
, &oldset
);
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
;
272 loop
->signals_fired
[i
]--;
275 loop
->signal_fired
= FALSE
;
277 sigprocmask(SIG_SETMASK
, &oldset
, NULL
);
278 } else if (XPending(loop
->display
)) {
280 XNextEvent(loop
->display
, &e
);
282 for (it
= loop
->x_handlers
; it
; it
= g_slist_next(it
)) {
283 ObMainLoopXHandlerType
*h
= it
->data
;
284 h
->func(&e
, h
->data
);
286 } while (XPending(loop
->display
) && loop
->run
);
288 /* this only runs if there were no x events received */
290 timer_dispatch(loop
, (GTimeVal
**)&wait
);
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'
298 if (!loop
->signal_fired
)
299 select(loop
->fd_max
+ 1, &selset
, NULL
, NULL
, wait
);
301 /* handle the X events with highest prioirity */
302 if (FD_ISSET(loop
->fd_x
, &selset
))
305 g_hash_table_foreach(loop
->fd_handlers
,
306 fd_handle_foreach
, &selset
);
310 loop
->running
= FALSE
;
313 void ob_main_loop_exit(ObMainLoop
*loop
)
318 /*** XEVENT WATCHERS ***/
320 void ob_main_loop_x_add(ObMainLoop
*loop
,
321 ObMainLoopXHandler handler
,
323 GDestroyNotify notify
)
325 ObMainLoopXHandlerType
*h
;
327 h
= g_new(ObMainLoopXHandlerType
, 1);
332 loop
->x_handlers
= g_slist_prepend(loop
->x_handlers
, h
);
335 void ob_main_loop_x_remove(ObMainLoop
*loop
,
336 ObMainLoopXHandler handler
)
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
);
351 /*** SIGNAL WATCHERS ***/
353 static void sighandler(gint sig
)
358 g_return_if_fail(sig
< NUM_SIGNALS
);
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... */
365 fprintf(stderr
, "How are you gentlemen? All your base are"
366 " belong to us. (Openbox received signal %d)\n", sig
);
368 /* die with a core dump */
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
]++;
379 void ob_main_loop_signal_add(ObMainLoop
*loop
,
381 ObMainLoopSignalHandler handler
,
383 GDestroyNotify notify
)
385 ObMainLoopSignalHandlerType
*h
;
387 g_return_if_fail(signal
< NUM_SIGNALS
);
389 h
= g_new(ObMainLoopSignalHandlerType
, 1);
395 loop
->signal_handlers
[h
->signal
] =
396 g_slist_prepend(loop
->signal_handlers
[h
->signal
], h
);
398 if (!all_signals
[signal
].installed
) {
399 struct sigaction action
;
402 sigemptyset(&sigset
);
403 action
.sa_handler
= sighandler
;
404 action
.sa_mask
= sigset
;
405 action
.sa_flags
= SA_NOCLDSTOP
;
407 sigaction(signal
, &action
, &all_signals
[signal
].oldact
);
410 all_signals
[signal
].installed
++;
413 void ob_main_loop_signal_remove(ObMainLoop
*loop
,
414 ObMainLoopSignalHandler handler
)
419 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
420 for (it
= loop
->signal_handlers
[i
]; it
; it
= next
) {
421 ObMainLoopSignalHandlerType
*h
= it
->data
;
423 next
= g_slist_next(it
);
425 if (h
->func
== handler
) {
426 g_assert(all_signals
[h
->signal
].installed
> 0);
428 all_signals
[h
->signal
].installed
--;
429 if (!all_signals
[h
->signal
].installed
) {
430 sigaction(h
->signal
, &all_signals
[h
->signal
].oldact
, NULL
);
433 loop
->signal_handlers
[i
] =
434 g_slist_delete_link(loop
->signal_handlers
[i
], it
);
435 if (h
->destroy
) h
->destroy(h
->data
);
444 /*** FILE DESCRIPTOR WATCHERS ***/
446 static void max_fd_func(gpointer key
, gpointer value
, gpointer data
)
448 ObMainLoop
*loop
= data
;
451 loop
->fd_max
= MAX(loop
->fd_max
, *(gint
*)key
);
454 static void calc_max_fd(ObMainLoop
*loop
)
456 loop
->fd_max
= loop
->fd_x
;
458 g_hash_table_foreach(loop
->fd_handlers
, max_fd_func
, loop
);
461 void ob_main_loop_fd_add(ObMainLoop
*loop
,
463 ObMainLoopFdHandler handler
,
465 GDestroyNotify notify
)
467 ObMainLoopFdHandlerType
*h
;
469 h
= g_new(ObMainLoopFdHandlerType
, 1);
476 g_hash_table_replace(loop
->fd_handlers
, &h
->fd
, h
);
477 FD_SET(h
->fd
, &loop
->fd_set
);
481 static void fd_handler_destroy(gpointer data
)
483 ObMainLoopFdHandlerType
*h
= data
;
485 FD_CLR(h
->fd
, &h
->loop
->fd_set
);
491 void ob_main_loop_fd_remove(ObMainLoop
*loop
,
494 g_hash_table_remove(loop
->fd_handlers
, &fd
);
499 #define NEAREST_TIMEOUT(loop) \
500 (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
502 static glong
timecompare(GTimeVal
*a
, GTimeVal
*b
)
506 if ((r
= b
->tv_sec
- a
->tv_sec
)) return r
;
507 return b
->tv_usec
- a
->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);
533 t
->delay
= microseconds
;
539 g_get_current_time(&loop
->now
);
540 t
->last
= t
->timeout
= loop
->now
;
541 g_time_val_add(&t
->timeout
, t
->delay
);
543 insert_timer(loop
, t
);
546 void ob_main_loop_timeout_remove(ObMainLoop
*loop
,
551 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
552 ObMainLoopTimer
*t
= it
->data
;
553 if (t
->func
== handler
)
558 void ob_main_loop_timeout_remove_data(ObMainLoop
*loop
, GSourceFunc handler
,
559 gpointer data
, gboolean cancel_dest
)
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
)) {
573 /* find the time to wait for the nearest timeout */
574 static gboolean
nearest_timeout_wait(ObMainLoop
*loop
, GTimeVal
*tm
)
576 if (loop
->timers
== NULL
)
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
;
582 while (tm
->tv_usec
< 0) {
583 tm
->tv_usec
+= G_USEC_PER_SEC
;
586 tm
->tv_sec
+= tm
->tv_usec
/ G_USEC_PER_SEC
;
587 tm
->tv_usec
%= G_USEC_PER_SEC
;
594 static void timer_dispatch(ObMainLoop
*loop
, GTimeVal
**wait
)
598 gboolean fired
= FALSE
;
600 g_get_current_time(&loop
->now
);
602 for (it
= loop
->timers
; it
; it
= next
) {
603 ObMainLoopTimer
*curr
;
605 next
= g_slist_next(it
);
609 /* since timer_stop doesn't actually free the timer, we have to do our
610 real freeing in here.
614 loop
->timers
= g_slist_delete_link(loop
->timers
, it
);
616 curr
->destroy(curr
->data
);
621 /* the queue is sorted, so if this timer shouldn't fire, none are
623 if (timecompare(&NEAREST_TIMEOUT(loop
), &loop
->now
) < 0)
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.
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
);
637 curr
->destroy(curr
->data
);
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.
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
;