From 452627a51ce38229533dfe5d8eeb877b0918d02c Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Mon, 23 Jul 2007 20:22:28 -0400 Subject: Move the main loop out into the libobt --- Makefile.am | 11 +- obt/display.c | 51 ++++ obt/display.h | 32 +++ obt/instance.c | 75 ------ obt/instance.h | 38 --- obt/mainloop.c | 669 ++++++++++++++++++++++++++++++++++++++++++++++++ obt/mainloop.h | 77 ++++++ openbox/dock.c | 19 +- openbox/event.c | 29 +-- openbox/frame.c | 31 ++- openbox/keyboard.c | 13 +- openbox/mainloop.c | 661 ----------------------------------------------- openbox/mainloop.h | 76 ------ openbox/menu.c | 9 +- openbox/menuframe.c | 23 +- openbox/moveresize.c | 21 +- openbox/openbox.c | 36 ++- openbox/openbox.h | 5 +- openbox/popup.c | 9 +- openbox/startupnotify.c | 25 +- 20 files changed, 940 insertions(+), 970 deletions(-) create mode 100644 obt/display.c create mode 100644 obt/display.h delete mode 100644 obt/instance.c delete mode 100644 obt/instance.h create mode 100644 obt/mainloop.c create mode 100644 obt/mainloop.h delete mode 100644 openbox/mainloop.c delete mode 100644 openbox/mainloop.h diff --git a/Makefile.am b/Makefile.am index 528fc382..b547eaa1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -126,8 +126,10 @@ obt_libobt_la_LIBADD = \ $(XML_LIBS) obt_libobt_la_SOURCES = \ obt/obt.h \ - obt/instance.h \ - obt/instance.c \ + obt/display.h \ + obt/display.c \ + obt/mainloop.h \ + obt/mainloop.c \ obt/util.h ## openbox ## @@ -249,8 +251,6 @@ 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 \ @@ -403,7 +403,8 @@ pubinclude_HEADERS = \ render/theme.h \ parser/parse.h \ obt/obt.h \ - obt/instance.h \ + obt/display.h \ + obt/mainloop.h \ obt/util.h nodist_pubinclude_HEADERS = \ diff --git a/obt/display.c b/obt/display.c new file mode 100644 index 00000000..049b6aef --- /dev/null +++ b/obt/display.c @@ -0,0 +1,51 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/display.c for the Openbox window manager + Copyright (c) 2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "obt/display.h" +#include "obt/util.h" + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_FCNTL_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +Display* obt_display_open(const char *display_name) +{ + gchar *n; + Display *d = NULL; + + n = display_name ? g_strdup(display_name) : NULL; + d = XOpenDisplay(n); + if (d) { + if (fcntl(ConnectionNumber(d), F_SETFD, 1) == -1) + g_message("Failed to set display as close-on-exec"); + } + g_free(n); + + return d; +} + +void obt_display_close(Display *d) +{ + if (d) XCloseDisplay(d); +} diff --git a/obt/display.h b/obt/display.h new file mode 100644 index 00000000..a42630e1 --- /dev/null +++ b/obt/display.h @@ -0,0 +1,32 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/display.h for the Openbox window manager + Copyright (c) 2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __obt_instance_h +#define __obt_instance_h + +#include +#include + +G_BEGIN_DECLS + +Display* obt_display_open(const char *display_name); +void obt_display_close(Display *d); + +G_END_DECLS + +#endif /*__obt_instance_h*/ diff --git a/obt/instance.c b/obt/instance.c deleted file mode 100644 index 850fa1c5..00000000 --- a/obt/instance.c +++ /dev/null @@ -1,75 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- - - obt/instance.c for the Openbox window manager - Copyright (c) 2007 Dana Jansens - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - See the COPYING file for a copy of the GNU General Public License. -*/ - -#include "obt/instance.h" -#include "obt/util.h" - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_FCNTL_H -# include -#endif -#ifdef HAVE_UNISTD_H -# include -#endif - -struct _ObtInstance -{ - gint ref; - Display *d; -}; - -ObtInstance* obt_instance_new(const char *display_name) -{ - gchar *n; - Display *d; - ObtInstance *inst = NULL; - - n = display_name ? g_strdup(display_name) : NULL; - d = XOpenDisplay(n); - if (d) { - if (fcntl(ConnectionNumber(d), F_SETFD, 1) == -1) - g_message("Failed to set display as close-on-exec"); - - inst = g_new(ObtInstance, 1); - inst->ref = 1; - inst->d = d; - } - g_free(n); - - return inst; -} - -void obt_instance_ref(ObtInstance *inst) -{ - ++inst->ref; -} - -void obt_instance_unref(ObtInstance *inst) -{ - if (inst && --inst->ref == 0) { - XCloseDisplay(inst->d); - obt_free0(inst, ObtInstance, 1); - } -} - -Display* obt_display(const ObtInstance *inst) -{ - return inst->d; -} diff --git a/obt/instance.h b/obt/instance.h deleted file mode 100644 index fc0f3981..00000000 --- a/obt/instance.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- - - obt/instance.h for the Openbox window manager - Copyright (c) 2007 Dana Jansens - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - See the COPYING file for a copy of the GNU General Public License. -*/ - -#ifndef __obt_instance_h -#define __obt_instance_h - -#include -#include - -G_BEGIN_DECLS - -typedef struct _ObtInstance ObtInstance; - -/* Instance funcs */ -ObtInstance* obt_instance_new (const char *display_name); -void obt_instance_ref (ObtInstance *inst); -void obt_instance_unref (ObtInstance *inst); - -Display* obt_display (const ObtInstance *inst); - -G_END_DECLS - -#endif /*__obt_instance_h*/ diff --git a/obt/mainloop.c b/obt/mainloop.c new file mode 100644 index 00000000..9797770d --- /dev/null +++ b/obt/mainloop.c @@ -0,0 +1,669 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/mainloop.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "obt/mainloop.h" +#include "obt/util.h" + +#include +#include +#include +#include + +typedef struct _ObtMainLoopTimer ObtMainLoopTimer; +typedef struct _ObtMainLoopSignal ObtMainLoopSignal; +typedef struct _ObtMainLoopSignalHandlerType ObtMainLoopSignalHandlerType; +typedef struct _ObtMainLoopXHandlerType ObtMainLoopXHandlerType; +typedef struct _ObtMainLoopFdHandlerType ObtMainLoopFdHandlerType; + +/* this should be more than the number of possible signals on any + architecture... */ +#define NUM_SIGNALS 99 + +/* all created ObtMainLoops. Used by the signal handler to pass along + signals */ +static GSList *all_loops; + +/* signals are global to all loops */ +static struct { + guint installed; /* a ref count */ + struct sigaction oldact; +} all_signals[NUM_SIGNALS]; + +/* a set of all possible signals */ +static 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(ObtMainLoop *loop, GTimeVal **wait); +static void fd_handler_destroy(gpointer data); + +struct _ObtMainLoop +{ + gint ref; + 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 _ObtMainLoopTimer +{ + gulong delay; + GSourceFunc func; + gpointer data; + GEqualFunc equal; + 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; + + /* Only allow a timer's function to fire once per run through the list, + so that it doesn't get locked in there forever */ + gboolean fired; +}; + +struct _ObtMainLoopSignalHandlerType +{ + ObtMainLoop *loop; + gint signal; + gpointer data; + ObtMainLoopSignalHandler func; + GDestroyNotify destroy; +}; + +struct _ObtMainLoopXHandlerType +{ + ObtMainLoop *loop; + gpointer data; + ObtMainLoopXHandler func; + GDestroyNotify destroy; +}; + +struct _ObtMainLoopFdHandlerType +{ + ObtMainLoop *loop; + gint fd; + gpointer data; + ObtMainLoopFdHandler func; + GDestroyNotify destroy; +}; + +ObtMainLoop *obt_main_loop_new(Display *display) +{ + ObtMainLoop *loop; + + loop = g_new0(ObtMainLoop, 1); + loop->ref = 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 obt_main_loop_ref(ObtMainLoop *loop) +{ + ++loop->ref; +} + +void obt_main_loop_unref(ObtMainLoop *loop) +{ + guint i; + GSList *it, *next; + + if (loop && --loop->ref == 0) { + g_assert(loop->running == FALSE); + + for (it = loop->x_handlers; it; it = next) { + ObtMainLoopXHandlerType *h = it->data; + next = g_slist_next(it); + obt_main_loop_x_remove(loop, h->func); + } + + g_hash_table_destroy(loop->fd_handlers); + + for (it = loop->timers; it; it = g_slist_next(it)) { + ObtMainLoopTimer *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) { + ObtMainLoopSignalHandlerType *h = it->data; + next = g_slist_next(it); + obt_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) { + /* 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--; + } + } + } + + obt_free0(loop, ObtMainLoop, 1); + } +} + +static void fd_handle_foreach(gpointer key, + gpointer value, + gpointer data) +{ + ObtMainLoopFdHandlerType *h = value; + fd_set *set = data; + + if (FD_ISSET(h->fd, set)) + h->func(h->fd, h->data); +} + +void obt_main_loop_run(ObtMainLoop *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)) { + ObtMainLoopSignalHandlerType *h = it->data; + h->func(i, h->data); + } + loop->signals_fired[i]--; + } + } + loop->signal_fired = FALSE; + + sigprocmask(SIG_SETMASK, &oldset, NULL); + } else if (loop->display && XPending(loop->display)) { + do { + XNextEvent(loop->display, &e); + + for (it = loop->x_handlers; it; it = g_slist_next(it)) { + ObtMainLoopXHandlerType *h = it->data; + h->func(&e, h->data); + } + } while (XPending(loop->display) && loop->run); + } 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 obt_main_loop_exit(ObtMainLoop *loop) +{ + loop->run = FALSE; +} + +/*** XEVENT WATCHERS ***/ + +void obt_main_loop_x_add(ObtMainLoop *loop, + ObtMainLoopXHandler handler, + gpointer data, + GDestroyNotify notify) +{ + ObtMainLoopXHandlerType *h; + + h = g_new(ObtMainLoopXHandlerType, 1); + h->loop = loop; + h->func = handler; + h->data = data; + h->destroy = notify; + loop->x_handlers = g_slist_prepend(loop->x_handlers, h); +} + +void obt_main_loop_x_remove(ObtMainLoop *loop, + ObtMainLoopXHandler handler) +{ + GSList *it, *next; + + for (it = loop->x_handlers; it; it = next) { + ObtMainLoopXHandlerType *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, "How are you gentlemen? All your base are" + " belong to us. (Openbox received signal %d)\n", sig); + + /* die with a core dump */ + abort(); + } + + for (it = all_loops; it; it = g_slist_next(it)) { + ObtMainLoop *loop = it->data; + loop->signal_fired = TRUE; + loop->signals_fired[sig]++; + } +} + +void obt_main_loop_signal_add(ObtMainLoop *loop, + gint signal, + ObtMainLoopSignalHandler handler, + gpointer data, + GDestroyNotify notify) +{ + ObtMainLoopSignalHandlerType *h; + + g_return_if_fail(signal < NUM_SIGNALS); + + h = g_new(ObtMainLoopSignalHandlerType, 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 obt_main_loop_signal_remove(ObtMainLoop *loop, + ObtMainLoopSignalHandler handler) +{ + guint i; + GSList *it, *next; + + for (i = 0; i < NUM_SIGNALS; ++i) { + for (it = loop->signal_handlers[i]; it; it = next) { + ObtMainLoopSignalHandlerType *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) +{ + ObtMainLoop *loop = data; + + /* key is the fd */ + loop->fd_max = MAX(loop->fd_max, *(gint*)key); +} + +static void calc_max_fd(ObtMainLoop *loop) +{ + loop->fd_max = loop->fd_x; + + g_hash_table_foreach(loop->fd_handlers, max_fd_func, loop); +} + +void obt_main_loop_fd_add(ObtMainLoop *loop, + gint fd, + ObtMainLoopFdHandler handler, + gpointer data, + GDestroyNotify notify) +{ + ObtMainLoopFdHandlerType *h; + + h = g_new(ObtMainLoopFdHandlerType, 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) +{ + ObtMainLoopFdHandlerType *h = data; + + FD_CLR(h->fd, &h->loop->fd_set); + + if (h->destroy) + h->destroy(h->data); +} + +void obt_main_loop_fd_remove(ObtMainLoop *loop, + gint fd) +{ + g_hash_table_remove(loop->fd_handlers, &fd); +} + +/*** TIMEOUTS ***/ + +#define NEAREST_TIMEOUT(loop) \ + (((ObtMainLoopTimer*)(loop)->timers->data)->timeout) + +static glong timecompare(GTimeVal *a, GTimeVal *b) +{ + glong r; + if ((r = a->tv_sec - b->tv_sec)) return r; + return a->tv_usec - b->tv_usec; +} + +static void insert_timer(ObtMainLoop *loop, ObtMainLoopTimer *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 obt_main_loop_timeout_add(ObtMainLoop *loop, + gulong microseconds, + GSourceFunc handler, + gpointer data, + GEqualFunc cmp, + GDestroyNotify notify) +{ + ObMainLoopTimer *t = g_new(ObMainLoopTimer, 1); + + g_assert(microseconds > 0); /* if it's 0 it'll cause an infinite loop */ + + t->delay = microseconds; + t->func = handler; + t->data = data; + t->equal = cmp; + t->destroy = notify; + t->del_me = FALSE; + g_get_current_time(&loop->now); + t->last = t->timeout = loop->now; + g_time_val_add(&t->timeout, t->delay); + + insert_timer(loop, t); +} + +void obt_main_loop_timeout_remove(ObtMainLoop *loop, + GSourceFunc handler) +{ + GSList *it; + + for (it = loop->timers; it; it = g_slist_next(it)) { + ObtMainLoopTimer *t = it->data; + if (t->func == handler) + t->del_me = TRUE; + } +} + +void obt_main_loop_timeout_remove_data(ObtMainLoop *loop, GSourceFunc handler, + gpointer data, gboolean cancel_dest) +{ + GSList *it; + + for (it = loop->timers; it; it = g_slist_next(it)) { + ObtMainLoopTimer *t = it->data; + if (t->func == handler && t->equal(t->data, data)) { + t->del_me = TRUE; + if (cancel_dest) + t->destroy = NULL; + } + } +} + +/* find the time to wait for the nearest timeout */ +static gboolean nearest_timeout_wait(ObtMainLoop *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(ObtMainLoop *loop, GTimeVal **wait) +{ + GSList *it, *next; + + gboolean fired = FALSE; + + g_get_current_time(&loop->now); + + for (it = loop->timers; it; it = next) { + ObtMainLoopTimer *curr; + + next = g_slist_next(it); + + curr = it->data; + + /* 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, it); + if (curr->destroy) + curr->destroy(curr->data); + 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, it); + 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); + g_free(curr); + } + + /* the timer queue has been shuffled, start from the beginning + (which is the next one to fire) */ + next = loop->timers; + + fired = TRUE; + } + + if (fired) { + /* 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; + } else if (nearest_timeout_wait(loop, &loop->ret_wait)) + *wait = &loop->ret_wait; + else + *wait = NULL; +} diff --git a/obt/mainloop.h b/obt/mainloop.h new file mode 100644 index 00000000..46e19f03 --- /dev/null +++ b/obt/mainloop.h @@ -0,0 +1,77 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/mainloop.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __obt_mainloop_h +#define __obt_mainloop_h + +#include +#include + +typedef struct _ObtMainLoop ObtMainLoop; + +ObtMainLoop *obt_main_loop_new(Display *display); +void obt_main_loop_ref(ObtMainLoop *loop); +void obt_main_loop_unref(ObtMainLoop *loop); + +typedef void (*ObtMainLoopXHandler) (const XEvent *e, gpointer data); + +void obt_main_loop_x_add(ObtMainLoop *loop, + ObtMainLoopXHandler handler, + gpointer data, + GDestroyNotify notify); +void obt_main_loop_x_remove(ObtMainLoop *loop, + ObtMainLoopXHandler handler); + +typedef void (*ObtMainLoopFdHandler) (gint fd, gpointer data); + +void obt_main_loop_fd_add(ObtMainLoop *loop, + gint fd, + ObtMainLoopFdHandler handler, + gpointer data, + GDestroyNotify notify); +void obt_main_loop_fd_remove(ObtMainLoop *loop, + gint fd); + +typedef void (*ObtMainLoopSignalHandler) (gint signal, gpointer data); + +void obt_main_loop_signal_add(ObtMainLoop *loop, + gint signal, + ObtMainLoopSignalHandler handler, + gpointer data, + GDestroyNotify notify); +void obt_main_loop_signal_remove(ObtMainLoop *loop, + ObtMainLoopSignalHandler handler); + +void obt_main_loop_timeout_add(ObtMainLoop *loop, + gulong microseconds, + GSourceFunc handler, + gpointer data, + GEqualFunc cmp, + GDestroyNotify notify); +void obt_main_loop_timeout_remove(ObtMainLoop *loop, + GSourceFunc handler); +void obt_main_loop_timeout_remove_data(ObtMainLoop *loop, + GSourceFunc handler, + gpointer data, + gboolean cancel_dest); + +void obt_main_loop_run(ObtMainLoop *loop); +void obt_main_loop_exit(ObtMainLoop *loop); + +#endif diff --git a/openbox/dock.c b/openbox/dock.c index ed8bed4b..0747f40e 100644 --- a/openbox/dock.c +++ b/openbox/dock.c @@ -19,7 +19,6 @@ #include "debug.h" #include "dock.h" -#include "mainloop.h" #include "screen.h" #include "prop.h" #include "config.h" @@ -630,19 +629,21 @@ void dock_hide(gboolean hide) { if (!hide) { if (dock->hidden && config_dock_hide) { - ob_main_loop_timeout_add(ob_main_loop, - config_dock_show_delay * 1000, - show_timeout, NULL, g_direct_equal, NULL); + obt_main_loop_timeout_add(ob_main_loop, + config_dock_show_delay * 1000, + show_timeout, NULL, + g_direct_equal, NULL); } else if (!dock->hidden && config_dock_hide) { - ob_main_loop_timeout_remove(ob_main_loop, hide_timeout); + obt_main_loop_timeout_remove(ob_main_loop, hide_timeout); } } else { if (!dock->hidden && config_dock_hide) { - ob_main_loop_timeout_add(ob_main_loop, - config_dock_hide_delay * 1000, - hide_timeout, NULL, g_direct_equal, NULL); + obt_main_loop_timeout_add(ob_main_loop, + config_dock_hide_delay * 1000, + hide_timeout, NULL, + g_direct_equal, NULL); } else if (dock->hidden && config_dock_hide) { - ob_main_loop_timeout_remove(ob_main_loop, show_timeout); + obt_main_loop_timeout_remove(ob_main_loop, show_timeout); } } } diff --git a/openbox/event.c b/openbox/event.c index 025f1188..2e7feab7 100644 --- a/openbox/event.c +++ b/openbox/event.c @@ -35,7 +35,6 @@ #include "keyboard.h" #include "modkeys.h" #include "mouse.h" -#include "mainloop.h" #include "focus.h" #include "focus_cycle.h" #include "moveresize.h" @@ -123,9 +122,9 @@ static void ice_watch(IceConn conn, IcePointer data, Bool opening, if (opening) { fd = IceConnectionNumber(conn); - ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL); + obt_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL); } else { - ob_main_loop_fd_remove(ob_main_loop, fd); + obt_main_loop_fd_remove(ob_main_loop, fd); fd = -1; } } @@ -135,7 +134,7 @@ void event_startup(gboolean reconfig) { if (reconfig) return; - ob_main_loop_x_add(ob_main_loop, event_process, NULL, NULL); + obt_main_loop_x_add(ob_main_loop, event_process, NULL, NULL); #ifdef USE_SM IceAddConnectionWatch(ice_watch, NULL); @@ -800,17 +799,17 @@ void event_enter_client(ObClient *client) if (config_focus_delay) { ObFocusDelayData *data; - ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func); + obt_main_loop_timeout_remove(ob_main_loop, focus_delay_func); data = g_new(ObFocusDelayData, 1); data->client = client; data->time = event_curtime; data->serial = event_curserial; - ob_main_loop_timeout_add(ob_main_loop, - config_focus_delay * 1000, - focus_delay_func, - data, focus_delay_cmp, focus_delay_dest); + obt_main_loop_timeout_add(ob_main_loop, + config_focus_delay * 1000, + focus_delay_func, + data, focus_delay_cmp, focus_delay_dest); } else { ObFocusDelayData data; data.client = client; @@ -1000,9 +999,9 @@ static void event_handle_client(ObClient *client, XEvent *e) delay is up */ e->xcrossing.detail != NotifyInferior) { - ob_main_loop_timeout_remove_data(ob_main_loop, - focus_delay_func, - client, FALSE); + obt_main_loop_timeout_remove_data(ob_main_loop, + focus_delay_func, + client, FALSE); } break; default: @@ -1914,15 +1913,15 @@ static gboolean focus_delay_func(gpointer data) static void focus_delay_client_dest(ObClient *client, gpointer data) { - ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, - client, FALSE); + obt_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, + client, FALSE); } void event_halt_focus_delay(void) { /* ignore all enter events up till the event which caused this to occur */ if (event_curserial) event_ignore_enter_range(1, event_curserial); - ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func); + obt_main_loop_timeout_remove(ob_main_loop, focus_delay_func); } gulong event_start_ignore_all_enters(void) diff --git a/openbox/frame.c b/openbox/frame.c index a47c2f06..2952b6c7 100644 --- a/openbox/frame.c +++ b/openbox/frame.c @@ -25,7 +25,6 @@ #include "grab.h" #include "config.h" #include "framerender.h" -#include "mainloop.h" #include "focus_cycle.h" #include "focus_cycle_indicator.h" #include "moveresize.h" @@ -1053,8 +1052,8 @@ void frame_release_client(ObFrame *self) gboolean reparent = TRUE; /* if there was any animation going on, kill it */ - ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify, - self, FALSE); + obt_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify, + self, FALSE); /* check if the app has already reparented its window away */ while (XCheckTypedWindowEvent(ob_display, self->client->window, @@ -1130,7 +1129,7 @@ void frame_release_client(ObFrame *self) g_hash_table_remove(window_map, &self->rgriptop); g_hash_table_remove(window_map, &self->rgripbottom); - ob_main_loop_timeout_remove_data(ob_main_loop, flash_timeout, self, TRUE); + obt_main_loop_timeout_remove_data(ob_main_loop, flash_timeout, self, TRUE); } /* is there anything present between us and the label? */ @@ -1651,12 +1650,12 @@ void frame_flash_start(ObFrame *self) self->flash_on = self->focused; if (!self->flashing) - ob_main_loop_timeout_add(ob_main_loop, - G_USEC_PER_SEC * 0.6, - flash_timeout, - self, - g_direct_equal, - flash_done); + obt_main_loop_timeout_add(ob_main_loop, + G_USEC_PER_SEC * 0.6, + flash_timeout, + self, + g_direct_equal, + flash_done); g_get_current_time(&self->flash_end); g_time_val_add(&self->flash_end, G_USEC_PER_SEC * 5); @@ -1815,12 +1814,12 @@ void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying) } if (new_anim) { - ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify, - self, FALSE); - ob_main_loop_timeout_add(ob_main_loop, - FRAME_ANIMATE_ICONIFY_STEP_TIME, - frame_animate_iconify, self, - g_direct_equal, NULL); + obt_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify, + self, FALSE); + obt_main_loop_timeout_add(ob_main_loop, + FRAME_ANIMATE_ICONIFY_STEP_TIME, + frame_animate_iconify, self, + g_direct_equal, NULL); /* do the first step */ frame_animate_iconify(self); diff --git a/openbox/keyboard.c b/openbox/keyboard.c index 4c570dfb..077262a6 100644 --- a/openbox/keyboard.c +++ b/openbox/keyboard.c @@ -17,7 +17,6 @@ See the COPYING file for a copy of the GNU General Public License. */ -#include "mainloop.h" #include "focus.h" #include "screen.h" #include "frame.h" @@ -221,7 +220,7 @@ void keyboard_event(ObClient *client, const XEvent *e) if (e->xkey.keycode == config_keyboard_reset_keycode && e->xkey.state == config_keyboard_reset_state) { - ob_main_loop_timeout_remove(ob_main_loop, chain_timeout); + obt_main_loop_timeout_remove(ob_main_loop, chain_timeout); keyboard_reset_chains(-1); return; } @@ -239,11 +238,11 @@ void keyboard_event(ObClient *client, const XEvent *e) menu_frame_hide_all(); if (p->first_child != NULL) { /* part of a chain */ - ob_main_loop_timeout_remove(ob_main_loop, chain_timeout); + obt_main_loop_timeout_remove(ob_main_loop, chain_timeout); /* 3 second timeout for chains */ - ob_main_loop_timeout_add(ob_main_loop, 3 * G_USEC_PER_SEC, - chain_timeout, NULL, - g_direct_equal, NULL); + obt_main_loop_timeout_add(ob_main_loop, 3 * G_USEC_PER_SEC, + chain_timeout, NULL, + g_direct_equal, NULL); set_curpos(p); } else if (p->chroot) /* an empty chroot */ set_curpos(p); @@ -280,7 +279,7 @@ void keyboard_startup(gboolean reconfig) void keyboard_shutdown(gboolean reconfig) { - ob_main_loop_timeout_remove(ob_main_loop, chain_timeout); + obt_main_loop_timeout_remove(ob_main_loop, chain_timeout); keyboard_unbind_all(); set_curpos(NULL); diff --git a/openbox/mainloop.c b/openbox/mainloop.c deleted file mode 100644 index b2921207..00000000 --- a/openbox/mainloop.c +++ /dev/null @@ -1,661 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- - - mainloop.c for the Openbox window manager - Copyright (c) 2006 Mikael Magnusson - Copyright (c) 2003-2007 Dana Jansens - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - See the COPYING file for a copy of the GNU General Public License. -*/ - -#include "mainloop.h" -#include "event.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 */ -static struct { - guint installed; /* a ref count */ - struct sigaction oldact; -} all_signals[NUM_SIGNALS]; - -/* a set of all possible signals */ -static 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; - GEqualFunc equal; - 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; - - /* Only allow a timer's function to fire once per run through the list, - so that it doesn't get locked in there forever */ - gboolean fired; -}; - -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) { - /* 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) && loop->run); - } 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, "How are you gentlemen? All your base are" - " belong to us. (Openbox received 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 glong timecompare(GTimeVal *a, GTimeVal *b) -{ - glong r; - if ((r = a->tv_sec - b->tv_sec)) return r; - return a->tv_usec - b->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, - GEqualFunc cmp, - GDestroyNotify notify) -{ - ObMainLoopTimer *t = g_new(ObMainLoopTimer, 1); - - g_assert(microseconds > 0); /* if it's 0 it'll cause an infinite loop */ - - t->delay = microseconds; - t->func = handler; - t->data = data; - t->equal = cmp; - t->destroy = notify; - t->del_me = FALSE; - g_get_current_time(&loop->now); - 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; - } -} - -void ob_main_loop_timeout_remove_data(ObMainLoop *loop, GSourceFunc handler, - gpointer data, gboolean cancel_dest) -{ - GSList *it; - - for (it = loop->timers; it; it = g_slist_next(it)) { - ObMainLoopTimer *t = it->data; - if (t->func == handler && t->equal(t->data, data)) { - t->del_me = TRUE; - if (cancel_dest) - t->destroy = NULL; - } - } -} - -/* 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) -{ - GSList *it, *next; - - gboolean fired = FALSE; - - g_get_current_time(&loop->now); - - for (it = loop->timers; it; it = next) { - ObMainLoopTimer *curr; - - next = g_slist_next(it); - - curr = it->data; - - /* 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, it); - if (curr->destroy) - curr->destroy(curr->data); - 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, it); - 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); - g_free(curr); - } - - /* the timer queue has been shuffled, start from the beginning - (which is the next one to fire) */ - next = loop->timers; - - fired = TRUE; - } - - if (fired) { - /* 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; - } else 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 deleted file mode 100644 index 373528e3..00000000 --- a/openbox/mainloop.h +++ /dev/null @@ -1,76 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- - - mainloop.h for the Openbox window manager - Copyright (c) 2006 Mikael Magnusson - Copyright (c) 2003-2007 Dana Jansens - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - See the COPYING file for a copy of the GNU General Public License. -*/ - -#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, - GEqualFunc cmp, - GDestroyNotify notify); -void ob_main_loop_timeout_remove(ObMainLoop *loop, - GSourceFunc handler); -void ob_main_loop_timeout_remove_data(ObMainLoop *loop, - GSourceFunc handler, - gpointer data, - gboolean cancel_dest); - -void ob_main_loop_run(ObMainLoop *loop); -void ob_main_loop_exit(ObMainLoop *loop); - -#endif diff --git a/openbox/menu.c b/openbox/menu.c index d9426e90..67aff620 100644 --- a/openbox/menu.c +++ b/openbox/menu.c @@ -20,7 +20,6 @@ #include "debug.h" #include "menu.h" #include "openbox.h" -#include "mainloop.h" #include "stacking.h" #include "grab.h" #include "client.h" @@ -457,10 +456,10 @@ void menu_show(gchar *name, gint x, gint y, gboolean mouse, ObClient *client) menu_can_hide = TRUE; else { menu_can_hide = FALSE; - ob_main_loop_timeout_add(ob_main_loop, - config_menu_hide_delay * 1000, - menu_hide_delay_func, - NULL, g_direct_equal, NULL); + obt_main_loop_timeout_add(ob_main_loop, + config_menu_hide_delay * 1000, + menu_hide_delay_func, + NULL, g_direct_equal, NULL); } } } diff --git a/openbox/menuframe.c b/openbox/menuframe.c index 979e834f..18f1a1b7 100644 --- a/openbox/menuframe.c +++ b/openbox/menuframe.c @@ -24,7 +24,6 @@ #include "actions.h" #include "grab.h" #include "openbox.h" -#include "mainloop.h" #include "config.h" #include "render/theme.h" @@ -1063,8 +1062,8 @@ void menu_frame_hide_all(void) if (config_submenu_show_delay) { /* remove any submenu open requests */ - ob_main_loop_timeout_remove(ob_main_loop, - menu_entry_frame_submenu_timeout); + obt_main_loop_timeout_remove(ob_main_loop, + menu_entry_frame_submenu_timeout); } if ((it = g_list_last(menu_frame_visible))) menu_frame_hide(it->data); @@ -1078,8 +1077,8 @@ void menu_frame_hide_all_client(ObClient *client) if (f->client == client) { if (config_submenu_show_delay) { /* remove any submenu open requests */ - ob_main_loop_timeout_remove(ob_main_loop, - menu_entry_frame_submenu_timeout); + obt_main_loop_timeout_remove(ob_main_loop, + menu_entry_frame_submenu_timeout); } menu_frame_hide(f); } @@ -1145,8 +1144,8 @@ void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry, if (config_submenu_show_delay) { /* remove any submenu open requests */ - ob_main_loop_timeout_remove(ob_main_loop, - menu_entry_frame_submenu_timeout); + obt_main_loop_timeout_remove(ob_main_loop, + menu_entry_frame_submenu_timeout); } self->selected = entry; @@ -1162,11 +1161,11 @@ void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry, if (self->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) { if (config_submenu_show_delay && !immediate) { /* initiate a new submenu open request */ - ob_main_loop_timeout_add(ob_main_loop, - config_submenu_show_delay * 1000, - menu_entry_frame_submenu_timeout, - self->selected, g_direct_equal, - NULL); + obt_main_loop_timeout_add(ob_main_loop, + config_submenu_show_delay * 1000, + menu_entry_frame_submenu_timeout, + self->selected, g_direct_equal, + NULL); } else { menu_entry_frame_show_submenu(self->selected); } diff --git a/openbox/moveresize.c b/openbox/moveresize.c index 675cbe9c..4349f748 100644 --- a/openbox/moveresize.c +++ b/openbox/moveresize.c @@ -25,7 +25,6 @@ #include "frame.h" #include "openbox.h" #include "resist.h" -#include "mainloop.h" #include "modkeys.h" #include "popup.h" #include "moveresize.h" @@ -311,7 +310,7 @@ void moveresize_end(gboolean cancel) moveresize_alarm = None; } - ob_main_loop_timeout_remove(ob_main_loop, sync_timeout_func); + obt_main_loop_timeout_remove(ob_main_loop, sync_timeout_func); #endif client_configure(moveresize_client, @@ -396,10 +395,10 @@ static void do_resize(void) waiting_for_sync = TRUE; - ob_main_loop_timeout_remove(ob_main_loop, sync_timeout_func); - ob_main_loop_timeout_add(ob_main_loop, G_USEC_PER_SEC * 2, - sync_timeout_func, - NULL, NULL, NULL); + obt_main_loop_timeout_remove(ob_main_loop, sync_timeout_func); + obt_main_loop_timeout_add(ob_main_loop, G_USEC_PER_SEC * 2, + sync_timeout_func, + NULL, NULL, NULL); } #endif @@ -578,10 +577,10 @@ static void do_edge_warp(gint x, gint y) cancel_edge_warp(); if (dir != (ObDirection)-1) { edge_warp_odd = TRUE; /* switch on the first timeout */ - ob_main_loop_timeout_add(ob_main_loop, - config_mouse_screenedgetime * 1000, - edge_warp_delay_func, - NULL, NULL, NULL); + obt_main_loop_timeout_add(ob_main_loop, + config_mouse_screenedgetime * 1000, + edge_warp_delay_func, + NULL, NULL, NULL); } edge_warp_dir = dir; } @@ -589,7 +588,7 @@ static void do_edge_warp(gint x, gint y) static void cancel_edge_warp(void) { - ob_main_loop_timeout_remove(ob_main_loop, edge_warp_delay_func); + obt_main_loop_timeout_remove(ob_main_loop, edge_warp_delay_func); } static void move_with_keys(gint keycode, gint state) diff --git a/openbox/openbox.c b/openbox/openbox.c index c768bdf4..aa30e9af 100644 --- a/openbox/openbox.c +++ b/openbox/openbox.c @@ -49,7 +49,7 @@ #include "parser/parse.h" #include "render/render.h" #include "render/theme.h" -#include "obt/obt.h" +#include "obt/display.h" #ifdef HAVE_FCNTL_H # include @@ -84,10 +84,9 @@ #include #include -ObtInstance *obt_inst; RrInstance *ob_rr_inst; RrTheme *ob_rr_theme; -ObMainLoop *ob_main_loop; +ObtMainLoop *ob_main_loop; Display *ob_display; gint ob_screen; gboolean ob_replace_wm = FALSE; @@ -145,10 +144,9 @@ gint main(gint argc, gchar **argv) session_startup(argc, argv); } - obt_inst = obt_instance_new(NULL); - if (obt_inst == NULL) + ob_display = obt_display_open(NULL); + if (ob_display == NULL) ob_exit_with_error(_("Failed to open the display from the DISPLAY environment variable.")); - ob_display = obt_display(obt_inst); if (remote_control) { prop_startup(); @@ -158,20 +156,20 @@ gint main(gint argc, gchar **argv) * remote_control = 2 -> restart */ PROP_MSG(RootWindow(ob_display, ob_screen), ob_control, remote_control, 0, 0, 0); - obt_instance_unref(obt_inst); + obt_display_close(ob_display); exit(EXIT_SUCCESS); } - ob_main_loop = ob_main_loop_new(ob_display); + ob_main_loop = obt_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, SIGUSR2, 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); - ob_main_loop_signal_add(ob_main_loop, SIGCHLD, signal_handler, NULL, NULL); + obt_main_loop_signal_add(ob_main_loop, SIGUSR1, signal_handler, NULL,NULL); + obt_main_loop_signal_add(ob_main_loop, SIGUSR2, signal_handler, NULL,NULL); + obt_main_loop_signal_add(ob_main_loop, SIGTERM, signal_handler, NULL,NULL); + obt_main_loop_signal_add(ob_main_loop, SIGINT, signal_handler, NULL,NULL); + obt_main_loop_signal_add(ob_main_loop, SIGHUP, signal_handler, NULL,NULL); + obt_main_loop_signal_add(ob_main_loop, SIGPIPE, signal_handler, NULL,NULL); + obt_main_loop_signal_add(ob_main_loop, SIGCHLD, signal_handler, NULL,NULL); ob_screen = DefaultScreen(ob_display); @@ -346,7 +344,7 @@ gint main(gint argc, gchar **argv) reconfigure = FALSE; state = OB_STATE_RUNNING; - ob_main_loop_run(ob_main_loop); + obt_main_loop_run(ob_main_loop); state = OB_STATE_EXITING; if (!reconfigure) { @@ -385,7 +383,7 @@ gint main(gint argc, gchar **argv) session_shutdown(being_replaced); - obt_instance_unref(obt_inst); + obt_display_close(ob_display); parse_paths_shutdown(); @@ -646,14 +644,14 @@ void ob_reconfigure() void ob_exit(gint code) { exitcode = code; - ob_main_loop_exit(ob_main_loop); + obt_main_loop_exit(ob_main_loop); } void ob_exit_replace() { exitcode = 0; being_replaced = TRUE; - ob_main_loop_exit(ob_main_loop); + obt_main_loop_exit(ob_main_loop); } Cursor ob_cursor(ObCursor cursor) diff --git a/openbox/openbox.h b/openbox/openbox.h index b768fee3..7c38e20f 100644 --- a/openbox/openbox.h +++ b/openbox/openbox.h @@ -23,16 +23,15 @@ #include "render/render.h" #include "render/theme.h" +#include "obt/mainloop.h" #include #include -struct _ObMainLoop; - extern RrInstance *ob_rr_inst; extern RrTheme *ob_rr_theme; -extern struct _ObMainLoop *ob_main_loop; +extern ObtMainLoop *ob_main_loop; /*! The X display */ extern Display *ob_display; diff --git a/openbox/popup.c b/openbox/popup.c index 283348e7..979a896d 100644 --- a/openbox/popup.c +++ b/openbox/popup.c @@ -25,7 +25,6 @@ #include "stacking.h" #include "event.h" #include "screen.h" -#include "mainloop.h" #include "render/render.h" #include "render/theme.h" @@ -292,9 +291,9 @@ void popup_delay_show(ObPopup *self, gulong usec, gchar *text) if (usec) { /* don't kill previous show timers */ if (!self->delay_mapped) { - ob_main_loop_timeout_add(ob_main_loop, usec, - popup_show_timeout, self, - g_direct_equal, NULL); + obt_main_loop_timeout_add(ob_main_loop, usec, + popup_show_timeout, self, + g_direct_equal, NULL); self->delay_mapped = TRUE; } } else { @@ -318,7 +317,7 @@ void popup_hide(ObPopup *self) event_end_ignore_all_enters(ignore_start); } else if (self->delay_mapped) { - ob_main_loop_timeout_remove(ob_main_loop, popup_show_timeout); + obt_main_loop_timeout_remove(ob_main_loop, popup_show_timeout); self->delay_mapped = FALSE; } } diff --git a/openbox/startupnotify.c b/openbox/startupnotify.c index 66bce264..24f2a85c 100644 --- a/openbox/startupnotify.c +++ b/openbox/startupnotify.c @@ -40,7 +40,6 @@ void sn_spawn_cancel() {} #else #include "openbox.h" -#include "mainloop.h" #include "screen.h" #define SN_API_NOT_YET_FROZEN @@ -72,7 +71,7 @@ void sn_startup(gboolean reconfig) sn_event_func, NULL, NULL); sn_launcher = sn_launcher_context_new(sn_display, ob_screen); - ob_main_loop_x_add(ob_main_loop, sn_handler, NULL, NULL); + obt_main_loop_x_add(ob_main_loop, sn_handler, NULL, NULL); } void sn_shutdown(gboolean reconfig) @@ -81,7 +80,7 @@ void sn_shutdown(gboolean reconfig) if (reconfig) return; - ob_main_loop_x_remove(ob_main_loop, sn_handler); + obt_main_loop_x_remove(ob_main_loop, sn_handler); for (it = sn_waits; it; it = g_slist_next(it)) sn_startup_sequence_unref((SnStartupSequence*)it->data); @@ -144,10 +143,10 @@ static void sn_event_func(SnMonitorEvent *ev, gpointer data) sn_waits = g_slist_prepend(sn_waits, seq); /* 20 second timeout for apps to start if the launcher doesn't have a timeout */ - ob_main_loop_timeout_add(ob_main_loop, 20 * G_USEC_PER_SEC, - sn_wait_timeout, seq, - g_direct_equal, - (GDestroyNotify)sn_startup_sequence_unref); + obt_main_loop_timeout_add(ob_main_loop, 20 * G_USEC_PER_SEC, + sn_wait_timeout, seq, + g_direct_equal, + (GDestroyNotify)sn_startup_sequence_unref); change = TRUE; break; case SN_MONITOR_EVENT_CHANGED: @@ -158,8 +157,8 @@ static void sn_event_func(SnMonitorEvent *ev, gpointer data) case SN_MONITOR_EVENT_CANCELED: if ((seq = sequence_find(sn_startup_sequence_get_id(seq)))) { sn_waits = g_slist_remove(sn_waits, seq); - ob_main_loop_timeout_remove_data(ob_main_loop, sn_wait_timeout, - seq, FALSE); + obt_main_loop_timeout_remove_data(ob_main_loop, sn_wait_timeout, + seq, FALSE); change = TRUE; } break; @@ -258,10 +257,10 @@ void sn_setup_spawn_environment(gchar *program, gchar *name, /* 20 second timeout for apps to start */ sn_launcher_context_ref(sn_launcher); - ob_main_loop_timeout_add(ob_main_loop, 20 * G_USEC_PER_SEC, - sn_launch_wait_timeout, sn_launcher, - g_direct_equal, - (GDestroyNotify)sn_launcher_context_unref); + obt_main_loop_timeout_add(ob_main_loop, 20 * G_USEC_PER_SEC, + sn_launch_wait_timeout, sn_launcher, + g_direct_equal, + (GDestroyNotify)sn_launcher_context_unref); putenv(g_strdup_printf("DESKTOP_STARTUP_ID=%s", id)); -- cgit v1.2.3