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.
24 #include <sys/select.h>
27 typedef struct _ObMainLoopTimer ObMainLoopTimer
;
28 typedef struct _ObMainLoopSignal ObMainLoopSignal
;
29 typedef struct _ObMainLoopSignalHandlerType ObMainLoopSignalHandlerType
;
30 typedef struct _ObMainLoopXHandlerType ObMainLoopXHandlerType
;
31 typedef struct _ObMainLoopFdHandlerType ObMainLoopFdHandlerType
;
33 /* this should be more than the number of possible signals on any
35 #define NUM_SIGNALS 99
37 /* all created ObMainLoops. Used by the signal handler to pass along signals */
38 static GSList
*all_loops
;
40 /* signals are global to all loops */
42 guint installed
; /* a ref count */
43 struct sigaction oldact
;
44 } all_signals
[NUM_SIGNALS
];
46 /* a set of all possible signals */
47 sigset_t all_signals_set
;
49 /* signals which cause a core dump, these can't be used for callbacks */
50 static gint core_signals
[] =
63 #define NUM_CORE_SIGNALS (sizeof(core_signals) / sizeof(core_signals[0]))
65 static void sighandler(gint sig
);
66 static void timer_dispatch(ObMainLoop
*loop
, GTimeVal
**wait
);
67 static void fd_handler_destroy(gpointer data
);
73 gboolean run
; /* do keep running */
74 gboolean running
; /* is still running */
78 gint fd_x
; /* The X fd is a special case! */
80 GHashTable
*fd_handlers
;
87 gboolean signal_fired
;
88 guint signals_fired
[NUM_SIGNALS
];
89 GSList
*signal_handlers
[NUM_SIGNALS
];
94 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
);
181 loop
->action_queue
= g_queue_new();
186 void ob_main_loop_destroy(ObMainLoop
*loop
)
192 g_assert(loop
->running
== FALSE
);
194 for (it
= loop
->x_handlers
; it
; it
= next
) {
195 ObMainLoopXHandlerType
*h
= it
->data
;
196 next
= g_slist_next(it
);
197 ob_main_loop_x_remove(loop
, h
->func
);
200 g_hash_table_destroy(loop
->fd_handlers
);
202 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
203 ObMainLoopTimer
*t
= it
->data
;
204 if (t
->destroy
) t
->destroy(t
->data
);
207 g_slist_free(loop
->timers
);
210 for (i
= 0; i
< NUM_SIGNALS
; ++i
)
211 for (it
= loop
->signal_handlers
[i
]; it
; it
= next
) {
212 ObMainLoopSignalHandlerType
*h
= it
->data
;
213 next
= g_slist_next(it
);
214 ob_main_loop_signal_remove(loop
, h
->func
);
217 all_loops
= g_slist_remove(all_loops
, loop
);
219 /* only do this if we're the last loop destroyed */
223 /* grab all the signals that cause core dumps */
224 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
) {
225 if (all_signals
[core_signals
[i
]].installed
) {
226 sigaction(core_signals
[i
],
227 &all_signals
[core_signals
[i
]].oldact
, NULL
);
228 all_signals
[core_signals
[i
]].installed
--;
233 g_queue_free(loop
->action_queue
);
239 static void fd_handle_foreach(gpointer key
,
243 ObMainLoopFdHandlerType
*h
= value
;
246 if (FD_ISSET(h
->fd
, set
))
247 h
->func(h
->fd
, h
->data
);
250 void ob_main_loop_queue_action(ObMainLoop
*loop
, ObAction
*act
)
252 g_queue_push_tail(loop
->action_queue
, action_copy(act
));
255 void ob_main_loop_run(ObMainLoop
*loop
)
258 struct timeval
*wait
;
264 loop
->running
= TRUE
;
267 if (loop
->signal_fired
) {
271 /* block signals so that we can do this without the data changing
273 sigprocmask(SIG_SETMASK
, &all_signals_set
, &oldset
);
275 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
276 while (loop
->signals_fired
[i
]) {
277 for (it
= loop
->signal_handlers
[i
];
278 it
; it
= g_slist_next(it
)) {
279 ObMainLoopSignalHandlerType
*h
= it
->data
;
282 loop
->signals_fired
[i
]--;
285 loop
->signal_fired
= FALSE
;
287 sigprocmask(SIG_SETMASK
, &oldset
, NULL
);
288 } else if (XPending(loop
->display
)) {
290 XNextEvent(loop
->display
, &e
);
292 for (it
= loop
->x_handlers
; it
; it
= g_slist_next(it
)) {
293 ObMainLoopXHandlerType
*h
= it
->data
;
294 h
->func(&e
, h
->data
);
296 } while (XPending(loop
->display
));
297 } else if ((act
= g_queue_pop_head(loop
->action_queue
))) {
298 /* only fire off one action at a time, then go back for more
299 X events, since the action might cause some X events (like
301 act
->func(&act
->data
);
304 /* this only runs if there were no x events received */
306 timer_dispatch(loop
, (GTimeVal
**)&wait
);
308 selset
= loop
->fd_set
;
309 /* there is a small race condition here. if a signal occurs
310 between this if() and the select() then we will not process
311 the signal until 'wait' expires. possible solutions include
312 using GStaticMutex, and having the signal handler set 'wait'
314 if (!loop
->signal_fired
)
315 select(loop
->fd_max
+ 1, &selset
, NULL
, NULL
, wait
);
317 /* handle the X events with highest prioirity */
318 if (FD_ISSET(loop
->fd_x
, &selset
))
321 g_hash_table_foreach(loop
->fd_handlers
,
322 fd_handle_foreach
, &selset
);
326 loop
->running
= FALSE
;
329 void ob_main_loop_exit(ObMainLoop
*loop
)
334 /*** XEVENT WATCHERS ***/
336 void ob_main_loop_x_add(ObMainLoop
*loop
,
337 ObMainLoopXHandler handler
,
339 GDestroyNotify notify
)
341 ObMainLoopXHandlerType
*h
;
343 h
= g_new(ObMainLoopXHandlerType
, 1);
348 loop
->x_handlers
= g_slist_prepend(loop
->x_handlers
, h
);
351 void ob_main_loop_x_remove(ObMainLoop
*loop
,
352 ObMainLoopXHandler handler
)
356 for (it
= loop
->x_handlers
; it
; it
= next
) {
357 ObMainLoopXHandlerType
*h
= it
->data
;
358 next
= g_slist_next(it
);
359 if (h
->func
== handler
) {
360 loop
->x_handlers
= g_slist_delete_link(loop
->x_handlers
, it
);
361 if (h
->destroy
) h
->destroy(h
->data
);
367 /*** SIGNAL WATCHERS ***/
369 static void sighandler(gint sig
)
374 g_return_if_fail(sig
< NUM_SIGNALS
);
376 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
)
377 if (sig
== core_signals
[i
]) {
378 /* XXX special case for signals that default to core dump.
379 but throw some helpful output here... */
381 fprintf(stderr
, "Fuck yah. Core dump. (Signal=%d)\n", sig
);
383 /* die with a core dump */
387 for (it
= all_loops
; it
; it
= g_slist_next(it
)) {
388 ObMainLoop
*loop
= it
->data
;
389 loop
->signal_fired
= TRUE
;
390 loop
->signals_fired
[sig
]++;
394 void ob_main_loop_signal_add(ObMainLoop
*loop
,
396 ObMainLoopSignalHandler handler
,
398 GDestroyNotify notify
)
400 ObMainLoopSignalHandlerType
*h
;
402 g_return_if_fail(signal
< NUM_SIGNALS
);
404 h
= g_new(ObMainLoopSignalHandlerType
, 1);
410 loop
->signal_handlers
[h
->signal
] =
411 g_slist_prepend(loop
->signal_handlers
[h
->signal
], h
);
413 if (!all_signals
[signal
].installed
) {
414 struct sigaction action
;
417 sigemptyset(&sigset
);
418 action
.sa_handler
= sighandler
;
419 action
.sa_mask
= sigset
;
420 action
.sa_flags
= SA_NOCLDSTOP
;
422 sigaction(signal
, &action
, &all_signals
[signal
].oldact
);
425 all_signals
[signal
].installed
++;
428 void ob_main_loop_signal_remove(ObMainLoop
*loop
,
429 ObMainLoopSignalHandler handler
)
434 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
435 for (it
= loop
->signal_handlers
[i
]; it
; it
= next
) {
436 ObMainLoopSignalHandlerType
*h
= it
->data
;
438 next
= g_slist_next(it
);
440 if (h
->func
== handler
) {
441 g_assert(all_signals
[h
->signal
].installed
> 0);
443 all_signals
[h
->signal
].installed
--;
444 if (!all_signals
[h
->signal
].installed
) {
445 sigaction(h
->signal
, &all_signals
[h
->signal
].oldact
, NULL
);
448 loop
->signal_handlers
[i
] =
449 g_slist_delete_link(loop
->signal_handlers
[i
], it
);
450 if (h
->destroy
) h
->destroy(h
->data
);
459 /*** FILE DESCRIPTOR WATCHERS ***/
461 static void max_fd_func(gpointer key
, gpointer value
, gpointer data
)
463 ObMainLoop
*loop
= data
;
466 loop
->fd_max
= MAX(loop
->fd_max
, *(gint
*)key
);
469 static void calc_max_fd(ObMainLoop
*loop
)
471 loop
->fd_max
= loop
->fd_x
;
473 g_hash_table_foreach(loop
->fd_handlers
, max_fd_func
, loop
);
476 void ob_main_loop_fd_add(ObMainLoop
*loop
,
478 ObMainLoopFdHandler handler
,
480 GDestroyNotify notify
)
482 ObMainLoopFdHandlerType
*h
;
484 h
= g_new(ObMainLoopFdHandlerType
, 1);
491 g_hash_table_replace(loop
->fd_handlers
, &h
->fd
, h
);
492 FD_SET(h
->fd
, &loop
->fd_set
);
496 static void fd_handler_destroy(gpointer data
)
498 ObMainLoopFdHandlerType
*h
= data
;
500 FD_CLR(h
->fd
, &h
->loop
->fd_set
);
506 void ob_main_loop_fd_remove(ObMainLoop
*loop
,
509 g_hash_table_remove(loop
->fd_handlers
, &fd
);
514 #define NEAREST_TIMEOUT(loop) \
515 (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
517 static long timecompare(GTimeVal
*a
, GTimeVal
*b
)
521 if ((r
= b
->tv_sec
- a
->tv_sec
)) return r
;
522 return b
->tv_usec
- a
->tv_usec
;
526 static void insert_timer(ObMainLoop
*loop
, ObMainLoopTimer
*ins
)
529 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
530 ObMainLoopTimer
*t
= it
->data
;
531 if (timecompare(&ins
->timeout
, &t
->timeout
) >= 0) {
532 loop
->timers
= g_slist_insert_before(loop
->timers
, it
, ins
);
536 if (it
== NULL
) /* didnt fit anywhere in the list */
537 loop
->timers
= g_slist_append(loop
->timers
, ins
);
540 void ob_main_loop_timeout_add(ObMainLoop
*loop
,
544 GDestroyNotify notify
)
546 ObMainLoopTimer
*t
= g_new(ObMainLoopTimer
, 1);
547 t
->delay
= microseconds
;
552 g_get_current_time(&loop
->now
);
553 t
->last
= t
->timeout
= loop
->now
;
554 g_time_val_add(&t
->timeout
, t
->delay
);
556 insert_timer(loop
, t
);
559 void ob_main_loop_timeout_remove(ObMainLoop
*loop
,
564 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
565 ObMainLoopTimer
*t
= it
->data
;
566 if (t
->func
== handler
)
571 void ob_main_loop_timeout_remove_data(ObMainLoop
*loop
,
577 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
578 ObMainLoopTimer
*t
= it
->data
;
579 if (t
->func
== handler
&& t
->data
== data
)
584 /* find the time to wait for the nearest timeout */
585 static gboolean
nearest_timeout_wait(ObMainLoop
*loop
, GTimeVal
*tm
)
587 if (loop
->timers
== NULL
)
590 tm
->tv_sec
= NEAREST_TIMEOUT(loop
).tv_sec
- loop
->now
.tv_sec
;
591 tm
->tv_usec
= NEAREST_TIMEOUT(loop
).tv_usec
- loop
->now
.tv_usec
;
593 while (tm
->tv_usec
< 0) {
594 tm
->tv_usec
+= G_USEC_PER_SEC
;
597 tm
->tv_sec
+= tm
->tv_usec
/ G_USEC_PER_SEC
;
598 tm
->tv_usec
%= G_USEC_PER_SEC
;
605 static void timer_dispatch(ObMainLoop
*loop
, GTimeVal
**wait
)
609 gboolean fired
= FALSE
;
611 g_get_current_time(&loop
->now
);
613 for (it
= loop
->timers
; it
; it
= next
) {
614 ObMainLoopTimer
*curr
;
616 next
= g_slist_next(it
);
620 /* since timer_stop doesn't actually free the timer, we have to do our
621 real freeing in here.
625 loop
->timers
= g_slist_delete_link(loop
->timers
, it
);
630 /* the queue is sorted, so if this timer shouldn't fire, none are
632 if (timecompare(&NEAREST_TIMEOUT(loop
), &loop
->now
) < 0)
635 /* we set the last fired time to delay msec after the previous firing,
636 then re-insert. timers maintain their order and may trigger more
637 than once if they've waited more than one delay's worth of time.
639 loop
->timers
= g_slist_delete_link(loop
->timers
, it
);
640 g_time_val_add(&curr
->last
, curr
->delay
);
641 if (curr
->func(curr
->data
)) {
642 g_time_val_add(&curr
->timeout
, curr
->delay
);
643 insert_timer(loop
, curr
);
646 curr
->destroy(curr
->data
);
654 /* if at least one timer fires, then don't wait on X events, as there
655 may already be some in the queue from the timer callbacks.
657 loop
->ret_wait
.tv_sec
= loop
->ret_wait
.tv_usec
= 0;
658 *wait
= &loop
->ret_wait
;
659 } else if (nearest_timeout_wait(loop
, &loop
->ret_wait
))
660 *wait
= &loop
->ret_wait
;