From: Dana Jansens Date: Sat, 30 Aug 2003 07:20:16 +0000 (+0000) Subject: using the ObMainLoop, which rulz the planet X-Git-Url: https://git.brokenzipper.com/gitweb?a=commitdiff_plain;h=de4f92ccc66c1dad1a2820a07d1f0161bd61a855;p=chaz%2Fopenbox using the ObMainLoop, which rulz the planet --- diff --git a/Makefile.am b/Makefile.am index edfb202b..6d447a60 100644 --- a/Makefile.am +++ b/Makefile.am @@ -146,6 +146,8 @@ openbox_openbox_SOURCES = \ openbox/keyboard.h \ openbox/keytree.c \ openbox/keytree.h \ + openbox/mainloop.c \ + openbox/mainloop.h \ openbox/menuframe.c \ openbox/menuframe.h \ openbox/menu.c \ @@ -174,8 +176,6 @@ openbox_openbox_SOURCES = \ openbox/stacking.h \ openbox/startup.c \ openbox/startup.h \ - openbox/timer.c \ - openbox/timer.h \ openbox/translate.c \ openbox/translate.h \ openbox/window.c \ diff --git a/openbox/dock.c b/openbox/dock.c index 07f2dbe0..df176d66 100644 --- a/openbox/dock.c +++ b/openbox/dock.c @@ -1,5 +1,6 @@ #include "debug.h" #include "dock.h" +#include "mainloop.h" #include "screen.h" #include "prop.h" #include "config.h" @@ -522,15 +523,13 @@ void dock_app_drag(ObDockApp *app, XMotionEvent *e) dock_configure(); } -static void hide_timeout(void *n) +static gboolean hide_timeout(gpointer data) { - /* dont repeat */ - timer_stop(dock->hide_timer); - dock->hide_timer = NULL; - /* hide */ dock->hidden = TRUE; dock_configure(); + + return FALSE; /* don't repeat */ } void dock_hide(gboolean hide) @@ -543,14 +542,9 @@ void dock_hide(gboolean hide) dock_configure(); /* if was hiding, stop it */ - if (dock->hide_timer) { - timer_stop(dock->hide_timer); - dock->hide_timer = NULL; - } + ob_main_loop_timeout_remove(ob_main_loop, hide_timeout); } else { - g_assert(!dock->hide_timer); - dock->hide_timer = timer_start(config_dock_hide_timeout * 1000, - (ObTimeoutHandler)hide_timeout, - NULL); + ob_main_loop_timeout_add(ob_main_loop, config_dock_hide_timeout * 1000, + hide_timeout, NULL, NULL); } } diff --git a/openbox/dock.h b/openbox/dock.h index f42a2027..fbd5e169 100644 --- a/openbox/dock.h +++ b/openbox/dock.h @@ -1,7 +1,6 @@ #ifndef __dock_h #define __dock_h -#include "timer.h" #include "window.h" #include "stacking.h" #include "geom.h" @@ -28,7 +27,6 @@ struct _ObDock gint h; gboolean hidden; - ObTimer *hide_timer; GList *dock_apps; }; diff --git a/openbox/event.c b/openbox/event.c index 04e65145..92ebdfbf 100644 --- a/openbox/event.c +++ b/openbox/event.c @@ -11,12 +11,12 @@ #include "menuframe.h" #include "keyboard.h" #include "mouse.h" +#include "mainloop.h" #include "framerender.h" #include "focus.h" #include "moveresize.h" #include "stacking.h" #include "extensions.h" -#include "timer.h" #include "event.h" #include @@ -39,18 +39,12 @@ #include #endif -static void event_process(XEvent *e); +static void event_process(const XEvent *e, gpointer data); static void event_handle_root(XEvent *e); static void event_handle_menu(XEvent *e); static void event_handle_dock(ObDock *s, XEvent *e); static void event_handle_dockapp(ObDockApp *app, XEvent *e); static void event_handle_client(ObClient *c, XEvent *e); -static void fd_event_handle(); -#ifdef USE_SM -static void ice_watch(IceConn conn, IcePointer data, Bool opening, - IcePointer *watch_data); -#endif -static void find_max_fd(); #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \ (e)->xfocus.detail == NotifyAncestor || \ @@ -75,32 +69,38 @@ static const int mask_table[] = { }; static int mask_table_size; -static fd_set selset, allset; #ifdef USE_SM -static IceConn ice_conn; -static int ice_fd; -#endif -static int max_fd, x_fd; -static GData *fd_handler_list; - +static void ice_handler(int fd, gpointer conn) +{ + Bool b; + IceProcessMessages(conn, NULL, &b); +} -#ifdef USE_SM static void ice_watch(IceConn conn, IcePointer data, Bool opening, IcePointer *watch_data) { + static gint fd = -1; + if (opening) { - g_assert (ice_fd < 0); - ice_conn = conn; - ice_fd = IceConnectionNumber(conn); - FD_SET(ice_fd, &allset); + fd = IceConnectionNumber(conn); + ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL); } else { - FD_CLR(ice_fd, &allset); - ice_fd = -1; + ob_main_loop_fd_remove(ob_main_loop, fd); + fd = -1; } - find_max_fd(); } #endif +#ifdef USE_LIBSN +static void sn_handler(const XEvent *e, gpointer display) +{ + XEvent ec; + ec = *e; + sn_display_process_event(display, &ec); +} +#endif + + void event_startup() { mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]); @@ -128,59 +128,20 @@ void event_startup() } } - FD_ZERO(&allset); - max_fd = x_fd = ConnectionNumber(ob_display); - FD_SET(x_fd, &allset); + ob_main_loop_x_add(ob_main_loop, event_process, NULL, NULL); #ifdef USE_SM - ice_fd = -1; IceAddConnectionWatch(ice_watch, NULL); #endif - g_datalist_init(&fd_handler_list); +#ifdef USE_LIBSN + ob_main_loop_x_add(ob_main_loop, sn_handler, ob_sn_display, NULL); +#endif } void event_shutdown() { XFreeModifiermap(modmap); - g_datalist_clear(&fd_handler_list); -} - -void event_loop() -{ - XEvent e; - struct timeval *wait; - gboolean had_event = FALSE; - - while (XPending(ob_display)) { - XNextEvent(ob_display, &e); - -#ifdef USE_LIBSN - sn_display_process_event(ob_sn_display, &e); -#endif - - event_process(&e); - had_event = TRUE; - } - - if (!had_event) { - timer_dispatch((GTimeVal**)&wait); - selset = allset; - select(max_fd + 1, &selset, NULL, NULL, wait); - - /* handle the X events as soon as possible? */ - if (FD_ISSET(x_fd, &selset)) - return; - -#ifdef USE_SM - if (ice_fd >= 0 && FD_ISSET(ice_fd, &selset)) { - Bool b; - IceProcessMessages(ice_conn, NULL, &b); - } -#endif - - fd_event_handle(); - } } static Window event_get_window(XEvent *e) @@ -399,7 +360,7 @@ static gboolean event_ignore(XEvent *e, ObClient *client) #endif return TRUE; } else { - event_process(&fe); + event_process(&fe, NULL); #ifdef DEBUG_FOCUS ob_debug("focused window got an Out/In back to " "itself but focus_client was null " @@ -411,7 +372,7 @@ static gboolean event_ignore(XEvent *e, ObClient *client) /* once all the FocusOut's have been dealt with, if there is a FocusIn still left and it is valid, then use it */ - event_process(&fe); + event_process(&fe, NULL); /* secret magic way of event_process telling us that no client was found for the FocusIn event. ^_^ */ if (fe.xfocus.window != None) { @@ -457,13 +418,18 @@ static gboolean event_ignore(XEvent *e, ObClient *client) return FALSE; } -static void event_process(XEvent *e) +static void event_process(const XEvent *ec, gpointer data) { Window window; ObClient *client = NULL; ObDock *dock = NULL; ObDockApp *dockapp = NULL; ObWindow *obwin = NULL; + XEvent ee, *e; + + /* make a copy we can mangle */ + ee = *ec; + e = ⅇ window = event_get_window(e); if ((obwin = g_hash_table_lookup(window_map, &window))) { @@ -1063,50 +1029,6 @@ static void event_handle_client(ObClient *client, XEvent *e) } } -void event_add_fd_handler(event_fd_handler *h) { - g_datalist_id_set_data(&fd_handler_list, h->fd, h); - FD_SET(h->fd, &allset); - max_fd = MAX(max_fd, h->fd); -} - -static void find_max_fd_foreach(GQuark n, gpointer data, gpointer max) -{ - *((unsigned int *)max) = MAX(*((unsigned int *)max), n); -} - -static void find_max_fd() -{ - int tmpmax = -1; - g_datalist_foreach(&fd_handler_list, find_max_fd_foreach, - (gpointer)&tmpmax); - max_fd = MAX(x_fd, tmpmax); -#ifdef USE_SM - max_fd = MAX(ice_fd, max_fd); -#endif -} - -void event_remove_fd(gint n) -{ - FD_CLR(n, &allset); - g_datalist_id_remove_data(&fd_handler_list, (GQuark)n); - find_max_fd(); -} - -static void fd_event_handle_foreach(GQuark n, - gpointer data, gpointer user_data) -{ - if (FD_ISSET( (int)n, &selset)) { - event_fd_handler *h = (event_fd_handler *)data; - g_assert(h->fd == (int)n); - h->handler(h->fd, h->data); - } -} - -static void fd_event_handle() -{ - g_datalist_foreach(&fd_handler_list, fd_event_handle_foreach, NULL); -} - static void event_handle_dock(ObDock *s, XEvent *e) { switch (e->type) { diff --git a/openbox/event.h b/openbox/event.h index 6aade180..0c6c7b97 100644 --- a/openbox/event.h +++ b/openbox/event.h @@ -14,15 +14,4 @@ extern guint ScrollLockMask; void event_startup(); void event_shutdown(); -typedef struct event_fd_handler { - gint fd; - gpointer data; - void (*handler)(gint fd, gpointer data); -} event_fd_handler; - -void event_add_fd_handler(event_fd_handler *handler); -void event_remove_fd(gint n); - -void event_loop(); - #endif diff --git a/openbox/grab.c b/openbox/grab.c index 549b0f1f..2ef51fef 100644 --- a/openbox/grab.c +++ b/openbox/grab.c @@ -30,6 +30,9 @@ gboolean grab_keyboard(gboolean grab) XUngrabKeyboard(ob_display, event_lasttime); ret = TRUE; } + + g_message("grabs: %d", kgrabs); + return ret; } diff --git a/openbox/keyboard.c b/openbox/keyboard.c index 42eea890..86ead528 100644 --- a/openbox/keyboard.c +++ b/openbox/keyboard.c @@ -1,3 +1,4 @@ +#include "mainloop.h" #include "focus.h" #include "screen.h" #include "frame.h" @@ -7,7 +8,6 @@ #include "client.h" #include "action.h" #include "prop.h" -#include "timer.h" #include "config.h" #include "keytree.h" #include "keyboard.h" @@ -27,7 +27,6 @@ typedef struct { static GSList *interactive_states; static KeyBindingTree *curpos; -static ObTimer *chain_timer; static void grab_for_window(Window win, gboolean grab) { @@ -62,23 +61,23 @@ static void grab_keys(gboolean grab) grab_for_window(((ObClient*)it->data)->frame->window, grab); } +static gboolean chain_timeout(gpointer data) +{ + keyboard_reset_chains(); + + return FALSE; /* don't repeat */ +} + void keyboard_reset_chains() { - if (chain_timer) { - timer_stop(chain_timer); - chain_timer = NULL; - } + ob_main_loop_timeout_remove(ob_main_loop, chain_timeout); + if (curpos) { curpos = NULL; grab_keys(TRUE); } } -static void chain_timeout(ObTimer *t, void *data) -{ - keyboard_reset_chains(); -} - gboolean keyboard_bind(GList *keylist, ObAction *action) { KeyBindingTree *tree, *t; @@ -213,10 +212,10 @@ void keyboard_event(ObClient *client, const XEvent *e) if (p->key == e->xkey.keycode && p->state == e->xkey.state) { if (p->first_child != NULL) { /* part of a chain */ - if (chain_timer) timer_stop(chain_timer); + ob_main_loop_timeout_remove(ob_main_loop, chain_timeout); /* 5 second timeout for chains */ - chain_timer = timer_start(5000*1000, chain_timeout, - NULL); + ob_main_loop_timeout_add(ob_main_loop, 5 * G_USEC_PER_SEC, + chain_timeout, NULL, NULL); curpos = p; grab_keys(TRUE); } else { diff --git a/openbox/mainloop.c b/openbox/mainloop.c new file mode 100644 index 00000000..4b07cc4b --- /dev/null +++ b/openbox/mainloop.c @@ -0,0 +1,601 @@ +#include "mainloop.h" + +#include +#include +#include +#include + +typedef struct _ObMainLoopTimer ObMainLoopTimer; +typedef struct _ObMainLoopSignal ObMainLoopSignal; +typedef struct _ObMainLoopSignalHandlerType ObMainLoopSignalHandlerType; +typedef struct _ObMainLoopXHandlerType ObMainLoopXHandlerType; +typedef struct _ObMainLoopFdHandlerType ObMainLoopFdHandlerType; + +/* this should be more than the number of possible signals on any + architecture... */ +#define NUM_SIGNALS 99 + +/* all created ObMainLoops. Used by the signal handler to pass along signals */ +static GSList *all_loops; + +/* signals are global to all loops */ +struct { + guint installed; /* a ref count */ + struct sigaction oldact; +} all_signals[NUM_SIGNALS]; + +/* a set of all possible signals */ +sigset_t all_signals_set; + +/* signals which cause a core dump, these can't be used for callbacks */ +static gint core_signals[] = +{ + SIGABRT, + SIGSEGV, + SIGFPE, + SIGILL, + SIGQUIT, + SIGTRAP, + SIGSYS, + SIGBUS, + SIGXCPU, + SIGXFSZ +}; +#define NUM_CORE_SIGNALS (sizeof(core_signals) / sizeof(core_signals[0])) + +static void sighandler(gint sig); +static void timer_dispatch(ObMainLoop *loop, GTimeVal **wait); +static void fd_handler_destroy(gpointer data); + +struct _ObMainLoop +{ + Display *display; + + gboolean run; /* do keep running */ + gboolean running; /* is still running */ + + GSList *x_handlers; + + gint fd_x; /* The X fd is a special case! */ + gint fd_max; + GHashTable *fd_handlers; + fd_set fd_set; + + GSList *timers; + GTimeVal now; + GTimeVal ret_wait; + + gboolean signal_fired; + guint signals_fired[NUM_SIGNALS]; + GSList *signal_handlers[NUM_SIGNALS]; +}; + +struct _ObMainLoopTimer +{ + gulong delay; + GSourceFunc func; + gpointer data; + GDestroyNotify destroy; + + /* The timer needs to be freed */ + gboolean del_me; + /* The time the last fire should've been at */ + GTimeVal last; + /* When this timer will next trigger */ + GTimeVal timeout; +}; + +struct _ObMainLoopSignalHandlerType +{ + ObMainLoop *loop; + gint signal; + gpointer data; + ObMainLoopSignalHandler func; + GDestroyNotify destroy; +}; + +struct _ObMainLoopXHandlerType +{ + ObMainLoop *loop; + gpointer data; + ObMainLoopXHandler func; + GDestroyNotify destroy; +}; + +struct _ObMainLoopFdHandlerType +{ + ObMainLoop *loop; + gint fd; + gpointer data; + ObMainLoopFdHandler func; + GDestroyNotify destroy; +}; + +ObMainLoop *ob_main_loop_new(Display *display) +{ + ObMainLoop *loop; + + loop = g_new0(ObMainLoop, 1); + loop->display = display; + loop->fd_x = ConnectionNumber(display); + FD_ZERO(&loop->fd_set); + FD_SET(loop->fd_x, &loop->fd_set); + loop->fd_max = loop->fd_x; + + loop->fd_handlers = g_hash_table_new_full(g_int_hash, g_int_equal, + NULL, fd_handler_destroy); + + g_get_current_time(&loop->now); + + /* only do this if we're the first loop created */ + if (!all_loops) { + guint i; + struct sigaction action; + sigset_t sigset; + + /* initialize the all_signals_set */ + sigfillset(&all_signals_set); + + sigemptyset(&sigset); + action.sa_handler = sighandler; + action.sa_mask = sigset; + action.sa_flags = SA_NOCLDSTOP; + + /* grab all the signals that cause core dumps */ + for (i = 0; i < NUM_CORE_SIGNALS; ++i) { + /* SIGABRT is curiously not grabbed here!! that's because when we + get one of the core_signals, we use abort() to dump the core. + And having the abort() only go back to our signal handler again + is less than optimal */ + if (core_signals[i] != SIGABRT) { + sigaction(core_signals[i], &action, + &all_signals[core_signals[i]].oldact); + all_signals[core_signals[i]].installed++; + } + } + } + + all_loops = g_slist_prepend(all_loops, loop); + + return loop; +} + +void ob_main_loop_destroy(ObMainLoop *loop) +{ + guint i; + GSList *it, *next; + + if (loop) { + g_assert(loop->running == FALSE); + + for (it = loop->x_handlers; it; it = next) { + ObMainLoopXHandlerType *h = it->data; + next = g_slist_next(it); + ob_main_loop_x_remove(loop, h->func); + } + + g_hash_table_destroy(loop->fd_handlers); + + for (it = loop->timers; it; it = g_slist_next(it)) { + ObMainLoopTimer *t = it->data; + if (t->destroy) t->destroy(t->data); + g_free(t); + } + g_slist_free(loop->timers); + loop->timers = NULL; + + for (i = 0; i < NUM_SIGNALS; ++i) + for (it = loop->signal_handlers[i]; it; it = next) { + ObMainLoopSignalHandlerType *h = it->data; + next = g_slist_next(it); + ob_main_loop_signal_remove(loop, h->func); + } + + all_loops = g_slist_remove(all_loops, loop); + + /* only do this if we're the last loop destroyed */ + if (!all_loops) { + guint i; + + /* grab all the signals that cause core dumps */ + for (i = 0; i < NUM_CORE_SIGNALS; ++i) { + if (all_signals[core_signals[i]].installed) { + sigaction(core_signals[i], + &all_signals[core_signals[i]].oldact, NULL); + all_signals[core_signals[i]].installed--; + } + } + } + + g_free(loop); + } +} + +static void fd_handle_foreach(gpointer key, + gpointer value, + gpointer data) +{ + ObMainLoopFdHandlerType *h = value; + fd_set *set = data; + + if (FD_ISSET(h->fd, set)) + h->func(h->fd, h->data); +} + +void ob_main_loop_run(ObMainLoop *loop) +{ + XEvent e; + struct timeval *wait; + fd_set selset; + GSList *it; + + loop->run = TRUE; + loop->running = TRUE; + + while (loop->run) { + if (loop->signal_fired) { + guint i; + sigset_t oldset; + + /* block signals so that we can do this without the data changing + on us */ + sigprocmask(SIG_SETMASK, &all_signals_set, &oldset); + + for (i = 0; i < NUM_SIGNALS; ++i) { + while (loop->signals_fired[i]) { + for (it = loop->signal_handlers[i]; + it; it = g_slist_next(it)) { + ObMainLoopSignalHandlerType *h = it->data; + h->func(i, h->data); + } + loop->signals_fired[i]--; + } + } + loop->signal_fired = FALSE; + + sigprocmask(SIG_SETMASK, &oldset, NULL); + } else if (XPending(loop->display)) { + do { + XNextEvent(loop->display, &e); + + for (it = loop->x_handlers; it; it = g_slist_next(it)) { + ObMainLoopXHandlerType *h = it->data; + h->func(&e, h->data); + } + } while (XPending(loop->display)); + } else { + /* this only runs if there were no x events received */ + + timer_dispatch(loop, (GTimeVal**)&wait); + selset = loop->fd_set; + /* there is a small race condition here. if a signal occurs + between this if() and the select() then we will not process + the signal until 'wait' expires. possible solutions include + using GStaticMutex, and having the signal handler set 'wait' + to 0 */ + if (!loop->signal_fired) + select(loop->fd_max + 1, &selset, NULL, NULL, wait); + + /* handle the X events with highest prioirity */ + if (FD_ISSET(loop->fd_x, &selset)) + continue; + + g_hash_table_foreach(loop->fd_handlers, + fd_handle_foreach, &selset); + } + } + + loop->running = FALSE; +} + +void ob_main_loop_exit(ObMainLoop *loop) +{ + loop->run = FALSE; +} + +/*** XEVENT WATCHERS ***/ + +void ob_main_loop_x_add(ObMainLoop *loop, + ObMainLoopXHandler handler, + gpointer data, + GDestroyNotify notify) +{ + ObMainLoopXHandlerType *h; + + h = g_new(ObMainLoopXHandlerType, 1); + h->loop = loop; + h->func = handler; + h->data = data; + h->destroy = notify; + loop->x_handlers = g_slist_prepend(loop->x_handlers, h); +} + +void ob_main_loop_x_remove(ObMainLoop *loop, + ObMainLoopXHandler handler) +{ + GSList *it, *next; + + for (it = loop->x_handlers; it; it = next) { + ObMainLoopXHandlerType *h = it->data; + next = g_slist_next(it); + if (h->func == handler) { + loop->x_handlers = g_slist_delete_link(loop->x_handlers, it); + if (h->destroy) h->destroy(h->data); + g_free(h); + } + } +} + +/*** SIGNAL WATCHERS ***/ + +static void sighandler(gint sig) +{ + GSList *it; + guint i; + + g_return_if_fail(sig < NUM_SIGNALS); + + for (i = 0; i < NUM_CORE_SIGNALS; ++i) + if (sig == core_signals[i]) { + /* XXX special case for signals that default to core dump. + but throw some helpful output here... */ + + fprintf(stderr, "Fuck ya. Core dump. (Signal=%d)\n", sig); + + /* die with a core dump */ + abort(); + } + + for (it = all_loops; it; it = g_slist_next(it)) { + ObMainLoop *loop = it->data; + loop->signal_fired = TRUE; + loop->signals_fired[sig]++; + } +} + +void ob_main_loop_signal_add(ObMainLoop *loop, + gint signal, + ObMainLoopSignalHandler handler, + gpointer data, + GDestroyNotify notify) +{ + ObMainLoopSignalHandlerType *h; + + g_return_if_fail(signal < NUM_SIGNALS); + + h = g_new(ObMainLoopSignalHandlerType, 1); + h->loop = loop; + h->signal = signal; + h->func = handler; + h->data = data; + h->destroy = notify; + loop->signal_handlers[h->signal] = + g_slist_prepend(loop->signal_handlers[h->signal], h); + + if (!all_signals[signal].installed) { + struct sigaction action; + sigset_t sigset; + + sigemptyset(&sigset); + action.sa_handler = sighandler; + action.sa_mask = sigset; + action.sa_flags = SA_NOCLDSTOP; + + sigaction(signal, &action, &all_signals[signal].oldact); + } + + all_signals[signal].installed++; +} + +void ob_main_loop_signal_remove(ObMainLoop *loop, + ObMainLoopSignalHandler handler) +{ + guint i; + GSList *it, *next; + + for (i = 0; i < NUM_SIGNALS; ++i) { + for (it = loop->signal_handlers[i]; it; it = next) { + ObMainLoopSignalHandlerType *h = it->data; + + next = g_slist_next(it); + + if (h->func == handler) { + g_assert(all_signals[h->signal].installed > 0); + + all_signals[h->signal].installed--; + if (!all_signals[h->signal].installed) { + sigaction(h->signal, &all_signals[h->signal].oldact, NULL); + } + + loop->signal_handlers[i] = + g_slist_delete_link(loop->signal_handlers[i], it); + if (h->destroy) h->destroy(h->data); + + g_free(h); + } + } + } + +} + +/*** FILE DESCRIPTOR WATCHERS ***/ + +static void max_fd_func(gpointer key, gpointer value, gpointer data) +{ + ObMainLoop *loop = data; + + /* key is the fd */ + loop->fd_max = MAX(loop->fd_max, *(gint*)key); +} + +static void calc_max_fd(ObMainLoop *loop) +{ + loop->fd_max = loop->fd_x; + + g_hash_table_foreach(loop->fd_handlers, max_fd_func, loop); +} + +void ob_main_loop_fd_add(ObMainLoop *loop, + gint fd, + ObMainLoopFdHandler handler, + gpointer data, + GDestroyNotify notify) +{ + ObMainLoopFdHandlerType *h; + + h = g_new(ObMainLoopFdHandlerType, 1); + h->loop = loop; + h->fd = fd; + h->func = handler; + h->data = data; + h->destroy = notify; + + g_hash_table_replace(loop->fd_handlers, &h->fd, h); + FD_SET(h->fd, &loop->fd_set); + calc_max_fd(loop); +} + +static void fd_handler_destroy(gpointer data) +{ + ObMainLoopFdHandlerType *h = data; + + FD_CLR(h->fd, &h->loop->fd_set); + + if (h->destroy) + h->destroy(h->data); +} + +void ob_main_loop_fd_remove(ObMainLoop *loop, + gint fd) +{ + g_hash_table_remove(loop->fd_handlers, &fd); +} + +/*** TIMEOUTS ***/ + +#define NEAREST_TIMEOUT(loop) \ + (((ObMainLoopTimer*)(loop)->timers->data)->timeout) + +static long timecompare(GTimeVal *a, GTimeVal *b) +{ + long r; + + if ((r = b->tv_sec - a->tv_sec)) return r; + return b->tv_usec - a->tv_usec; + +} + +static void insert_timer(ObMainLoop *loop, ObMainLoopTimer *ins) +{ + GSList *it; + for (it = loop->timers; it; it = g_slist_next(it)) { + ObMainLoopTimer *t = it->data; + if (timecompare(&ins->timeout, &t->timeout) <= 0) { + loop->timers = g_slist_insert_before(loop->timers, it, ins); + break; + } + } + if (it == NULL) /* didnt fit anywhere in the list */ + loop->timers = g_slist_append(loop->timers, ins); +} + +void ob_main_loop_timeout_add(ObMainLoop *loop, + gulong microseconds, + GSourceFunc handler, + gpointer data, + GDestroyNotify notify) +{ + ObMainLoopTimer *t = g_new(ObMainLoopTimer, 1); + t->delay = microseconds; + t->func = handler; + t->data = data; + t->destroy = notify; + t->del_me = FALSE; + t->last = t->timeout = loop->now; + g_time_val_add(&t->timeout, t->delay); + + insert_timer(loop, t); +} + +void ob_main_loop_timeout_remove(ObMainLoop *loop, + GSourceFunc handler) +{ + GSList *it; + + for (it = loop->timers; it; it = g_slist_next(it)) { + ObMainLoopTimer *t = it->data; + if (t->func == handler) { + t->del_me = TRUE; + break; + } + } +} + +/* find the time to wait for the nearest timeout */ +static gboolean nearest_timeout_wait(ObMainLoop *loop, GTimeVal *tm) +{ + if (loop->timers == NULL) + return FALSE; + + tm->tv_sec = NEAREST_TIMEOUT(loop).tv_sec - loop->now.tv_sec; + tm->tv_usec = NEAREST_TIMEOUT(loop).tv_usec - loop->now.tv_usec; + + while (tm->tv_usec < 0) { + tm->tv_usec += G_USEC_PER_SEC; + tm->tv_sec--; + } + tm->tv_sec += tm->tv_usec / G_USEC_PER_SEC; + tm->tv_usec %= G_USEC_PER_SEC; + if (tm->tv_sec < 0) + tm->tv_sec = 0; + + return TRUE; +} + +static void timer_dispatch(ObMainLoop *loop, GTimeVal **wait) +{ + g_get_current_time(&loop->now); + + while (loop->timers != NULL) { + ObMainLoopTimer *curr = loop->timers->data; /* get the top element */ + /* since timer_stop doesn't actually free the timer, we have to do our + real freeing in here. + */ + if (curr->del_me) { + /* delete the top */ + loop->timers = g_slist_delete_link(loop->timers, loop->timers); + g_free(curr); + continue; + } + + /* the queue is sorted, so if this timer shouldn't fire, none are + ready */ + if (timecompare(&NEAREST_TIMEOUT(loop), &loop->now) <= 0) + break; + + /* we set the last fired time to delay msec after the previous firing, + then re-insert. timers maintain their order and may trigger more + than once if they've waited more than one delay's worth of time. + */ + loop->timers = g_slist_delete_link(loop->timers, loop->timers); + g_time_val_add(&curr->last, curr->delay); + if (curr->func(curr->data)) { + g_time_val_add(&curr->timeout, curr->delay); + insert_timer(loop, curr); + } else if (curr->destroy) { + curr->destroy(curr->data); + } + + /* if at least one timer fires, then don't wait on X events, as there + may already be some in the queue from the timer callbacks. + */ + loop->ret_wait.tv_sec = loop->ret_wait.tv_usec = 0; + *wait = &loop->ret_wait; + return; + } + + if (nearest_timeout_wait(loop, &loop->ret_wait)) + *wait = &loop->ret_wait; + else + *wait = NULL; +} diff --git a/openbox/mainloop.h b/openbox/mainloop.h new file mode 100644 index 00000000..ecbcc389 --- /dev/null +++ b/openbox/mainloop.h @@ -0,0 +1,52 @@ +#ifndef __ob__mainloop_h +#define __ob__mainloop_h + +#include +#include + +typedef struct _ObMainLoop ObMainLoop; + +ObMainLoop *ob_main_loop_new(Display *display); +void ob_main_loop_destroy(ObMainLoop *loop); + +typedef void (*ObMainLoopXHandler) (const XEvent *e, gpointer data); + +void ob_main_loop_x_add(ObMainLoop *loop, + ObMainLoopXHandler handler, + gpointer data, + GDestroyNotify notify); +void ob_main_loop_x_remove(ObMainLoop *loop, + ObMainLoopXHandler handler); + +typedef void (*ObMainLoopFdHandler) (gint fd, gpointer data); + +void ob_main_loop_fd_add(ObMainLoop *loop, + gint fd, + ObMainLoopFdHandler handler, + gpointer data, + GDestroyNotify notify); +void ob_main_loop_fd_remove(ObMainLoop *loop, + gint fd); + +typedef void (*ObMainLoopSignalHandler) (gint signal, gpointer data); + +void ob_main_loop_signal_add(ObMainLoop *loop, + gint signal, + ObMainLoopSignalHandler handler, + gpointer data, + GDestroyNotify notify); +void ob_main_loop_signal_remove(ObMainLoop *loop, + ObMainLoopSignalHandler handler); + +void ob_main_loop_timeout_add(ObMainLoop *loop, + gulong microseconds, + GSourceFunc handler, + gpointer data, + GDestroyNotify notify); +void ob_main_loop_timeout_remove(ObMainLoop *loop, + GSourceFunc handler); + +void ob_main_loop_run(ObMainLoop *loop); +void ob_main_loop_exit(ObMainLoop *loop); + +#endif diff --git a/openbox/openbox.c b/openbox/openbox.c index ca02d8bd..52146960 100644 --- a/openbox/openbox.c +++ b/openbox/openbox.c @@ -16,9 +16,9 @@ #include "mouse.h" #include "extensions.h" #include "grab.h" -#include "timer.h" #include "group.h" #include "config.h" +#include "mainloop.h" #include "gettext.h" #include "parser/parse.h" #include "render/render.h" @@ -49,6 +49,7 @@ RrInstance *ob_rr_inst; RrTheme *ob_rr_theme; +ObMainLoop *ob_main_loop; Display *ob_display; gint ob_screen; gboolean ob_sm_use = TRUE; @@ -57,20 +58,17 @@ gboolean ob_replace_wm; static ObState state; static gboolean xsync; -static gboolean shutdown; static gboolean restart; static char *restart_path; static Cursor cursors[OB_NUM_CURSORS]; static KeyCode keys[OB_NUM_KEYS]; static gchar *sm_save_file; -static void signal_handler(int signal); +static void signal_handler(int signal, gpointer data); static void parse_args(int argc, char **argv); int main(int argc, char **argv) { - struct sigaction action; - sigset_t sigset; char *path; xmlDocPtr doc; xmlNodePtr node; @@ -88,19 +86,6 @@ int main(int argc, char **argv) bind_textdomain_codeset(PACKAGE_NAME, "UTF-8"); textdomain(PACKAGE_NAME); - /* set up signal handler */ - sigemptyset(&sigset); - action.sa_handler = signal_handler; - action.sa_mask = sigset; - action.sa_flags = SA_NOCLDSTOP | SA_NODEFER; - sigaction(SIGUSR1, &action, (struct sigaction *) NULL); - sigaction(SIGPIPE, &action, (struct sigaction *) NULL); -/* sigaction(SIGSEGV, &action, (struct sigaction *) NULL);*/ - sigaction(SIGFPE, &action, (struct sigaction *) NULL); - sigaction(SIGTERM, &action, (struct sigaction *) NULL); - sigaction(SIGINT, &action, (struct sigaction *) NULL); - sigaction(SIGHUP, &action, (struct sigaction *) NULL); - /* create the ~/.openbox dir */ path = g_build_filename(g_get_home_dir(), ".openbox", NULL); mkdir(path, (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | @@ -128,6 +113,15 @@ int main(int argc, char **argv) if (fcntl(ConnectionNumber(ob_display), F_SETFD, 1) == -1) ob_exit_with_error("Failed to set display as close-on-exec."); + ob_main_loop = ob_main_loop_new(ob_display); + + /* set up signal handler */ + ob_main_loop_signal_add(ob_main_loop, SIGUSR1, signal_handler, NULL, NULL); + ob_main_loop_signal_add(ob_main_loop, SIGTERM, signal_handler, NULL, NULL); + ob_main_loop_signal_add(ob_main_loop, SIGINT, signal_handler, NULL, NULL); + ob_main_loop_signal_add(ob_main_loop, SIGHUP, signal_handler, NULL, NULL); + ob_main_loop_signal_add(ob_main_loop, SIGPIPE, signal_handler, NULL, NULL); + if (sm_save_file) session_load(sm_save_file); session_startup(argc, argv); @@ -209,9 +203,6 @@ int main(int argc, char **argv) /* startup the parsing so everything can register sections of the rc */ i = parse_startup(); - /* anything that is going to read data from the rc file needs to be - in this group */ - timer_startup(); event_startup(); grab_startup(); /* focus_backup is used for stacking, so this needs to come before @@ -246,8 +237,7 @@ int main(int argc, char **argv) client_manage_all(); state = OB_STATE_RUNNING; - while (!shutdown) - event_loop(); + ob_main_loop_run(ob_main_loop); state = OB_STATE_EXITING; dock_remove_all(); @@ -265,7 +255,6 @@ int main(int argc, char **argv) window_shutdown(); grab_shutdown(); event_shutdown(); - timer_shutdown(); config_shutdown(); } @@ -304,26 +293,14 @@ int main(int argc, char **argv) return 0; } -static void signal_handler(int sig) +static void signal_handler(int signal, gpointer data) { - switch (sig) { - case SIGUSR1: - fprintf(stderr, "Caught SIGUSR1 signal. Restarting."); + if (signal == SIGUSR1) { + fprintf(stderr, "Caught signal %d. Restarting.\n", signal); ob_restart(); - break; - - case SIGHUP: - case SIGINT: - case SIGTERM: - case SIGPIPE: - fprintf(stderr, "Caught signal %d. Exiting.", sig); + } else { + fprintf(stderr, "Caught signal %d. Exiting.\n", signal); ob_exit(); - break; - - case SIGFPE: - case SIGSEGV: - fprintf(stderr, "Caught signal %d. Aborting and dumping core.", sig); - abort(); } } @@ -418,7 +395,7 @@ void ob_restart() void ob_exit() { - shutdown = TRUE; + ob_main_loop_exit(ob_main_loop); } Cursor ob_cursor(ObCursor cursor) diff --git a/openbox/openbox.h b/openbox/openbox.h index b5b473b8..2f325dff 100644 --- a/openbox/openbox.h +++ b/openbox/openbox.h @@ -14,9 +14,13 @@ #include #include +struct _ObMainLoop; + extern RrInstance *ob_rr_inst; extern RrTheme *ob_rr_theme; +extern struct _ObMainLoop *ob_main_loop; + /*! The X display */ extern Display *ob_display; diff --git a/openbox/screen.c b/openbox/screen.c index 1eb0051d..7913770c 100644 --- a/openbox/screen.c +++ b/openbox/screen.c @@ -1,11 +1,11 @@ #include "debug.h" #include "openbox.h" +#include "mainloop.h" #include "dock.h" #include "xerror.h" #include "prop.h" #include "startup.h" #include "grab.h" -#include "timer.h" #include "config.h" #include "screen.h" #include "client.h" @@ -50,7 +50,6 @@ static Popup *desktop_cycle_popup; #ifdef USE_LIBSN static SnMonitorContext *sn_context; static int sn_busy_cnt; -static ObTimer *sn_timer; static void sn_event_func(SnMonitorEvent *event, void *data); #endif @@ -1077,13 +1076,13 @@ static void set_root_cursor() } #ifdef USE_LIBSN -static void sn_timeout(ObTimer *t, void *data) +static gboolean sn_timeout(gpointer data) { - timer_stop(sn_timer); - sn_timer = NULL; sn_busy_cnt = 0; set_root_cursor(); + + return FALSE; /* don't repeat */ } static void sn_event_func(SnMonitorEvent *ev, void *data) @@ -1104,26 +1103,20 @@ static void sn_event_func(SnMonitorEvent *ev, void *data) switch (sn_monitor_event_get_type(ev)) { case SN_MONITOR_EVENT_INITIATED: ++sn_busy_cnt; - if (sn_timer) - timer_stop(sn_timer); + ob_main_loop_timeout_remove(ob_main_loop, sn_timeout); /* 30 second timeout for apps to start */ - sn_timer = timer_start(30 * 1000000, sn_timeout, NULL); + ob_main_loop_timeout_add(ob_main_loop, 30 * G_USEC_PER_SEC, + sn_timeout, NULL, NULL); break; case SN_MONITOR_EVENT_CHANGED: break; case SN_MONITOR_EVENT_COMPLETED: if (sn_busy_cnt) --sn_busy_cnt; - if (sn_timer) { - timer_stop(sn_timer); - sn_timer = NULL; - } + ob_main_loop_timeout_remove(ob_main_loop, sn_timeout); break; case SN_MONITOR_EVENT_CANCELED: if (sn_busy_cnt) --sn_busy_cnt; - if (sn_timer) { - timer_stop(sn_timer); - sn_timer = NULL; - } + ob_main_loop_timeout_remove(ob_main_loop, sn_timeout); }; if (sn_busy_cnt != cnt) diff --git a/openbox/timer.c b/openbox/timer.c deleted file mode 100644 index 33f4bab7..00000000 --- a/openbox/timer.c +++ /dev/null @@ -1,137 +0,0 @@ -#include "timer.h" - -#ifdef HAVE_SYS_TIME_H -# include -#endif - -static GTimeVal now; -static GTimeVal ret_wait; -static GSList *timers; /* nearest timer is at the top */ - -#define NEAREST_TIMEOUT (((ObTimer*)timers->data)->timeout) - -static long timecompare(GTimeVal *a, GTimeVal *b) -{ - long r; - - if ((r = b->tv_sec - a->tv_sec)) return r; - return b->tv_usec - a->tv_usec; - -} - -static void insert_timer(ObTimer *self) -{ - GSList *it; - for (it = timers; it != NULL; it = it->next) { - ObTimer *t = it->data; - if (timecompare(&self->timeout, &t->timeout) <= 0) { - timers = g_slist_insert_before(timers, it, self); - break; - } - } - if (it == NULL) /* didnt fit anywhere in the list */ - timers = g_slist_append(timers, self); -} - -void timer_startup() -{ - g_get_current_time(&now); - timers = NULL; -} - -void timer_shutdown() -{ - GSList *it; - for (it = timers; it != NULL; it = it->next) { - g_free(it->data); - } - g_slist_free(timers); - timers = NULL; -} - -ObTimer *timer_start(long delay, ObTimeoutHandler cb, void *data) -{ - ObTimer *self = g_new(ObTimer, 1); - self->delay = delay; - self->action = cb; - self->data = data; - self->del_me = FALSE; - g_get_current_time(&now); - self->last = self->timeout = now; - g_time_val_add(&self->timeout, delay); - - insert_timer(self); - - return self; -} - -void timer_stop(ObTimer *self) -{ - self->del_me = TRUE; -} - -/* find the time to wait for the nearest timeout */ -static gboolean nearest_timeout_wait(GTimeVal *tm) -{ - if (timers == NULL) - return FALSE; - - tm->tv_sec = NEAREST_TIMEOUT.tv_sec - now.tv_sec; - tm->tv_usec = NEAREST_TIMEOUT.tv_usec - now.tv_usec; - - while (tm->tv_usec < 0) { - tm->tv_usec += G_USEC_PER_SEC; - tm->tv_sec--; - } - tm->tv_sec += tm->tv_usec / G_USEC_PER_SEC; - tm->tv_usec %= G_USEC_PER_SEC; - if (tm->tv_sec < 0) - tm->tv_sec = 0; - - return TRUE; -} - - -void timer_dispatch(GTimeVal **wait) -{ - g_get_current_time(&now); - - while (timers != NULL) { - ObTimer *curr = timers->data; /* get the top element */ - /* since timer_stop doesn't actually free the timer, we have to do our - real freeing in here. - */ - if (curr->del_me) { - timers = g_slist_delete_link(timers, timers); /* delete the top */ - g_free(curr); - continue; - } - - /* the queue is sorted, so if this timer shouldn't fire, none are - ready */ - if (timecompare(&NEAREST_TIMEOUT, &now) <= 0) - break; - - /* we set the last fired time to delay msec after the previous firing, - then re-insert. timers maintain their order and may trigger more - than once if they've waited more than one delay's worth of time. - */ - timers = g_slist_delete_link(timers, timers); - g_time_val_add(&curr->last, curr->delay); - curr->action(curr, curr->data); - g_time_val_add(&curr->timeout, curr->delay); - insert_timer(curr); - - /* if at least one timer fires, then don't wait on X events, as there - may already be some in the queue from the timer callbacks. - */ - ret_wait.tv_sec = ret_wait.tv_usec = 0; - *wait = &ret_wait; - return; - } - - if (nearest_timeout_wait(&ret_wait)) - *wait = &ret_wait; - else - *wait = NULL; -} diff --git a/openbox/timer.h b/openbox/timer.h deleted file mode 100644 index aa26c986..00000000 --- a/openbox/timer.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef __timer_h -#define __timer_h - -#include - -typedef struct _ObTimer ObTimer; - -/*! Data type of Timer callback */ -typedef void (*ObTimeoutHandler)(ObTimer *t, void *data); - -struct _ObTimer -{ - /*! Microseconds between timer firings */ - long delay; - /*! Callback for timer expiry */ - ObTimeoutHandler action; - /*! Data sent to callback */ - void *data; - /*! We overload the delete operator to just set this to true */ - gboolean del_me; - /*! The time the last fire should've been at */ - GTimeVal last; - /*! When this timer will next trigger */ - GTimeVal timeout; -}; - -/*! Initializes the timer subsection */ -void timer_startup(); -/*! Destroys the timer subsection */ -void timer_shutdown(); - -/* Creates a new timer with a given delay */ -ObTimer *timer_start(long delay, ObTimeoutHandler cb, void *data); -/* Stops and frees a timer */ -void timer_stop(ObTimer *self); - -/*! Dispatch all pending timers. Sets wait to the amount of time to wait for - the next timer, or NULL if there are no timers to wait for */ -void timer_dispatch(GTimeVal **wait); - -#endif