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 */
219 /* grab all the signals that cause core dumps */
220 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
) {
221 if (all_signals
[core_signals
[i
]].installed
) {
222 sigaction(core_signals
[i
],
223 &all_signals
[core_signals
[i
]].oldact
, NULL
);
224 all_signals
[core_signals
[i
]].installed
--;
233 static void fd_handle_foreach(gpointer key
,
237 ObMainLoopFdHandlerType
*h
= value
;
240 if (FD_ISSET(h
->fd
, set
))
241 h
->func(h
->fd
, h
->data
);
244 void ob_main_loop_run(ObMainLoop
*loop
)
247 struct timeval
*wait
;
252 loop
->running
= TRUE
;
255 if (loop
->signal_fired
) {
259 /* block signals so that we can do this without the data changing
261 sigprocmask(SIG_SETMASK
, &all_signals_set
, &oldset
);
263 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
264 while (loop
->signals_fired
[i
]) {
265 for (it
= loop
->signal_handlers
[i
];
266 it
; it
= g_slist_next(it
)) {
267 ObMainLoopSignalHandlerType
*h
= it
->data
;
270 loop
->signals_fired
[i
]--;
273 loop
->signal_fired
= FALSE
;
275 sigprocmask(SIG_SETMASK
, &oldset
, NULL
);
276 } else if (XPending(loop
->display
)) {
278 XNextEvent(loop
->display
, &e
);
280 for (it
= loop
->x_handlers
; it
; it
= g_slist_next(it
)) {
281 ObMainLoopXHandlerType
*h
= it
->data
;
282 h
->func(&e
, h
->data
);
284 } while (XPending(loop
->display
) && loop
->run
);
286 /* this only runs if there were no x events received */
288 timer_dispatch(loop
, (GTimeVal
**)&wait
);
290 selset
= loop
->fd_set
;
291 /* there is a small race condition here. if a signal occurs
292 between this if() and the select() then we will not process
293 the signal until 'wait' expires. possible solutions include
294 using GStaticMutex, and having the signal handler set 'wait'
296 if (!loop
->signal_fired
)
297 select(loop
->fd_max
+ 1, &selset
, NULL
, NULL
, wait
);
299 /* handle the X events with highest prioirity */
300 if (FD_ISSET(loop
->fd_x
, &selset
))
303 g_hash_table_foreach(loop
->fd_handlers
,
304 fd_handle_foreach
, &selset
);
308 loop
->running
= FALSE
;
311 void ob_main_loop_exit(ObMainLoop
*loop
)
316 /*** XEVENT WATCHERS ***/
318 void ob_main_loop_x_add(ObMainLoop
*loop
,
319 ObMainLoopXHandler handler
,
321 GDestroyNotify notify
)
323 ObMainLoopXHandlerType
*h
;
325 h
= g_new(ObMainLoopXHandlerType
, 1);
330 loop
->x_handlers
= g_slist_prepend(loop
->x_handlers
, h
);
333 void ob_main_loop_x_remove(ObMainLoop
*loop
,
334 ObMainLoopXHandler handler
)
338 for (it
= loop
->x_handlers
; it
; it
= next
) {
339 ObMainLoopXHandlerType
*h
= it
->data
;
340 next
= g_slist_next(it
);
341 if (h
->func
== handler
) {
342 loop
->x_handlers
= g_slist_delete_link(loop
->x_handlers
, it
);
343 if (h
->destroy
) h
->destroy(h
->data
);
349 /*** SIGNAL WATCHERS ***/
351 static void sighandler(gint sig
)
356 g_return_if_fail(sig
< NUM_SIGNALS
);
358 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
)
359 if (sig
== core_signals
[i
]) {
360 /* XXX special case for signals that default to core dump.
361 but throw some helpful output here... */
363 fprintf(stderr
, "How are you gentlemen? All your base are"
364 " belong to us. (Openbox received signal %d)\n", sig
);
366 /* die with a core dump */
370 for (it
= all_loops
; it
; it
= g_slist_next(it
)) {
371 ObMainLoop
*loop
= it
->data
;
372 loop
->signal_fired
= TRUE
;
373 loop
->signals_fired
[sig
]++;
377 void ob_main_loop_signal_add(ObMainLoop
*loop
,
379 ObMainLoopSignalHandler handler
,
381 GDestroyNotify notify
)
383 ObMainLoopSignalHandlerType
*h
;
385 g_return_if_fail(signal
< NUM_SIGNALS
);
387 h
= g_new(ObMainLoopSignalHandlerType
, 1);
393 loop
->signal_handlers
[h
->signal
] =
394 g_slist_prepend(loop
->signal_handlers
[h
->signal
], h
);
396 if (!all_signals
[signal
].installed
) {
397 struct sigaction action
;
400 sigemptyset(&sigset
);
401 action
.sa_handler
= sighandler
;
402 action
.sa_mask
= sigset
;
403 action
.sa_flags
= SA_NOCLDSTOP
;
405 sigaction(signal
, &action
, &all_signals
[signal
].oldact
);
408 all_signals
[signal
].installed
++;
411 void ob_main_loop_signal_remove(ObMainLoop
*loop
,
412 ObMainLoopSignalHandler handler
)
417 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
418 for (it
= loop
->signal_handlers
[i
]; it
; it
= next
) {
419 ObMainLoopSignalHandlerType
*h
= it
->data
;
421 next
= g_slist_next(it
);
423 if (h
->func
== handler
) {
424 g_assert(all_signals
[h
->signal
].installed
> 0);
426 all_signals
[h
->signal
].installed
--;
427 if (!all_signals
[h
->signal
].installed
) {
428 sigaction(h
->signal
, &all_signals
[h
->signal
].oldact
, NULL
);
431 loop
->signal_handlers
[i
] =
432 g_slist_delete_link(loop
->signal_handlers
[i
], it
);
433 if (h
->destroy
) h
->destroy(h
->data
);
442 /*** FILE DESCRIPTOR WATCHERS ***/
444 static void max_fd_func(gpointer key
, gpointer value
, gpointer data
)
446 ObMainLoop
*loop
= data
;
449 loop
->fd_max
= MAX(loop
->fd_max
, *(gint
*)key
);
452 static void calc_max_fd(ObMainLoop
*loop
)
454 loop
->fd_max
= loop
->fd_x
;
456 g_hash_table_foreach(loop
->fd_handlers
, max_fd_func
, loop
);
459 void ob_main_loop_fd_add(ObMainLoop
*loop
,
461 ObMainLoopFdHandler handler
,
463 GDestroyNotify notify
)
465 ObMainLoopFdHandlerType
*h
;
467 h
= g_new(ObMainLoopFdHandlerType
, 1);
474 g_hash_table_replace(loop
->fd_handlers
, &h
->fd
, h
);
475 FD_SET(h
->fd
, &loop
->fd_set
);
479 static void fd_handler_destroy(gpointer data
)
481 ObMainLoopFdHandlerType
*h
= data
;
483 FD_CLR(h
->fd
, &h
->loop
->fd_set
);
489 void ob_main_loop_fd_remove(ObMainLoop
*loop
,
492 g_hash_table_remove(loop
->fd_handlers
, &fd
);
497 #define NEAREST_TIMEOUT(loop) \
498 (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
500 static glong
timecompare(GTimeVal
*a
, GTimeVal
*b
)
504 if ((r
= b
->tv_sec
- a
->tv_sec
)) return r
;
505 return b
->tv_usec
- a
->tv_usec
;
509 static void insert_timer(ObMainLoop
*loop
, ObMainLoopTimer
*ins
)
512 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
513 ObMainLoopTimer
*t
= it
->data
;
514 if (timecompare(&ins
->timeout
, &t
->timeout
) >= 0) {
515 loop
->timers
= g_slist_insert_before(loop
->timers
, it
, ins
);
519 if (it
== NULL
) /* didnt fit anywhere in the list */
520 loop
->timers
= g_slist_append(loop
->timers
, ins
);
523 void ob_main_loop_timeout_add(ObMainLoop
*loop
,
528 GDestroyNotify notify
)
530 ObMainLoopTimer
*t
= g_new(ObMainLoopTimer
, 1);
531 t
->delay
= microseconds
;
537 g_get_current_time(&loop
->now
);
538 t
->last
= t
->timeout
= loop
->now
;
539 g_time_val_add(&t
->timeout
, t
->delay
);
541 insert_timer(loop
, t
);
544 void ob_main_loop_timeout_remove(ObMainLoop
*loop
,
549 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
550 ObMainLoopTimer
*t
= it
->data
;
551 if (t
->func
== handler
)
556 void ob_main_loop_timeout_remove_data(ObMainLoop
*loop
, GSourceFunc handler
,
557 gpointer data
, gboolean cancel_dest
)
561 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
562 ObMainLoopTimer
*t
= it
->data
;
563 if (t
->func
== handler
&& t
->equal(t
->data
, data
)) {
571 /* find the time to wait for the nearest timeout */
572 static gboolean
nearest_timeout_wait(ObMainLoop
*loop
, GTimeVal
*tm
)
574 if (loop
->timers
== NULL
)
577 tm
->tv_sec
= NEAREST_TIMEOUT(loop
).tv_sec
- loop
->now
.tv_sec
;
578 tm
->tv_usec
= NEAREST_TIMEOUT(loop
).tv_usec
- loop
->now
.tv_usec
;
580 while (tm
->tv_usec
< 0) {
581 tm
->tv_usec
+= G_USEC_PER_SEC
;
584 tm
->tv_sec
+= tm
->tv_usec
/ G_USEC_PER_SEC
;
585 tm
->tv_usec
%= G_USEC_PER_SEC
;
592 static void timer_dispatch(ObMainLoop
*loop
, GTimeVal
**wait
)
596 gboolean fired
= FALSE
;
598 g_get_current_time(&loop
->now
);
600 for (it
= loop
->timers
; it
; it
= next
) {
601 ObMainLoopTimer
*curr
;
603 next
= g_slist_next(it
);
607 /* since timer_stop doesn't actually free the timer, we have to do our
608 real freeing in here.
612 loop
->timers
= g_slist_delete_link(loop
->timers
, it
);
614 curr
->destroy(curr
->data
);
619 /* the queue is sorted, so if this timer shouldn't fire, none are
621 if (timecompare(&NEAREST_TIMEOUT(loop
), &loop
->now
) < 0)
624 /* we set the last fired time to delay msec after the previous firing,
625 then re-insert. timers maintain their order and may trigger more
626 than once if they've waited more than one delay's worth of time.
628 loop
->timers
= g_slist_delete_link(loop
->timers
, it
);
629 g_time_val_add(&curr
->last
, curr
->delay
);
630 if (curr
->func(curr
->data
)) {
631 g_time_val_add(&curr
->timeout
, curr
->delay
);
632 insert_timer(loop
, curr
);
635 curr
->destroy(curr
->data
);
643 /* if at least one timer fires, then don't wait on X events, as there
644 may already be some in the queue from the timer callbacks.
646 loop
->ret_wait
.tv_sec
= loop
->ret_wait
.tv_usec
= 0;
647 *wait
= &loop
->ret_wait
;
648 } else if (nearest_timeout_wait(loop
, &loop
->ret_wait
))
649 *wait
= &loop
->ret_wait
;