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 Ben 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.
27 #include <sys/select.h>
30 typedef struct _ObMainLoopTimer ObMainLoopTimer
;
31 typedef struct _ObMainLoopSignal ObMainLoopSignal
;
32 typedef struct _ObMainLoopSignalHandlerType ObMainLoopSignalHandlerType
;
33 typedef struct _ObMainLoopXHandlerType ObMainLoopXHandlerType
;
34 typedef struct _ObMainLoopFdHandlerType ObMainLoopFdHandlerType
;
36 /* this should be more than the number of possible signals on any
38 #define NUM_SIGNALS 99
40 /* all created ObMainLoops. Used by the signal handler to pass along signals */
41 static GSList
*all_loops
;
43 /* signals are global to all loops */
45 guint installed
; /* a ref count */
46 struct sigaction oldact
;
47 } all_signals
[NUM_SIGNALS
];
49 /* a set of all possible signals */
50 sigset_t all_signals_set
;
52 /* signals which cause a core dump, these can't be used for callbacks */
53 static gint core_signals
[] =
66 #define NUM_CORE_SIGNALS (sizeof(core_signals) / sizeof(core_signals[0]))
68 static void sighandler(gint sig
);
69 static void timer_dispatch(ObMainLoop
*loop
, GTimeVal
**wait
);
70 static void fd_handler_destroy(gpointer data
);
76 gboolean run
; /* do keep running */
77 gboolean running
; /* is still running */
81 gint fd_x
; /* The X fd is a special case! */
83 GHashTable
*fd_handlers
;
90 gboolean signal_fired
;
91 guint signals_fired
[NUM_SIGNALS
];
92 GSList
*signal_handlers
[NUM_SIGNALS
];
97 struct _ObMainLoopTimer
102 GDestroyNotify destroy
;
104 /* The timer needs to be freed */
106 /* The time the last fire should've been at */
108 /* When this timer will next trigger */
112 struct _ObMainLoopSignalHandlerType
117 ObMainLoopSignalHandler func
;
118 GDestroyNotify destroy
;
121 struct _ObMainLoopXHandlerType
125 ObMainLoopXHandler func
;
126 GDestroyNotify destroy
;
129 struct _ObMainLoopFdHandlerType
134 ObMainLoopFdHandler func
;
135 GDestroyNotify destroy
;
138 ObMainLoop
*ob_main_loop_new(Display
*display
)
142 loop
= g_new0(ObMainLoop
, 1);
143 loop
->display
= display
;
144 loop
->fd_x
= ConnectionNumber(display
);
145 FD_ZERO(&loop
->fd_set
);
146 FD_SET(loop
->fd_x
, &loop
->fd_set
);
147 loop
->fd_max
= loop
->fd_x
;
149 loop
->fd_handlers
= g_hash_table_new_full(g_int_hash
, g_int_equal
,
150 NULL
, fd_handler_destroy
);
152 g_get_current_time(&loop
->now
);
154 /* only do this if we're the first loop created */
157 struct sigaction action
;
160 /* initialize the all_signals_set */
161 sigfillset(&all_signals_set
);
163 sigemptyset(&sigset
);
164 action
.sa_handler
= sighandler
;
165 action
.sa_mask
= sigset
;
166 action
.sa_flags
= SA_NOCLDSTOP
;
168 /* grab all the signals that cause core dumps */
169 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
) {
170 /* SIGABRT is curiously not grabbed here!! that's because when we
171 get one of the core_signals, we use abort() to dump the core.
172 And having the abort() only go back to our signal handler again
173 is less than optimal */
174 if (core_signals
[i
] != SIGABRT
) {
175 sigaction(core_signals
[i
], &action
,
176 &all_signals
[core_signals
[i
]].oldact
);
177 all_signals
[core_signals
[i
]].installed
++;
182 all_loops
= g_slist_prepend(all_loops
, loop
);
184 loop
->action_queue
= NULL
;
189 void ob_main_loop_destroy(ObMainLoop
*loop
)
195 g_assert(loop
->running
== FALSE
);
197 for (it
= loop
->x_handlers
; it
; it
= next
) {
198 ObMainLoopXHandlerType
*h
= it
->data
;
199 next
= g_slist_next(it
);
200 ob_main_loop_x_remove(loop
, h
->func
);
203 g_hash_table_destroy(loop
->fd_handlers
);
205 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
206 ObMainLoopTimer
*t
= it
->data
;
207 if (t
->destroy
) t
->destroy(t
->data
);
210 g_slist_free(loop
->timers
);
213 for (i
= 0; i
< NUM_SIGNALS
; ++i
)
214 for (it
= loop
->signal_handlers
[i
]; it
; it
= next
) {
215 ObMainLoopSignalHandlerType
*h
= it
->data
;
216 next
= g_slist_next(it
);
217 ob_main_loop_signal_remove(loop
, h
->func
);
220 all_loops
= g_slist_remove(all_loops
, loop
);
222 /* only do this if we're the last loop destroyed */
226 /* grab all the signals that cause core dumps */
227 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
) {
228 if (all_signals
[core_signals
[i
]].installed
) {
229 sigaction(core_signals
[i
],
230 &all_signals
[core_signals
[i
]].oldact
, NULL
);
231 all_signals
[core_signals
[i
]].installed
--;
236 for (it
= loop
->action_queue
; it
; it
= g_slist_next(it
))
237 action_unref(it
->data
);
238 g_slist_free(loop
->action_queue
);
244 static void fd_handle_foreach(gpointer key
,
248 ObMainLoopFdHandlerType
*h
= value
;
251 if (FD_ISSET(h
->fd
, set
))
252 h
->func(h
->fd
, h
->data
);
255 void ob_main_loop_queue_action(ObMainLoop
*loop
, ObAction
*act
)
257 loop
->action_queue
= g_slist_append(loop
->action_queue
, action_copy(act
));
260 static void ob_main_loop_client_destroy(ObClient
*client
, gpointer data
)
262 ObMainLoop
*loop
= data
;
265 for (it
= loop
->action_queue
; it
; it
= g_slist_next(it
)) {
266 ObAction
*act
= it
->data
;
268 if (act
->data
.any
.c
== client
)
269 act
->data
.any
.c
= NULL
;
273 void ob_main_loop_run(ObMainLoop
*loop
)
276 struct timeval
*wait
;
282 loop
->running
= TRUE
;
284 client_add_destructor(ob_main_loop_client_destroy
, loop
);
287 if (loop
->signal_fired
) {
291 /* block signals so that we can do this without the data changing
293 sigprocmask(SIG_SETMASK
, &all_signals_set
, &oldset
);
295 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
296 while (loop
->signals_fired
[i
]) {
297 for (it
= loop
->signal_handlers
[i
];
298 it
; it
= g_slist_next(it
)) {
299 ObMainLoopSignalHandlerType
*h
= it
->data
;
302 loop
->signals_fired
[i
]--;
305 loop
->signal_fired
= FALSE
;
307 sigprocmask(SIG_SETMASK
, &oldset
, NULL
);
308 } else if (XPending(loop
->display
)) {
310 XNextEvent(loop
->display
, &e
);
312 for (it
= loop
->x_handlers
; it
; it
= g_slist_next(it
)) {
313 ObMainLoopXHandlerType
*h
= it
->data
;
314 h
->func(&e
, h
->data
);
316 } while (XPending(loop
->display
));
317 } else if (loop
->action_queue
) {
318 /* only fire off one action at a time, then go back for more
319 X events, since the action might cause some X events (like
323 act
= loop
->action_queue
->data
;
324 if (act
->data
.any
.client_action
== OB_CLIENT_ACTION_ALWAYS
&&
328 g_slist_delete_link(loop
->action_queue
,
333 } while (!act
&& loop
->action_queue
);
336 event_curtime
= act
->data
.any
.time
;
337 act
->func(&act
->data
);
338 event_curtime
= CurrentTime
;
340 g_slist_delete_link(loop
->action_queue
,
345 /* this only runs if there were no x events received */
347 timer_dispatch(loop
, (GTimeVal
**)&wait
);
349 selset
= loop
->fd_set
;
350 /* there is a small race condition here. if a signal occurs
351 between this if() and the select() then we will not process
352 the signal until 'wait' expires. possible solutions include
353 using GStaticMutex, and having the signal handler set 'wait'
355 if (!loop
->signal_fired
)
356 select(loop
->fd_max
+ 1, &selset
, NULL
, NULL
, wait
);
358 /* handle the X events with highest prioirity */
359 if (FD_ISSET(loop
->fd_x
, &selset
))
362 g_hash_table_foreach(loop
->fd_handlers
,
363 fd_handle_foreach
, &selset
);
367 client_remove_destructor(ob_main_loop_client_destroy
);
369 loop
->running
= FALSE
;
372 void ob_main_loop_exit(ObMainLoop
*loop
)
377 /*** XEVENT WATCHERS ***/
379 void ob_main_loop_x_add(ObMainLoop
*loop
,
380 ObMainLoopXHandler handler
,
382 GDestroyNotify notify
)
384 ObMainLoopXHandlerType
*h
;
386 h
= g_new(ObMainLoopXHandlerType
, 1);
391 loop
->x_handlers
= g_slist_prepend(loop
->x_handlers
, h
);
394 void ob_main_loop_x_remove(ObMainLoop
*loop
,
395 ObMainLoopXHandler handler
)
399 for (it
= loop
->x_handlers
; it
; it
= next
) {
400 ObMainLoopXHandlerType
*h
= it
->data
;
401 next
= g_slist_next(it
);
402 if (h
->func
== handler
) {
403 loop
->x_handlers
= g_slist_delete_link(loop
->x_handlers
, it
);
404 if (h
->destroy
) h
->destroy(h
->data
);
410 /*** SIGNAL WATCHERS ***/
412 static void sighandler(gint sig
)
417 g_return_if_fail(sig
< NUM_SIGNALS
);
419 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
)
420 if (sig
== core_signals
[i
]) {
421 /* XXX special case for signals that default to core dump.
422 but throw some helpful output here... */
424 fprintf(stderr
, "How are you gentlemen? All your base are"
425 " belong to us. (Openbox received signal %d)\n", sig
);
427 /* die with a core dump */
431 for (it
= all_loops
; it
; it
= g_slist_next(it
)) {
432 ObMainLoop
*loop
= it
->data
;
433 loop
->signal_fired
= TRUE
;
434 loop
->signals_fired
[sig
]++;
438 void ob_main_loop_signal_add(ObMainLoop
*loop
,
440 ObMainLoopSignalHandler handler
,
442 GDestroyNotify notify
)
444 ObMainLoopSignalHandlerType
*h
;
446 g_return_if_fail(signal
< NUM_SIGNALS
);
448 h
= g_new(ObMainLoopSignalHandlerType
, 1);
454 loop
->signal_handlers
[h
->signal
] =
455 g_slist_prepend(loop
->signal_handlers
[h
->signal
], h
);
457 if (!all_signals
[signal
].installed
) {
458 struct sigaction action
;
461 sigemptyset(&sigset
);
462 action
.sa_handler
= sighandler
;
463 action
.sa_mask
= sigset
;
464 action
.sa_flags
= SA_NOCLDSTOP
;
466 sigaction(signal
, &action
, &all_signals
[signal
].oldact
);
469 all_signals
[signal
].installed
++;
472 void ob_main_loop_signal_remove(ObMainLoop
*loop
,
473 ObMainLoopSignalHandler handler
)
478 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
479 for (it
= loop
->signal_handlers
[i
]; it
; it
= next
) {
480 ObMainLoopSignalHandlerType
*h
= it
->data
;
482 next
= g_slist_next(it
);
484 if (h
->func
== handler
) {
485 g_assert(all_signals
[h
->signal
].installed
> 0);
487 all_signals
[h
->signal
].installed
--;
488 if (!all_signals
[h
->signal
].installed
) {
489 sigaction(h
->signal
, &all_signals
[h
->signal
].oldact
, NULL
);
492 loop
->signal_handlers
[i
] =
493 g_slist_delete_link(loop
->signal_handlers
[i
], it
);
494 if (h
->destroy
) h
->destroy(h
->data
);
503 /*** FILE DESCRIPTOR WATCHERS ***/
505 static void max_fd_func(gpointer key
, gpointer value
, gpointer data
)
507 ObMainLoop
*loop
= data
;
510 loop
->fd_max
= MAX(loop
->fd_max
, *(gint
*)key
);
513 static void calc_max_fd(ObMainLoop
*loop
)
515 loop
->fd_max
= loop
->fd_x
;
517 g_hash_table_foreach(loop
->fd_handlers
, max_fd_func
, loop
);
520 void ob_main_loop_fd_add(ObMainLoop
*loop
,
522 ObMainLoopFdHandler handler
,
524 GDestroyNotify notify
)
526 ObMainLoopFdHandlerType
*h
;
528 h
= g_new(ObMainLoopFdHandlerType
, 1);
535 g_hash_table_replace(loop
->fd_handlers
, &h
->fd
, h
);
536 FD_SET(h
->fd
, &loop
->fd_set
);
540 static void fd_handler_destroy(gpointer data
)
542 ObMainLoopFdHandlerType
*h
= data
;
544 FD_CLR(h
->fd
, &h
->loop
->fd_set
);
550 void ob_main_loop_fd_remove(ObMainLoop
*loop
,
553 g_hash_table_remove(loop
->fd_handlers
, &fd
);
558 #define NEAREST_TIMEOUT(loop) \
559 (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
561 static glong
timecompare(GTimeVal
*a
, GTimeVal
*b
)
565 if ((r
= b
->tv_sec
- a
->tv_sec
)) return r
;
566 return b
->tv_usec
- a
->tv_usec
;
570 static void insert_timer(ObMainLoop
*loop
, ObMainLoopTimer
*ins
)
573 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
574 ObMainLoopTimer
*t
= it
->data
;
575 if (timecompare(&ins
->timeout
, &t
->timeout
) >= 0) {
576 loop
->timers
= g_slist_insert_before(loop
->timers
, it
, ins
);
580 if (it
== NULL
) /* didnt fit anywhere in the list */
581 loop
->timers
= g_slist_append(loop
->timers
, ins
);
584 void ob_main_loop_timeout_add(ObMainLoop
*loop
,
588 GDestroyNotify notify
)
590 ObMainLoopTimer
*t
= g_new(ObMainLoopTimer
, 1);
591 t
->delay
= microseconds
;
596 g_get_current_time(&loop
->now
);
597 t
->last
= t
->timeout
= loop
->now
;
598 g_time_val_add(&t
->timeout
, t
->delay
);
600 insert_timer(loop
, t
);
603 void ob_main_loop_timeout_remove(ObMainLoop
*loop
,
608 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
609 ObMainLoopTimer
*t
= it
->data
;
610 if (t
->func
== handler
)
615 void ob_main_loop_timeout_remove_data(ObMainLoop
*loop
, GSourceFunc handler
,
616 gpointer data
, gboolean cancel_dest
)
620 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
621 ObMainLoopTimer
*t
= it
->data
;
622 if (t
->func
== handler
&& t
->data
== data
) {
630 /* find the time to wait for the nearest timeout */
631 static gboolean
nearest_timeout_wait(ObMainLoop
*loop
, GTimeVal
*tm
)
633 if (loop
->timers
== NULL
)
636 tm
->tv_sec
= NEAREST_TIMEOUT(loop
).tv_sec
- loop
->now
.tv_sec
;
637 tm
->tv_usec
= NEAREST_TIMEOUT(loop
).tv_usec
- loop
->now
.tv_usec
;
639 while (tm
->tv_usec
< 0) {
640 tm
->tv_usec
+= G_USEC_PER_SEC
;
643 tm
->tv_sec
+= tm
->tv_usec
/ G_USEC_PER_SEC
;
644 tm
->tv_usec
%= G_USEC_PER_SEC
;
651 static void timer_dispatch(ObMainLoop
*loop
, GTimeVal
**wait
)
655 gboolean fired
= FALSE
;
657 g_get_current_time(&loop
->now
);
659 for (it
= loop
->timers
; it
; it
= next
) {
660 ObMainLoopTimer
*curr
;
662 next
= g_slist_next(it
);
666 /* since timer_stop doesn't actually free the timer, we have to do our
667 real freeing in here.
671 loop
->timers
= g_slist_delete_link(loop
->timers
, it
);
673 curr
->destroy(curr
->data
);
678 /* the queue is sorted, so if this timer shouldn't fire, none are
680 if (timecompare(&NEAREST_TIMEOUT(loop
), &loop
->now
) < 0)
683 /* we set the last fired time to delay msec after the previous firing,
684 then re-insert. timers maintain their order and may trigger more
685 than once if they've waited more than one delay's worth of time.
687 loop
->timers
= g_slist_delete_link(loop
->timers
, it
);
688 g_time_val_add(&curr
->last
, curr
->delay
);
689 if (curr
->func(curr
->data
)) {
690 g_time_val_add(&curr
->timeout
, curr
->delay
);
691 insert_timer(loop
, curr
);
694 curr
->destroy(curr
->data
);
702 /* if at least one timer fires, then don't wait on X events, as there
703 may already be some in the queue from the timer callbacks.
705 loop
->ret_wait
.tv_sec
= loop
->ret_wait
.tv_usec
= 0;
706 *wait
= &loop
->ret_wait
;
707 } else if (nearest_timeout_wait(loop
, &loop
->ret_wait
))
708 *wait
= &loop
->ret_wait
;