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 GDestroyNotify destroy
;
127 struct _ObMainLoopFdHandlerType
132 ObMainLoopFdHandler func
;
133 GDestroyNotify destroy
;
136 ObMainLoop
*ob_main_loop_new(Display
*display
)
140 loop
= g_new0(ObMainLoop
, 1);
141 loop
->display
= display
;
142 loop
->fd_x
= ConnectionNumber(display
);
143 FD_ZERO(&loop
->fd_set
);
144 FD_SET(loop
->fd_x
, &loop
->fd_set
);
145 loop
->fd_max
= loop
->fd_x
;
147 loop
->fd_handlers
= g_hash_table_new_full(g_int_hash
, g_int_equal
,
148 NULL
, fd_handler_destroy
);
150 g_get_current_time(&loop
->now
);
152 /* only do this if we're the first loop created */
155 struct sigaction action
;
158 /* initialize the all_signals_set */
159 sigfillset(&all_signals_set
);
161 sigemptyset(&sigset
);
162 action
.sa_handler
= sighandler
;
163 action
.sa_mask
= sigset
;
164 action
.sa_flags
= SA_NOCLDSTOP
;
166 /* grab all the signals that cause core dumps */
167 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
) {
168 /* SIGABRT is curiously not grabbed here!! that's because when we
169 get one of the core_signals, we use abort() to dump the core.
170 And having the abort() only go back to our signal handler again
171 is less than optimal */
172 if (core_signals
[i
] != SIGABRT
) {
173 sigaction(core_signals
[i
], &action
,
174 &all_signals
[core_signals
[i
]].oldact
);
175 all_signals
[core_signals
[i
]].installed
++;
180 all_loops
= g_slist_prepend(all_loops
, loop
);
182 loop
->action_queue
= NULL
;
187 void ob_main_loop_destroy(ObMainLoop
*loop
)
193 g_assert(loop
->running
== FALSE
);
195 for (it
= loop
->x_handlers
; it
; it
= next
) {
196 ObMainLoopXHandlerType
*h
= it
->data
;
197 next
= g_slist_next(it
);
198 ob_main_loop_x_remove(loop
, h
->func
);
201 g_hash_table_destroy(loop
->fd_handlers
);
203 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
204 ObMainLoopTimer
*t
= it
->data
;
205 if (t
->destroy
) t
->destroy(t
->data
);
208 g_slist_free(loop
->timers
);
211 for (i
= 0; i
< NUM_SIGNALS
; ++i
)
212 for (it
= loop
->signal_handlers
[i
]; it
; it
= next
) {
213 ObMainLoopSignalHandlerType
*h
= it
->data
;
214 next
= g_slist_next(it
);
215 ob_main_loop_signal_remove(loop
, h
->func
);
218 all_loops
= g_slist_remove(all_loops
, loop
);
220 /* only do this if we're the last loop destroyed */
224 /* grab all the signals that cause core dumps */
225 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
) {
226 if (all_signals
[core_signals
[i
]].installed
) {
227 sigaction(core_signals
[i
],
228 &all_signals
[core_signals
[i
]].oldact
, NULL
);
229 all_signals
[core_signals
[i
]].installed
--;
234 for (it
= loop
->action_queue
; it
; it
= g_slist_next(it
))
235 action_unref(it
->data
);
236 g_slist_free(loop
->action_queue
);
242 static void fd_handle_foreach(gpointer key
,
246 ObMainLoopFdHandlerType
*h
= value
;
249 if (FD_ISSET(h
->fd
, set
))
250 h
->func(h
->fd
, h
->data
);
253 void ob_main_loop_queue_action(ObMainLoop
*loop
, ObAction
*act
)
255 loop
->action_queue
= g_slist_append(loop
->action_queue
, action_copy(act
));
258 static void ob_main_loop_client_destroy(ObClient
*client
, gpointer data
)
260 ObMainLoop
*loop
= data
;
263 for (it
= loop
->action_queue
; it
; it
= g_slist_next(it
)) {
264 ObAction
*act
= it
->data
;
266 if (act
->data
.any
.c
== client
)
267 act
->data
.any
.c
= NULL
;
271 void ob_main_loop_run(ObMainLoop
*loop
)
274 struct timeval
*wait
;
280 loop
->running
= TRUE
;
282 client_add_destructor(ob_main_loop_client_destroy
, loop
);
285 if (loop
->signal_fired
) {
289 /* block signals so that we can do this without the data changing
291 sigprocmask(SIG_SETMASK
, &all_signals_set
, &oldset
);
293 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
294 while (loop
->signals_fired
[i
]) {
295 for (it
= loop
->signal_handlers
[i
];
296 it
; it
= g_slist_next(it
)) {
297 ObMainLoopSignalHandlerType
*h
= it
->data
;
300 loop
->signals_fired
[i
]--;
303 loop
->signal_fired
= FALSE
;
305 sigprocmask(SIG_SETMASK
, &oldset
, NULL
);
306 } else if (XPending(loop
->display
)) {
308 XNextEvent(loop
->display
, &e
);
310 for (it
= loop
->x_handlers
; it
; it
= g_slist_next(it
)) {
311 ObMainLoopXHandlerType
*h
= it
->data
;
312 h
->func(&e
, h
->data
);
314 } while (XPending(loop
->display
));
315 } else if (loop
->action_queue
) {
316 /* only fire off one action at a time, then go back for more
317 X events, since the action might cause some X events (like
321 act
= loop
->action_queue
->data
;
322 if (act
->data
.any
.client_action
== OB_CLIENT_ACTION_ALWAYS
&&
326 g_slist_delete_link(loop
->action_queue
,
331 } while (!act
&& loop
->action_queue
);
334 act
->func(&act
->data
);
336 g_slist_delete_link(loop
->action_queue
,
341 /* this only runs if there were no x events received */
343 timer_dispatch(loop
, (GTimeVal
**)&wait
);
345 selset
= loop
->fd_set
;
346 /* there is a small race condition here. if a signal occurs
347 between this if() and the select() then we will not process
348 the signal until 'wait' expires. possible solutions include
349 using GStaticMutex, and having the signal handler set 'wait'
351 if (!loop
->signal_fired
)
352 select(loop
->fd_max
+ 1, &selset
, NULL
, NULL
, wait
);
354 /* handle the X events with highest prioirity */
355 if (FD_ISSET(loop
->fd_x
, &selset
))
358 g_hash_table_foreach(loop
->fd_handlers
,
359 fd_handle_foreach
, &selset
);
363 client_remove_destructor(ob_main_loop_client_destroy
);
365 loop
->running
= FALSE
;
368 void ob_main_loop_exit(ObMainLoop
*loop
)
373 /*** XEVENT WATCHERS ***/
375 void ob_main_loop_x_add(ObMainLoop
*loop
,
376 ObMainLoopXHandler handler
,
378 GDestroyNotify notify
)
380 ObMainLoopXHandlerType
*h
;
382 h
= g_new(ObMainLoopXHandlerType
, 1);
387 loop
->x_handlers
= g_slist_prepend(loop
->x_handlers
, h
);
390 void ob_main_loop_x_remove(ObMainLoop
*loop
,
391 ObMainLoopXHandler handler
)
395 for (it
= loop
->x_handlers
; it
; it
= next
) {
396 ObMainLoopXHandlerType
*h
= it
->data
;
397 next
= g_slist_next(it
);
398 if (h
->func
== handler
) {
399 loop
->x_handlers
= g_slist_delete_link(loop
->x_handlers
, it
);
400 if (h
->destroy
) h
->destroy(h
->data
);
406 /*** SIGNAL WATCHERS ***/
408 static void sighandler(gint sig
)
413 g_return_if_fail(sig
< NUM_SIGNALS
);
415 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
)
416 if (sig
== core_signals
[i
]) {
417 /* XXX special case for signals that default to core dump.
418 but throw some helpful output here... */
420 fprintf(stderr
, "Fuck yah. Core dump. (Signal=%d)\n", sig
);
422 /* die with a core dump */
426 for (it
= all_loops
; it
; it
= g_slist_next(it
)) {
427 ObMainLoop
*loop
= it
->data
;
428 loop
->signal_fired
= TRUE
;
429 loop
->signals_fired
[sig
]++;
433 void ob_main_loop_signal_add(ObMainLoop
*loop
,
435 ObMainLoopSignalHandler handler
,
437 GDestroyNotify notify
)
439 ObMainLoopSignalHandlerType
*h
;
441 g_return_if_fail(signal
< NUM_SIGNALS
);
443 h
= g_new(ObMainLoopSignalHandlerType
, 1);
449 loop
->signal_handlers
[h
->signal
] =
450 g_slist_prepend(loop
->signal_handlers
[h
->signal
], h
);
452 if (!all_signals
[signal
].installed
) {
453 struct sigaction action
;
456 sigemptyset(&sigset
);
457 action
.sa_handler
= sighandler
;
458 action
.sa_mask
= sigset
;
459 action
.sa_flags
= SA_NOCLDSTOP
;
461 sigaction(signal
, &action
, &all_signals
[signal
].oldact
);
464 all_signals
[signal
].installed
++;
467 void ob_main_loop_signal_remove(ObMainLoop
*loop
,
468 ObMainLoopSignalHandler handler
)
473 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
474 for (it
= loop
->signal_handlers
[i
]; it
; it
= next
) {
475 ObMainLoopSignalHandlerType
*h
= it
->data
;
477 next
= g_slist_next(it
);
479 if (h
->func
== handler
) {
480 g_assert(all_signals
[h
->signal
].installed
> 0);
482 all_signals
[h
->signal
].installed
--;
483 if (!all_signals
[h
->signal
].installed
) {
484 sigaction(h
->signal
, &all_signals
[h
->signal
].oldact
, NULL
);
487 loop
->signal_handlers
[i
] =
488 g_slist_delete_link(loop
->signal_handlers
[i
], it
);
489 if (h
->destroy
) h
->destroy(h
->data
);
498 /*** FILE DESCRIPTOR WATCHERS ***/
500 static void max_fd_func(gpointer key
, gpointer value
, gpointer data
)
502 ObMainLoop
*loop
= data
;
505 loop
->fd_max
= MAX(loop
->fd_max
, *(gint
*)key
);
508 static void calc_max_fd(ObMainLoop
*loop
)
510 loop
->fd_max
= loop
->fd_x
;
512 g_hash_table_foreach(loop
->fd_handlers
, max_fd_func
, loop
);
515 void ob_main_loop_fd_add(ObMainLoop
*loop
,
517 ObMainLoopFdHandler handler
,
519 GDestroyNotify notify
)
521 ObMainLoopFdHandlerType
*h
;
523 h
= g_new(ObMainLoopFdHandlerType
, 1);
530 g_hash_table_replace(loop
->fd_handlers
, &h
->fd
, h
);
531 FD_SET(h
->fd
, &loop
->fd_set
);
535 static void fd_handler_destroy(gpointer data
)
537 ObMainLoopFdHandlerType
*h
= data
;
539 FD_CLR(h
->fd
, &h
->loop
->fd_set
);
545 void ob_main_loop_fd_remove(ObMainLoop
*loop
,
548 g_hash_table_remove(loop
->fd_handlers
, &fd
);
553 #define NEAREST_TIMEOUT(loop) \
554 (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
556 static glong
timecompare(GTimeVal
*a
, GTimeVal
*b
)
560 if ((r
= b
->tv_sec
- a
->tv_sec
)) return r
;
561 return b
->tv_usec
- a
->tv_usec
;
565 static void insert_timer(ObMainLoop
*loop
, ObMainLoopTimer
*ins
)
568 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
569 ObMainLoopTimer
*t
= it
->data
;
570 if (timecompare(&ins
->timeout
, &t
->timeout
) >= 0) {
571 loop
->timers
= g_slist_insert_before(loop
->timers
, it
, ins
);
575 if (it
== NULL
) /* didnt fit anywhere in the list */
576 loop
->timers
= g_slist_append(loop
->timers
, ins
);
579 void ob_main_loop_timeout_add(ObMainLoop
*loop
,
583 GDestroyNotify notify
)
585 ObMainLoopTimer
*t
= g_new(ObMainLoopTimer
, 1);
586 t
->delay
= microseconds
;
591 g_get_current_time(&loop
->now
);
592 t
->last
= t
->timeout
= loop
->now
;
593 g_time_val_add(&t
->timeout
, t
->delay
);
595 insert_timer(loop
, t
);
598 void ob_main_loop_timeout_remove(ObMainLoop
*loop
,
603 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
604 ObMainLoopTimer
*t
= it
->data
;
605 if (t
->func
== handler
)
610 void ob_main_loop_timeout_remove_data(ObMainLoop
*loop
,
616 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
617 ObMainLoopTimer
*t
= it
->data
;
618 if (t
->func
== handler
&& t
->data
== data
)
623 /* find the time to wait for the nearest timeout */
624 static gboolean
nearest_timeout_wait(ObMainLoop
*loop
, GTimeVal
*tm
)
626 if (loop
->timers
== NULL
)
629 tm
->tv_sec
= NEAREST_TIMEOUT(loop
).tv_sec
- loop
->now
.tv_sec
;
630 tm
->tv_usec
= NEAREST_TIMEOUT(loop
).tv_usec
- loop
->now
.tv_usec
;
632 while (tm
->tv_usec
< 0) {
633 tm
->tv_usec
+= G_USEC_PER_SEC
;
636 tm
->tv_sec
+= tm
->tv_usec
/ G_USEC_PER_SEC
;
637 tm
->tv_usec
%= G_USEC_PER_SEC
;
644 static void timer_dispatch(ObMainLoop
*loop
, GTimeVal
**wait
)
648 gboolean fired
= FALSE
;
650 g_get_current_time(&loop
->now
);
652 for (it
= loop
->timers
; it
; it
= next
) {
653 ObMainLoopTimer
*curr
;
655 next
= g_slist_next(it
);
659 /* since timer_stop doesn't actually free the timer, we have to do our
660 real freeing in here.
664 loop
->timers
= g_slist_delete_link(loop
->timers
, it
);
666 curr
->destroy(curr
->data
);
671 /* the queue is sorted, so if this timer shouldn't fire, none are
673 if (timecompare(&NEAREST_TIMEOUT(loop
), &loop
->now
) < 0)
676 /* we set the last fired time to delay msec after the previous firing,
677 then re-insert. timers maintain their order and may trigger more
678 than once if they've waited more than one delay's worth of time.
680 loop
->timers
= g_slist_delete_link(loop
->timers
, it
);
681 g_time_val_add(&curr
->last
, curr
->delay
);
682 if (curr
->func(curr
->data
)) {
683 g_time_val_add(&curr
->timeout
, curr
->delay
);
684 insert_timer(loop
, curr
);
687 curr
->destroy(curr
->data
);
695 /* if at least one timer fires, then don't wait on X events, as there
696 may already be some in the queue from the timer callbacks.
698 loop
->ret_wait
.tv_sec
= loop
->ret_wait
.tv_usec
= 0;
699 *wait
= &loop
->ret_wait
;
700 } else if (nearest_timeout_wait(loop
, &loop
->ret_wait
))
701 *wait
= &loop
->ret_wait
;