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.
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
103 GDestroyNotify destroy
;
105 /* The timer needs to be freed */
107 /* The time the last fire should've been at */
109 /* When this timer will next trigger */
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
);
185 loop
->action_queue
= NULL
;
190 void ob_main_loop_destroy(ObMainLoop
*loop
)
196 g_assert(loop
->running
== FALSE
);
198 for (it
= loop
->x_handlers
; it
; it
= next
) {
199 ObMainLoopXHandlerType
*h
= it
->data
;
200 next
= g_slist_next(it
);
201 ob_main_loop_x_remove(loop
, h
->func
);
204 g_hash_table_destroy(loop
->fd_handlers
);
206 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
207 ObMainLoopTimer
*t
= it
->data
;
208 if (t
->destroy
) t
->destroy(t
->data
);
211 g_slist_free(loop
->timers
);
214 for (i
= 0; i
< NUM_SIGNALS
; ++i
)
215 for (it
= loop
->signal_handlers
[i
]; it
; it
= next
) {
216 ObMainLoopSignalHandlerType
*h
= it
->data
;
217 next
= g_slist_next(it
);
218 ob_main_loop_signal_remove(loop
, h
->func
);
221 all_loops
= g_slist_remove(all_loops
, loop
);
223 /* only do this if we're the last loop destroyed */
227 /* grab all the signals that cause core dumps */
228 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
) {
229 if (all_signals
[core_signals
[i
]].installed
) {
230 sigaction(core_signals
[i
],
231 &all_signals
[core_signals
[i
]].oldact
, NULL
);
232 all_signals
[core_signals
[i
]].installed
--;
237 for (it
= loop
->action_queue
; it
; it
= g_slist_next(it
))
238 action_unref(it
->data
);
239 g_slist_free(loop
->action_queue
);
245 static void fd_handle_foreach(gpointer key
,
249 ObMainLoopFdHandlerType
*h
= value
;
252 if (FD_ISSET(h
->fd
, set
))
253 h
->func(h
->fd
, h
->data
);
256 void ob_main_loop_queue_action(ObMainLoop
*loop
, ObAction
*act
)
258 loop
->action_queue
= g_slist_append(loop
->action_queue
, action_copy(act
));
261 static void ob_main_loop_client_destroy(ObClient
*client
, gpointer data
)
263 ObMainLoop
*loop
= data
;
266 for (it
= loop
->action_queue
; it
; it
= g_slist_next(it
)) {
267 ObAction
*act
= it
->data
;
269 if (act
->data
.any
.c
== client
)
270 act
->data
.any
.c
= NULL
;
274 void ob_main_loop_run(ObMainLoop
*loop
)
277 struct timeval
*wait
;
283 loop
->running
= TRUE
;
285 client_add_destroy_notify(ob_main_loop_client_destroy
, loop
);
288 if (loop
->signal_fired
) {
292 /* block signals so that we can do this without the data changing
294 sigprocmask(SIG_SETMASK
, &all_signals_set
, &oldset
);
296 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
297 while (loop
->signals_fired
[i
]) {
298 for (it
= loop
->signal_handlers
[i
];
299 it
; it
= g_slist_next(it
)) {
300 ObMainLoopSignalHandlerType
*h
= it
->data
;
303 loop
->signals_fired
[i
]--;
306 loop
->signal_fired
= FALSE
;
308 sigprocmask(SIG_SETMASK
, &oldset
, NULL
);
309 } else if (XPending(loop
->display
)) {
311 XNextEvent(loop
->display
, &e
);
313 for (it
= loop
->x_handlers
; it
; it
= g_slist_next(it
)) {
314 ObMainLoopXHandlerType
*h
= it
->data
;
315 h
->func(&e
, h
->data
);
317 } while (XPending(loop
->display
) && loop
->run
);
318 } else if (loop
->action_queue
) {
319 /* only fire off one action at a time, then go back for more
320 X events, since the action might cause some X events (like
324 act
= loop
->action_queue
->data
;
325 if (act
->data
.any
.client_action
== OB_CLIENT_ACTION_ALWAYS
&&
329 g_slist_delete_link(loop
->action_queue
,
334 } while (!act
&& loop
->action_queue
&& loop
->run
);
337 event_curtime
= act
->data
.any
.time
;
338 act
->func(&act
->data
);
339 event_curtime
= CurrentTime
;
341 g_slist_delete_link(loop
->action_queue
,
346 /* this only runs if there were no x events received */
348 timer_dispatch(loop
, (GTimeVal
**)&wait
);
350 selset
= loop
->fd_set
;
351 /* there is a small race condition here. if a signal occurs
352 between this if() and the select() then we will not process
353 the signal until 'wait' expires. possible solutions include
354 using GStaticMutex, and having the signal handler set 'wait'
356 if (!loop
->signal_fired
)
357 select(loop
->fd_max
+ 1, &selset
, NULL
, NULL
, wait
);
359 /* handle the X events with highest prioirity */
360 if (FD_ISSET(loop
->fd_x
, &selset
))
363 g_hash_table_foreach(loop
->fd_handlers
,
364 fd_handle_foreach
, &selset
);
368 client_remove_destroy_notify(ob_main_loop_client_destroy
);
370 loop
->running
= FALSE
;
373 void ob_main_loop_exit(ObMainLoop
*loop
)
378 /*** XEVENT WATCHERS ***/
380 void ob_main_loop_x_add(ObMainLoop
*loop
,
381 ObMainLoopXHandler handler
,
383 GDestroyNotify notify
)
385 ObMainLoopXHandlerType
*h
;
387 h
= g_new(ObMainLoopXHandlerType
, 1);
392 loop
->x_handlers
= g_slist_prepend(loop
->x_handlers
, h
);
395 void ob_main_loop_x_remove(ObMainLoop
*loop
,
396 ObMainLoopXHandler handler
)
400 for (it
= loop
->x_handlers
; it
; it
= next
) {
401 ObMainLoopXHandlerType
*h
= it
->data
;
402 next
= g_slist_next(it
);
403 if (h
->func
== handler
) {
404 loop
->x_handlers
= g_slist_delete_link(loop
->x_handlers
, it
);
405 if (h
->destroy
) h
->destroy(h
->data
);
411 /*** SIGNAL WATCHERS ***/
413 static void sighandler(gint sig
)
418 g_return_if_fail(sig
< NUM_SIGNALS
);
420 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
)
421 if (sig
== core_signals
[i
]) {
422 /* XXX special case for signals that default to core dump.
423 but throw some helpful output here... */
425 fprintf(stderr
, "How are you gentlemen? All your base are"
426 " belong to us. (Openbox received signal %d)\n", sig
);
428 /* die with a core dump */
432 for (it
= all_loops
; it
; it
= g_slist_next(it
)) {
433 ObMainLoop
*loop
= it
->data
;
434 loop
->signal_fired
= TRUE
;
435 loop
->signals_fired
[sig
]++;
439 void ob_main_loop_signal_add(ObMainLoop
*loop
,
441 ObMainLoopSignalHandler handler
,
443 GDestroyNotify notify
)
445 ObMainLoopSignalHandlerType
*h
;
447 g_return_if_fail(signal
< NUM_SIGNALS
);
449 h
= g_new(ObMainLoopSignalHandlerType
, 1);
455 loop
->signal_handlers
[h
->signal
] =
456 g_slist_prepend(loop
->signal_handlers
[h
->signal
], h
);
458 if (!all_signals
[signal
].installed
) {
459 struct sigaction action
;
462 sigemptyset(&sigset
);
463 action
.sa_handler
= sighandler
;
464 action
.sa_mask
= sigset
;
465 action
.sa_flags
= SA_NOCLDSTOP
;
467 sigaction(signal
, &action
, &all_signals
[signal
].oldact
);
470 all_signals
[signal
].installed
++;
473 void ob_main_loop_signal_remove(ObMainLoop
*loop
,
474 ObMainLoopSignalHandler handler
)
479 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
480 for (it
= loop
->signal_handlers
[i
]; it
; it
= next
) {
481 ObMainLoopSignalHandlerType
*h
= it
->data
;
483 next
= g_slist_next(it
);
485 if (h
->func
== handler
) {
486 g_assert(all_signals
[h
->signal
].installed
> 0);
488 all_signals
[h
->signal
].installed
--;
489 if (!all_signals
[h
->signal
].installed
) {
490 sigaction(h
->signal
, &all_signals
[h
->signal
].oldact
, NULL
);
493 loop
->signal_handlers
[i
] =
494 g_slist_delete_link(loop
->signal_handlers
[i
], it
);
495 if (h
->destroy
) h
->destroy(h
->data
);
504 /*** FILE DESCRIPTOR WATCHERS ***/
506 static void max_fd_func(gpointer key
, gpointer value
, gpointer data
)
508 ObMainLoop
*loop
= data
;
511 loop
->fd_max
= MAX(loop
->fd_max
, *(gint
*)key
);
514 static void calc_max_fd(ObMainLoop
*loop
)
516 loop
->fd_max
= loop
->fd_x
;
518 g_hash_table_foreach(loop
->fd_handlers
, max_fd_func
, loop
);
521 void ob_main_loop_fd_add(ObMainLoop
*loop
,
523 ObMainLoopFdHandler handler
,
525 GDestroyNotify notify
)
527 ObMainLoopFdHandlerType
*h
;
529 h
= g_new(ObMainLoopFdHandlerType
, 1);
536 g_hash_table_replace(loop
->fd_handlers
, &h
->fd
, h
);
537 FD_SET(h
->fd
, &loop
->fd_set
);
541 static void fd_handler_destroy(gpointer data
)
543 ObMainLoopFdHandlerType
*h
= data
;
545 FD_CLR(h
->fd
, &h
->loop
->fd_set
);
551 void ob_main_loop_fd_remove(ObMainLoop
*loop
,
554 g_hash_table_remove(loop
->fd_handlers
, &fd
);
559 #define NEAREST_TIMEOUT(loop) \
560 (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
562 static glong
timecompare(GTimeVal
*a
, GTimeVal
*b
)
566 if ((r
= b
->tv_sec
- a
->tv_sec
)) return r
;
567 return b
->tv_usec
- a
->tv_usec
;
571 static void insert_timer(ObMainLoop
*loop
, ObMainLoopTimer
*ins
)
574 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
575 ObMainLoopTimer
*t
= it
->data
;
576 if (timecompare(&ins
->timeout
, &t
->timeout
) >= 0) {
577 loop
->timers
= g_slist_insert_before(loop
->timers
, it
, ins
);
581 if (it
== NULL
) /* didnt fit anywhere in the list */
582 loop
->timers
= g_slist_append(loop
->timers
, ins
);
585 void ob_main_loop_timeout_add(ObMainLoop
*loop
,
590 GDestroyNotify notify
)
592 ObMainLoopTimer
*t
= g_new(ObMainLoopTimer
, 1);
593 t
->delay
= microseconds
;
599 g_get_current_time(&loop
->now
);
600 t
->last
= t
->timeout
= loop
->now
;
601 g_time_val_add(&t
->timeout
, t
->delay
);
603 insert_timer(loop
, t
);
606 void ob_main_loop_timeout_remove(ObMainLoop
*loop
,
611 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
612 ObMainLoopTimer
*t
= it
->data
;
613 if (t
->func
== handler
)
618 void ob_main_loop_timeout_remove_data(ObMainLoop
*loop
, GSourceFunc handler
,
619 gpointer data
, gboolean cancel_dest
)
623 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
624 ObMainLoopTimer
*t
= it
->data
;
625 if (t
->func
== handler
&& t
->equal(t
->data
, data
)) {
633 /* find the time to wait for the nearest timeout */
634 static gboolean
nearest_timeout_wait(ObMainLoop
*loop
, GTimeVal
*tm
)
636 if (loop
->timers
== NULL
)
639 tm
->tv_sec
= NEAREST_TIMEOUT(loop
).tv_sec
- loop
->now
.tv_sec
;
640 tm
->tv_usec
= NEAREST_TIMEOUT(loop
).tv_usec
- loop
->now
.tv_usec
;
642 while (tm
->tv_usec
< 0) {
643 tm
->tv_usec
+= G_USEC_PER_SEC
;
646 tm
->tv_sec
+= tm
->tv_usec
/ G_USEC_PER_SEC
;
647 tm
->tv_usec
%= G_USEC_PER_SEC
;
654 static void timer_dispatch(ObMainLoop
*loop
, GTimeVal
**wait
)
658 gboolean fired
= FALSE
;
660 g_get_current_time(&loop
->now
);
662 for (it
= loop
->timers
; it
; it
= next
) {
663 ObMainLoopTimer
*curr
;
665 next
= g_slist_next(it
);
669 /* since timer_stop doesn't actually free the timer, we have to do our
670 real freeing in here.
674 loop
->timers
= g_slist_delete_link(loop
->timers
, it
);
676 curr
->destroy(curr
->data
);
681 /* the queue is sorted, so if this timer shouldn't fire, none are
683 if (timecompare(&NEAREST_TIMEOUT(loop
), &loop
->now
) < 0)
686 /* we set the last fired time to delay msec after the previous firing,
687 then re-insert. timers maintain their order and may trigger more
688 than once if they've waited more than one delay's worth of time.
690 loop
->timers
= g_slist_delete_link(loop
->timers
, it
);
691 g_time_val_add(&curr
->last
, curr
->delay
);
692 if (curr
->func(curr
->data
)) {
693 g_time_val_add(&curr
->timeout
, curr
->delay
);
694 insert_timer(loop
, curr
);
697 curr
->destroy(curr
->data
);
705 /* if at least one timer fires, then don't wait on X events, as there
706 may already be some in the queue from the timer callbacks.
708 loop
->ret_wait
.tv_sec
= loop
->ret_wait
.tv_usec
= 0;
709 *wait
= &loop
->ret_wait
;
710 } else if (nearest_timeout_wait(loop
, &loop
->ret_wait
))
711 *wait
= &loop
->ret_wait
;