1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 mainloop.c for the Openbox window manager
4 Copyright (c) 2003 Ben Jansens
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 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
];
95 struct _ObMainLoopTimer
100 GDestroyNotify destroy
;
102 /* The timer needs to be freed */
104 /* The time the last fire should've been at */
106 /* When this timer will next trigger */
110 struct _ObMainLoopSignalHandlerType
115 ObMainLoopSignalHandler func
;
116 GDestroyNotify destroy
;
119 struct _ObMainLoopXHandlerType
123 ObMainLoopXHandler func
;
124 ObMainLoopXDoneHandler done_func
;
125 GDestroyNotify destroy
;
128 struct _ObMainLoopFdHandlerType
133 ObMainLoopFdHandler func
;
134 GDestroyNotify destroy
;
137 ObMainLoop
*ob_main_loop_new(Display
*display
)
141 loop
= g_new0(ObMainLoop
, 1);
142 loop
->display
= display
;
143 loop
->fd_x
= ConnectionNumber(display
);
144 FD_ZERO(&loop
->fd_set
);
145 FD_SET(loop
->fd_x
, &loop
->fd_set
);
146 loop
->fd_max
= loop
->fd_x
;
148 loop
->fd_handlers
= g_hash_table_new_full(g_int_hash
, g_int_equal
,
149 NULL
, fd_handler_destroy
);
151 g_get_current_time(&loop
->now
);
153 /* only do this if we're the first loop created */
156 struct sigaction action
;
159 /* initialize the all_signals_set */
160 sigfillset(&all_signals_set
);
162 sigemptyset(&sigset
);
163 action
.sa_handler
= sighandler
;
164 action
.sa_mask
= sigset
;
165 action
.sa_flags
= SA_NOCLDSTOP
;
167 /* grab all the signals that cause core dumps */
168 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
) {
169 /* SIGABRT is curiously not grabbed here!! that's because when we
170 get one of the core_signals, we use abort() to dump the core.
171 And having the abort() only go back to our signal handler again
172 is less than optimal */
173 if (core_signals
[i
] != SIGABRT
) {
174 sigaction(core_signals
[i
], &action
,
175 &all_signals
[core_signals
[i
]].oldact
);
176 all_signals
[core_signals
[i
]].installed
++;
181 all_loops
= g_slist_prepend(all_loops
, loop
);
183 loop
->action_queue
= NULL
;
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 */
225 /* grab all the signals that cause core dumps */
226 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
) {
227 if (all_signals
[core_signals
[i
]].installed
) {
228 sigaction(core_signals
[i
],
229 &all_signals
[core_signals
[i
]].oldact
, NULL
);
230 all_signals
[core_signals
[i
]].installed
--;
235 for (it
= loop
->action_queue
; it
; it
= g_slist_next(it
))
236 action_unref(it
->data
);
237 g_slist_free(loop
->action_queue
);
243 static void fd_handle_foreach(gpointer key
,
247 ObMainLoopFdHandlerType
*h
= value
;
250 if (FD_ISSET(h
->fd
, set
))
251 h
->func(h
->fd
, h
->data
);
254 void ob_main_loop_queue_action(ObMainLoop
*loop
, ObAction
*act
)
256 loop
->action_queue
= g_slist_append(loop
->action_queue
, action_copy(act
));
259 static void ob_main_loop_client_destroy(ObClient
*client
, gpointer data
)
261 ObMainLoop
*loop
= data
;
264 for (it
= loop
->action_queue
; it
; it
= g_slist_next(it
)) {
265 ObAction
*act
= it
->data
;
267 if (act
->data
.any
.c
== client
)
268 act
->data
.any
.c
= NULL
;
272 void ob_main_loop_run(ObMainLoop
*loop
)
275 struct timeval
*wait
;
281 loop
->running
= TRUE
;
283 client_add_destructor(ob_main_loop_client_destroy
, loop
);
286 if (loop
->signal_fired
) {
290 /* block signals so that we can do this without the data changing
292 sigprocmask(SIG_SETMASK
, &all_signals_set
, &oldset
);
294 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
295 while (loop
->signals_fired
[i
]) {
296 for (it
= loop
->signal_handlers
[i
];
297 it
; it
= g_slist_next(it
)) {
298 ObMainLoopSignalHandlerType
*h
= it
->data
;
301 loop
->signals_fired
[i
]--;
304 loop
->signal_fired
= FALSE
;
306 sigprocmask(SIG_SETMASK
, &oldset
, NULL
);
307 } else if (XPending(loop
->display
)) {
309 XNextEvent(loop
->display
, &e
);
311 for (it
= loop
->x_handlers
; it
; it
= g_slist_next(it
)) {
312 ObMainLoopXHandlerType
*h
= it
->data
;
313 h
->func(&e
, h
->data
);
315 } while (XPending(loop
->display
));
317 for (it
= loop
->x_handlers
; it
; it
= g_slist_next(it
)) {
318 ObMainLoopXHandlerType
*h
= it
->data
;
320 h
->done_func(h
->data
);
323 if (loop
->action_queue
) {
324 /* only fire off one action at a time, then go back for more
325 X events, since the action might cause some X events (like
329 act
= loop
->action_queue
->data
;
330 if (act
->data
.any
.client_action
==
331 OB_CLIENT_ACTION_ALWAYS
&&
335 g_slist_delete_link(loop
->action_queue
,
340 } while (!act
&& loop
->action_queue
);
343 act
->func(&act
->data
);
345 g_slist_delete_link(loop
->action_queue
,
350 /* this only runs if there were no x events received */
352 timer_dispatch(loop
, (GTimeVal
**)&wait
);
354 selset
= loop
->fd_set
;
355 /* there is a small race condition here. if a signal occurs
356 between this if() and the select() then we will not process
357 the signal until 'wait' expires. possible solutions include
358 using GStaticMutex, and having the signal handler set 'wait'
360 if (!loop
->signal_fired
)
361 select(loop
->fd_max
+ 1, &selset
, NULL
, NULL
, wait
);
363 /* handle the X events with highest prioirity */
364 if (FD_ISSET(loop
->fd_x
, &selset
))
367 g_hash_table_foreach(loop
->fd_handlers
,
368 fd_handle_foreach
, &selset
);
373 client_remove_destructor(ob_main_loop_client_destroy
);
375 loop
->running
= FALSE
;
378 void ob_main_loop_exit(ObMainLoop
*loop
)
383 /*** XEVENT WATCHERS ***/
385 void ob_main_loop_x_add(ObMainLoop
*loop
,
386 ObMainLoopXHandler handler
,
387 ObMainLoopXDoneHandler done_handler
,
389 GDestroyNotify notify
)
391 ObMainLoopXHandlerType
*h
;
393 h
= g_new(ObMainLoopXHandlerType
, 1);
396 h
->done_func
= done_handler
;
399 loop
->x_handlers
= g_slist_prepend(loop
->x_handlers
, h
);
402 void ob_main_loop_x_remove(ObMainLoop
*loop
,
403 ObMainLoopXHandler handler
)
407 for (it
= loop
->x_handlers
; it
; it
= next
) {
408 ObMainLoopXHandlerType
*h
= it
->data
;
409 next
= g_slist_next(it
);
410 if (h
->func
== handler
) {
411 loop
->x_handlers
= g_slist_delete_link(loop
->x_handlers
, it
);
412 if (h
->destroy
) h
->destroy(h
->data
);
418 /*** SIGNAL WATCHERS ***/
420 static void sighandler(gint sig
)
425 g_return_if_fail(sig
< NUM_SIGNALS
);
427 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
)
428 if (sig
== core_signals
[i
]) {
429 /* XXX special case for signals that default to core dump.
430 but throw some helpful output here... */
432 fprintf(stderr
, "Fuck yah. Core dump. (Signal=%d)\n", sig
);
434 /* die with a core dump */
438 for (it
= all_loops
; it
; it
= g_slist_next(it
)) {
439 ObMainLoop
*loop
= it
->data
;
440 loop
->signal_fired
= TRUE
;
441 loop
->signals_fired
[sig
]++;
445 void ob_main_loop_signal_add(ObMainLoop
*loop
,
447 ObMainLoopSignalHandler handler
,
449 GDestroyNotify notify
)
451 ObMainLoopSignalHandlerType
*h
;
453 g_return_if_fail(signal
< NUM_SIGNALS
);
455 h
= g_new(ObMainLoopSignalHandlerType
, 1);
461 loop
->signal_handlers
[h
->signal
] =
462 g_slist_prepend(loop
->signal_handlers
[h
->signal
], h
);
464 if (!all_signals
[signal
].installed
) {
465 struct sigaction action
;
468 sigemptyset(&sigset
);
469 action
.sa_handler
= sighandler
;
470 action
.sa_mask
= sigset
;
471 action
.sa_flags
= SA_NOCLDSTOP
;
473 sigaction(signal
, &action
, &all_signals
[signal
].oldact
);
476 all_signals
[signal
].installed
++;
479 void ob_main_loop_signal_remove(ObMainLoop
*loop
,
480 ObMainLoopSignalHandler handler
)
485 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
486 for (it
= loop
->signal_handlers
[i
]; it
; it
= next
) {
487 ObMainLoopSignalHandlerType
*h
= it
->data
;
489 next
= g_slist_next(it
);
491 if (h
->func
== handler
) {
492 g_assert(all_signals
[h
->signal
].installed
> 0);
494 all_signals
[h
->signal
].installed
--;
495 if (!all_signals
[h
->signal
].installed
) {
496 sigaction(h
->signal
, &all_signals
[h
->signal
].oldact
, NULL
);
499 loop
->signal_handlers
[i
] =
500 g_slist_delete_link(loop
->signal_handlers
[i
], it
);
501 if (h
->destroy
) h
->destroy(h
->data
);
510 /*** FILE DESCRIPTOR WATCHERS ***/
512 static void max_fd_func(gpointer key
, gpointer value
, gpointer data
)
514 ObMainLoop
*loop
= data
;
517 loop
->fd_max
= MAX(loop
->fd_max
, *(gint
*)key
);
520 static void calc_max_fd(ObMainLoop
*loop
)
522 loop
->fd_max
= loop
->fd_x
;
524 g_hash_table_foreach(loop
->fd_handlers
, max_fd_func
, loop
);
527 void ob_main_loop_fd_add(ObMainLoop
*loop
,
529 ObMainLoopFdHandler handler
,
531 GDestroyNotify notify
)
533 ObMainLoopFdHandlerType
*h
;
535 h
= g_new(ObMainLoopFdHandlerType
, 1);
542 g_hash_table_replace(loop
->fd_handlers
, &h
->fd
, h
);
543 FD_SET(h
->fd
, &loop
->fd_set
);
547 static void fd_handler_destroy(gpointer data
)
549 ObMainLoopFdHandlerType
*h
= data
;
551 FD_CLR(h
->fd
, &h
->loop
->fd_set
);
557 void ob_main_loop_fd_remove(ObMainLoop
*loop
,
560 g_hash_table_remove(loop
->fd_handlers
, &fd
);
565 #define NEAREST_TIMEOUT(loop) \
566 (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
568 static glong
timecompare(GTimeVal
*a
, GTimeVal
*b
)
572 if ((r
= b
->tv_sec
- a
->tv_sec
)) return r
;
573 return b
->tv_usec
- a
->tv_usec
;
577 static void insert_timer(ObMainLoop
*loop
, ObMainLoopTimer
*ins
)
580 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
581 ObMainLoopTimer
*t
= it
->data
;
582 if (timecompare(&ins
->timeout
, &t
->timeout
) >= 0) {
583 loop
->timers
= g_slist_insert_before(loop
->timers
, it
, ins
);
587 if (it
== NULL
) /* didnt fit anywhere in the list */
588 loop
->timers
= g_slist_append(loop
->timers
, ins
);
591 void ob_main_loop_timeout_add(ObMainLoop
*loop
,
595 GDestroyNotify notify
)
597 ObMainLoopTimer
*t
= g_new(ObMainLoopTimer
, 1);
598 t
->delay
= microseconds
;
603 g_get_current_time(&loop
->now
);
604 t
->last
= t
->timeout
= loop
->now
;
605 g_time_val_add(&t
->timeout
, t
->delay
);
607 insert_timer(loop
, t
);
610 void ob_main_loop_timeout_remove(ObMainLoop
*loop
,
615 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
616 ObMainLoopTimer
*t
= it
->data
;
617 if (t
->func
== handler
)
622 void ob_main_loop_timeout_remove_data(ObMainLoop
*loop
,
628 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
629 ObMainLoopTimer
*t
= it
->data
;
630 if (t
->func
== handler
&& t
->data
== data
)
635 /* find the time to wait for the nearest timeout */
636 static gboolean
nearest_timeout_wait(ObMainLoop
*loop
, GTimeVal
*tm
)
638 if (loop
->timers
== NULL
)
641 tm
->tv_sec
= NEAREST_TIMEOUT(loop
).tv_sec
- loop
->now
.tv_sec
;
642 tm
->tv_usec
= NEAREST_TIMEOUT(loop
).tv_usec
- loop
->now
.tv_usec
;
644 while (tm
->tv_usec
< 0) {
645 tm
->tv_usec
+= G_USEC_PER_SEC
;
648 tm
->tv_sec
+= tm
->tv_usec
/ G_USEC_PER_SEC
;
649 tm
->tv_usec
%= G_USEC_PER_SEC
;
656 static void timer_dispatch(ObMainLoop
*loop
, GTimeVal
**wait
)
660 gboolean fired
= FALSE
;
662 g_get_current_time(&loop
->now
);
664 for (it
= loop
->timers
; it
; it
= next
) {
665 ObMainLoopTimer
*curr
;
667 next
= g_slist_next(it
);
671 /* since timer_stop doesn't actually free the timer, we have to do our
672 real freeing in here.
676 loop
->timers
= g_slist_delete_link(loop
->timers
, it
);
678 curr
->destroy(curr
->data
);
683 /* the queue is sorted, so if this timer shouldn't fire, none are
685 if (timecompare(&NEAREST_TIMEOUT(loop
), &loop
->now
) < 0)
688 /* we set the last fired time to delay msec after the previous firing,
689 then re-insert. timers maintain their order and may trigger more
690 than once if they've waited more than one delay's worth of time.
692 loop
->timers
= g_slist_delete_link(loop
->timers
, it
);
693 g_time_val_add(&curr
->last
, curr
->delay
);
694 if (curr
->func(curr
->data
)) {
695 g_time_val_add(&curr
->timeout
, curr
->delay
);
696 insert_timer(loop
, curr
);
699 curr
->destroy(curr
->data
);
707 /* if at least one timer fires, then don't wait on X events, as there
708 may already be some in the queue from the timer callbacks.
710 loop
->ret_wait
.tv_sec
= loop
->ret_wait
.tv_usec
= 0;
711 *wait
= &loop
->ret_wait
;
712 } else if (nearest_timeout_wait(loop
, &loop
->ret_wait
))
713 *wait
= &loop
->ret_wait
;