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.
23 #include <sys/select.h>
26 typedef struct _ObMainLoopTimer ObMainLoopTimer
;
27 typedef struct _ObMainLoopSignal ObMainLoopSignal
;
28 typedef struct _ObMainLoopSignalHandlerType ObMainLoopSignalHandlerType
;
29 typedef struct _ObMainLoopXHandlerType ObMainLoopXHandlerType
;
30 typedef struct _ObMainLoopFdHandlerType ObMainLoopFdHandlerType
;
32 /* this should be more than the number of possible signals on any
34 #define NUM_SIGNALS 99
36 /* all created ObMainLoops. Used by the signal handler to pass along signals */
37 static GSList
*all_loops
;
39 /* signals are global to all loops */
41 guint installed
; /* a ref count */
42 struct sigaction oldact
;
43 } all_signals
[NUM_SIGNALS
];
45 /* a set of all possible signals */
46 sigset_t all_signals_set
;
48 /* signals which cause a core dump, these can't be used for callbacks */
49 static gint core_signals
[] =
62 #define NUM_CORE_SIGNALS (sizeof(core_signals) / sizeof(core_signals[0]))
64 static void sighandler(gint sig
);
65 static void timer_dispatch(ObMainLoop
*loop
, GTimeVal
**wait
);
66 static void fd_handler_destroy(gpointer data
);
72 gboolean run
; /* do keep running */
73 gboolean running
; /* is still running */
77 gint fd_x
; /* The X fd is a special case! */
79 GHashTable
*fd_handlers
;
86 gboolean signal_fired
;
87 guint signals_fired
[NUM_SIGNALS
];
88 GSList
*signal_handlers
[NUM_SIGNALS
];
91 struct _ObMainLoopTimer
96 GDestroyNotify destroy
;
98 /* The timer needs to be freed */
100 /* The time the last fire should've been at */
102 /* When this timer will next trigger */
106 struct _ObMainLoopSignalHandlerType
111 ObMainLoopSignalHandler func
;
112 GDestroyNotify destroy
;
115 struct _ObMainLoopXHandlerType
119 ObMainLoopXHandler func
;
120 GDestroyNotify destroy
;
123 struct _ObMainLoopFdHandlerType
128 ObMainLoopFdHandler func
;
129 GDestroyNotify destroy
;
132 ObMainLoop
*ob_main_loop_new(Display
*display
)
136 loop
= g_new0(ObMainLoop
, 1);
137 loop
->display
= display
;
138 loop
->fd_x
= ConnectionNumber(display
);
139 FD_ZERO(&loop
->fd_set
);
140 FD_SET(loop
->fd_x
, &loop
->fd_set
);
141 loop
->fd_max
= loop
->fd_x
;
143 loop
->fd_handlers
= g_hash_table_new_full(g_int_hash
, g_int_equal
,
144 NULL
, fd_handler_destroy
);
146 g_get_current_time(&loop
->now
);
148 /* only do this if we're the first loop created */
151 struct sigaction action
;
154 /* initialize the all_signals_set */
155 sigfillset(&all_signals_set
);
157 sigemptyset(&sigset
);
158 action
.sa_handler
= sighandler
;
159 action
.sa_mask
= sigset
;
160 action
.sa_flags
= SA_NOCLDSTOP
;
162 /* grab all the signals that cause core dumps */
163 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
) {
164 /* SIGABRT is curiously not grabbed here!! that's because when we
165 get one of the core_signals, we use abort() to dump the core.
166 And having the abort() only go back to our signal handler again
167 is less than optimal */
168 if (core_signals
[i
] != SIGABRT
) {
169 sigaction(core_signals
[i
], &action
,
170 &all_signals
[core_signals
[i
]].oldact
);
171 all_signals
[core_signals
[i
]].installed
++;
176 all_loops
= g_slist_prepend(all_loops
, loop
);
181 void ob_main_loop_destroy(ObMainLoop
*loop
)
187 g_assert(loop
->running
== FALSE
);
189 for (it
= loop
->x_handlers
; it
; it
= next
) {
190 ObMainLoopXHandlerType
*h
= it
->data
;
191 next
= g_slist_next(it
);
192 ob_main_loop_x_remove(loop
, h
->func
);
195 g_hash_table_destroy(loop
->fd_handlers
);
197 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
198 ObMainLoopTimer
*t
= it
->data
;
199 if (t
->destroy
) t
->destroy(t
->data
);
202 g_slist_free(loop
->timers
);
205 for (i
= 0; i
< NUM_SIGNALS
; ++i
)
206 for (it
= loop
->signal_handlers
[i
]; it
; it
= next
) {
207 ObMainLoopSignalHandlerType
*h
= it
->data
;
208 next
= g_slist_next(it
);
209 ob_main_loop_signal_remove(loop
, h
->func
);
212 all_loops
= g_slist_remove(all_loops
, loop
);
214 /* only do this if we're the last loop destroyed */
218 /* grab all the signals that cause core dumps */
219 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
) {
220 if (all_signals
[core_signals
[i
]].installed
) {
221 sigaction(core_signals
[i
],
222 &all_signals
[core_signals
[i
]].oldact
, NULL
);
223 all_signals
[core_signals
[i
]].installed
--;
232 static void fd_handle_foreach(gpointer key
,
236 ObMainLoopFdHandlerType
*h
= value
;
239 if (FD_ISSET(h
->fd
, set
))
240 h
->func(h
->fd
, h
->data
);
243 void ob_main_loop_run(ObMainLoop
*loop
)
246 struct timeval
*wait
;
251 loop
->running
= TRUE
;
254 if (loop
->signal_fired
) {
258 /* block signals so that we can do this without the data changing
260 sigprocmask(SIG_SETMASK
, &all_signals_set
, &oldset
);
262 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
263 while (loop
->signals_fired
[i
]) {
264 for (it
= loop
->signal_handlers
[i
];
265 it
; it
= g_slist_next(it
)) {
266 ObMainLoopSignalHandlerType
*h
= it
->data
;
269 loop
->signals_fired
[i
]--;
272 loop
->signal_fired
= FALSE
;
274 sigprocmask(SIG_SETMASK
, &oldset
, NULL
);
275 } else if (XPending(loop
->display
)) {
277 XNextEvent(loop
->display
, &e
);
279 for (it
= loop
->x_handlers
; it
; it
= g_slist_next(it
)) {
280 ObMainLoopXHandlerType
*h
= it
->data
;
281 h
->func(&e
, h
->data
);
283 } while (XPending(loop
->display
));
285 /* this only runs if there were no x events received */
287 timer_dispatch(loop
, (GTimeVal
**)&wait
);
288 selset
= loop
->fd_set
;
289 /* there is a small race condition here. if a signal occurs
290 between this if() and the select() then we will not process
291 the signal until 'wait' expires. possible solutions include
292 using GStaticMutex, and having the signal handler set 'wait'
294 if (!loop
->signal_fired
)
295 select(loop
->fd_max
+ 1, &selset
, NULL
, NULL
, wait
);
297 /* handle the X events with highest prioirity */
298 if (FD_ISSET(loop
->fd_x
, &selset
))
301 g_hash_table_foreach(loop
->fd_handlers
,
302 fd_handle_foreach
, &selset
);
306 loop
->running
= FALSE
;
309 void ob_main_loop_exit(ObMainLoop
*loop
)
314 /*** XEVENT WATCHERS ***/
316 void ob_main_loop_x_add(ObMainLoop
*loop
,
317 ObMainLoopXHandler handler
,
319 GDestroyNotify notify
)
321 ObMainLoopXHandlerType
*h
;
323 h
= g_new(ObMainLoopXHandlerType
, 1);
328 loop
->x_handlers
= g_slist_prepend(loop
->x_handlers
, h
);
331 void ob_main_loop_x_remove(ObMainLoop
*loop
,
332 ObMainLoopXHandler handler
)
336 for (it
= loop
->x_handlers
; it
; it
= next
) {
337 ObMainLoopXHandlerType
*h
= it
->data
;
338 next
= g_slist_next(it
);
339 if (h
->func
== handler
) {
340 loop
->x_handlers
= g_slist_delete_link(loop
->x_handlers
, it
);
341 if (h
->destroy
) h
->destroy(h
->data
);
347 /*** SIGNAL WATCHERS ***/
349 static void sighandler(gint sig
)
354 g_return_if_fail(sig
< NUM_SIGNALS
);
356 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
)
357 if (sig
== core_signals
[i
]) {
358 /* XXX special case for signals that default to core dump.
359 but throw some helpful output here... */
361 fprintf(stderr
, "Fuck yah. Core dump. (Signal=%d)\n", sig
);
363 /* die with a core dump */
367 for (it
= all_loops
; it
; it
= g_slist_next(it
)) {
368 ObMainLoop
*loop
= it
->data
;
369 loop
->signal_fired
= TRUE
;
370 loop
->signals_fired
[sig
]++;
374 void ob_main_loop_signal_add(ObMainLoop
*loop
,
376 ObMainLoopSignalHandler handler
,
378 GDestroyNotify notify
)
380 ObMainLoopSignalHandlerType
*h
;
382 g_return_if_fail(signal
< NUM_SIGNALS
);
384 h
= g_new(ObMainLoopSignalHandlerType
, 1);
390 loop
->signal_handlers
[h
->signal
] =
391 g_slist_prepend(loop
->signal_handlers
[h
->signal
], h
);
393 if (!all_signals
[signal
].installed
) {
394 struct sigaction action
;
397 sigemptyset(&sigset
);
398 action
.sa_handler
= sighandler
;
399 action
.sa_mask
= sigset
;
400 action
.sa_flags
= SA_NOCLDSTOP
;
402 sigaction(signal
, &action
, &all_signals
[signal
].oldact
);
405 all_signals
[signal
].installed
++;
408 void ob_main_loop_signal_remove(ObMainLoop
*loop
,
409 ObMainLoopSignalHandler handler
)
414 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
415 for (it
= loop
->signal_handlers
[i
]; it
; it
= next
) {
416 ObMainLoopSignalHandlerType
*h
= it
->data
;
418 next
= g_slist_next(it
);
420 if (h
->func
== handler
) {
421 g_assert(all_signals
[h
->signal
].installed
> 0);
423 all_signals
[h
->signal
].installed
--;
424 if (!all_signals
[h
->signal
].installed
) {
425 sigaction(h
->signal
, &all_signals
[h
->signal
].oldact
, NULL
);
428 loop
->signal_handlers
[i
] =
429 g_slist_delete_link(loop
->signal_handlers
[i
], it
);
430 if (h
->destroy
) h
->destroy(h
->data
);
439 /*** FILE DESCRIPTOR WATCHERS ***/
441 static void max_fd_func(gpointer key
, gpointer value
, gpointer data
)
443 ObMainLoop
*loop
= data
;
446 loop
->fd_max
= MAX(loop
->fd_max
, *(gint
*)key
);
449 static void calc_max_fd(ObMainLoop
*loop
)
451 loop
->fd_max
= loop
->fd_x
;
453 g_hash_table_foreach(loop
->fd_handlers
, max_fd_func
, loop
);
456 void ob_main_loop_fd_add(ObMainLoop
*loop
,
458 ObMainLoopFdHandler handler
,
460 GDestroyNotify notify
)
462 ObMainLoopFdHandlerType
*h
;
464 h
= g_new(ObMainLoopFdHandlerType
, 1);
471 g_hash_table_replace(loop
->fd_handlers
, &h
->fd
, h
);
472 FD_SET(h
->fd
, &loop
->fd_set
);
476 static void fd_handler_destroy(gpointer data
)
478 ObMainLoopFdHandlerType
*h
= data
;
480 FD_CLR(h
->fd
, &h
->loop
->fd_set
);
486 void ob_main_loop_fd_remove(ObMainLoop
*loop
,
489 g_hash_table_remove(loop
->fd_handlers
, &fd
);
494 #define NEAREST_TIMEOUT(loop) \
495 (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
497 static long timecompare(GTimeVal
*a
, GTimeVal
*b
)
501 if ((r
= b
->tv_sec
- a
->tv_sec
)) return r
;
502 return b
->tv_usec
- a
->tv_usec
;
506 static void insert_timer(ObMainLoop
*loop
, ObMainLoopTimer
*ins
)
509 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
510 ObMainLoopTimer
*t
= it
->data
;
511 if (timecompare(&ins
->timeout
, &t
->timeout
) >= 0) {
512 loop
->timers
= g_slist_insert_before(loop
->timers
, it
, ins
);
516 if (it
== NULL
) /* didnt fit anywhere in the list */
517 loop
->timers
= g_slist_append(loop
->timers
, ins
);
520 void ob_main_loop_timeout_add(ObMainLoop
*loop
,
524 GDestroyNotify notify
)
526 ObMainLoopTimer
*t
= g_new(ObMainLoopTimer
, 1);
527 t
->delay
= microseconds
;
532 g_get_current_time(&loop
->now
);
533 t
->last
= t
->timeout
= loop
->now
;
534 g_time_val_add(&t
->timeout
, t
->delay
);
536 insert_timer(loop
, t
);
539 void ob_main_loop_timeout_remove(ObMainLoop
*loop
,
544 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
545 ObMainLoopTimer
*t
= it
->data
;
546 if (t
->func
== handler
)
551 void ob_main_loop_timeout_remove_data(ObMainLoop
*loop
,
557 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
558 ObMainLoopTimer
*t
= it
->data
;
559 if (t
->func
== handler
&& t
->data
== data
)
564 /* find the time to wait for the nearest timeout */
565 static gboolean
nearest_timeout_wait(ObMainLoop
*loop
, GTimeVal
*tm
)
567 if (loop
->timers
== NULL
)
570 tm
->tv_sec
= NEAREST_TIMEOUT(loop
).tv_sec
- loop
->now
.tv_sec
;
571 tm
->tv_usec
= NEAREST_TIMEOUT(loop
).tv_usec
- loop
->now
.tv_usec
;
573 while (tm
->tv_usec
< 0) {
574 tm
->tv_usec
+= G_USEC_PER_SEC
;
577 tm
->tv_sec
+= tm
->tv_usec
/ G_USEC_PER_SEC
;
578 tm
->tv_usec
%= G_USEC_PER_SEC
;
585 static void timer_dispatch(ObMainLoop
*loop
, GTimeVal
**wait
)
589 gboolean fired
= FALSE
;
591 g_get_current_time(&loop
->now
);
593 for (it
= loop
->timers
; it
; it
= next
) {
594 ObMainLoopTimer
*curr
;
596 next
= g_slist_next(it
);
600 /* since timer_stop doesn't actually free the timer, we have to do our
601 real freeing in here.
605 loop
->timers
= g_slist_delete_link(loop
->timers
, it
);
610 /* the queue is sorted, so if this timer shouldn't fire, none are
612 if (timecompare(&NEAREST_TIMEOUT(loop
), &loop
->now
) < 0)
615 /* we set the last fired time to delay msec after the previous firing,
616 then re-insert. timers maintain their order and may trigger more
617 than once if they've waited more than one delay's worth of time.
619 loop
->timers
= g_slist_delete_link(loop
->timers
, it
);
620 g_time_val_add(&curr
->last
, curr
->delay
);
621 if (curr
->func(curr
->data
)) {
622 g_time_val_add(&curr
->timeout
, curr
->delay
);
623 insert_timer(loop
, curr
);
626 curr
->destroy(curr
->data
);
634 /* if at least one timer fires, then don't wait on X events, as there
635 may already be some in the queue from the timer callbacks.
637 loop
->ret_wait
.tv_sec
= loop
->ret_wait
.tv_usec
= 0;
638 *wait
= &loop
->ret_wait
;
639 } else if (nearest_timeout_wait(loop
, &loop
->ret_wait
))
640 *wait
= &loop
->ret_wait
;