diff options
Diffstat (limited to 'obt')
| -rw-r--r-- | obt/Makefile | 4 | ||||
| -rw-r--r-- | obt/display.c | 152 | ||||
| -rw-r--r-- | obt/display.h | 68 | ||||
| -rw-r--r-- | obt/internal.h | 27 | ||||
| -rw-r--r-- | obt/keyboard.c | 224 | ||||
| -rw-r--r-- | obt/keyboard.h | 70 | ||||
| -rw-r--r-- | obt/mainloop.c | 685 | ||||
| -rw-r--r-- | obt/mainloop.h | 81 | ||||
| -rw-r--r-- | obt/obt-4.0.pc.in | 14 | ||||
| -rw-r--r-- | obt/parse.c | 413 | ||||
| -rw-r--r-- | obt/parse.h | 85 | ||||
| -rw-r--r-- | obt/paths.c | 247 | ||||
| -rw-r--r-- | obt/paths.h | 44 | ||||
| -rw-r--r-- | obt/prop.c | 498 | ||||
| -rw-r--r-- | obt/prop.h | 282 | ||||
| -rw-r--r-- | obt/util.h | 37 | ||||
| -rw-r--r-- | obt/version.h.in | 15 | ||||
| -rw-r--r-- | obt/xevent.c | 134 | ||||
| -rw-r--r-- | obt/xevent.h | 48 |
19 files changed, 3128 insertions, 0 deletions
diff --git a/obt/Makefile b/obt/Makefile new file mode 100644 index 00000000..b90edacf --- /dev/null +++ b/obt/Makefile @@ -0,0 +1,4 @@ +all clean install: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean install diff --git a/obt/display.c b/obt/display.c new file mode 100644 index 00000000..f34fc574 --- /dev/null +++ b/obt/display.c @@ -0,0 +1,152 @@ +/* -*- 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/prop.h" +#include "obt/internal.h" +#include "obt/keyboard.h" + +#ifdef HAVE_STRING_H +# include <string.h> +#endif +#ifdef HAVE_FCNTL_H +# include <fcntl.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +Display* obt_display = NULL; + +gboolean obt_display_error_occured = FALSE; + +gboolean obt_display_extension_xkb = FALSE; +gint obt_display_extension_xkb_basep; +gboolean obt_display_extension_shape = FALSE; +gint obt_display_extension_shape_basep; +gboolean obt_display_extension_xinerama = FALSE; +gint obt_display_extension_xinerama_basep; +gboolean obt_display_extension_randr = FALSE; +gint obt_display_extension_randr_basep; +gboolean obt_display_extension_sync = FALSE; +gint obt_display_extension_sync_basep; + +static gint xerror_handler(Display *d, XErrorEvent *e); + +static gboolean xerror_ignore = FALSE; + +gboolean obt_display_open(const char *display_name) +{ + gchar *n; + Display *d = NULL; + + n = display_name ? g_strdup(display_name) : NULL; + obt_display = d = XOpenDisplay(n); + if (d) { + gint junk; + (void)junk; + + if (fcntl(ConnectionNumber(d), F_SETFD, 1) == -1) + g_message("Failed to set display as close-on-exec"); + XSetErrorHandler(xerror_handler); + + /* read what extensions are present */ +#ifdef XKB + obt_display_extension_xkb = + XkbQueryExtension(d, &junk, + &obt_display_extension_xkb_basep, &junk, + NULL, NULL); + if (!obt_display_extension_xkb) + g_message("XKB extension is not present on the server"); +#endif + +#ifdef SHAPE + obt_display_extension_shape = + XShapeQueryExtension(d, &obt_display_extension_shape_basep, + &junk); + if (!obt_display_extension_shape) + g_message("X Shape extension is not present on the server"); +#endif + +#ifdef XINERAMA + obt_display_extension_xinerama = + XineramaQueryExtension(d, + &obt_display_extension_xinerama_basep, + &junk) && XineramaIsActive(d); + if (!obt_display_extension_xinerama) + g_message("Xinerama extension is not present on the server"); +#endif + +#ifdef XRANDR + obt_display_extension_randr = + XRRQueryExtension(d, &obt_display_extension_randr_basep, + &junk); + if (!obt_display_extension_randr) + g_message("XRandR extension is not present on the server"); +#endif + +#ifdef SYNC + obt_display_extension_sync = + XSyncQueryExtension(d, &obt_display_extension_sync_basep, + &junk) && XSyncInitialize(d, &junk, &junk); + if (!obt_display_extension_sync) + g_message("X Sync extension is not present on the server or is an " + "incompatible version"); +#endif + + obt_prop_startup(); + obt_keyboard_reload(); + } + g_free(n); + + return obt_display != NULL; +} + +void obt_display_close(void) +{ + obt_keyboard_shutdown(); + if (obt_display) XCloseDisplay(obt_display); +} + +static gint xerror_handler(Display *d, XErrorEvent *e) +{ +#ifdef DEBUG + gchar errtxt[128]; + + XGetErrorText(d, e->error_code, errtxt, 127); + if (!xerror_ignore) { + if (e->error_code == BadWindow) + /*g_debug(_("X Error: %s\n"), errtxt)*/; + else + g_error("X Error: %s", errtxt); + } else + g_debug("Ignoring XError code %d '%s'", e->error_code, errtxt); +#else + (void)d; (void)e; +#endif + + obt_display_error_occured = TRUE; + return 0; +} + +void obt_display_ignore_errors(gboolean ignore) +{ + XSync(obt_display, FALSE); + xerror_ignore = ignore; + if (ignore) obt_display_error_occured = FALSE; +} diff --git a/obt/display.h b/obt/display.h new file mode 100644 index 00000000..ff20f9c9 --- /dev/null +++ b/obt/display.h @@ -0,0 +1,68 @@ +/* -*- 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_display_h +#define __obt_display_h + +#include <X11/Xlib.h> +#include <glib.h> + +#include <X11/Xutil.h> /* shape.h uses Region which is in here */ +#ifdef XKB +#include <X11/XKBlib.h> +#endif +#ifdef SHAPE +#include <X11/extensions/shape.h> +#endif +#ifdef XINERAMA +#include <X11/extensions/Xinerama.h> +#endif +#ifdef XRANDR +#include <X11/extensions/Xrandr.h> +#endif +#ifdef SYNC +#include <X11/extensions/sync.h> +#endif + +G_BEGIN_DECLS + +extern gboolean obt_display_error_occured; + +extern gboolean obt_display_extension_xkb; +extern gint obt_display_extension_xkb_basep; +extern gboolean obt_display_extension_shape; +extern gint obt_display_extension_shape_basep; +extern gboolean obt_display_extension_xinerama; +extern gint obt_display_extension_xinerama_basep; +extern gboolean obt_display_extension_randr; +extern gint obt_display_extension_randr_basep; +extern gboolean obt_display_extension_sync; +extern gint obt_display_extension_sync_basep; + +extern Display* obt_display; + +gboolean obt_display_open(const char *display_name); +void obt_display_close(void); + +void obt_display_ignore_errors(gboolean ignore); + +#define obt_root(screen) (RootWindow(obt_display, screen)) + +G_END_DECLS + +#endif /*__obt_display_h*/ diff --git a/obt/internal.h b/obt/internal.h new file mode 100644 index 00000000..818107dd --- /dev/null +++ b/obt/internal.h @@ -0,0 +1,27 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/internal.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_internal_h +#define __obt_internal_h + +void obt_prop_startup(void); + +void obt_keyboard_shutdown(void); + +#endif /* __obt_internal_h */ diff --git a/obt/keyboard.c b/obt/keyboard.c new file mode 100644 index 00000000..699fa8ba --- /dev/null +++ b/obt/keyboard.c @@ -0,0 +1,224 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/keyboard.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/keyboard.h" + +#include <X11/Xlib.h> +#include <X11/keysym.h> + +/* These masks are constants and the modifier keys are bound to them as + anyone sees fit: + ShiftMask (1<<0), LockMask (1<<1), ControlMask (1<<2), Mod1Mask (1<<3), + Mod2Mask (1<<4), Mod3Mask (1<<5), Mod4Mask (1<<6), Mod5Mask (1<<7) +*/ +#define NUM_MASKS 8 +#define ALL_MASKS 0xff /* an or'ing of all 8 keyboard masks */ + +/* Get the bitflag for the n'th modifier mask */ +#define nth_mask(n) (1 << n) + +static void set_modkey_mask(guchar mask, KeySym sym); +void obt_keyboard_shutdown(); + +static XModifierKeymap *modmap; +static KeySym *keymap; +static gint min_keycode, max_keycode, keysyms_per_keycode; +/* This is a bitmask of the different masks for each modifier key */ +static guchar modkeys_keys[OBT_KEYBOARD_NUM_MODKEYS]; + +static gboolean alt_l = FALSE; +static gboolean meta_l = FALSE; +static gboolean super_l = FALSE; +static gboolean hyper_l = FALSE; + +static gboolean started = FALSE; + +void obt_keyboard_reload(void) +{ + gint i, j, k; + + if (started) obt_keyboard_shutdown(); /* free stuff */ + started = TRUE; + + /* reset the keys to not be bound to any masks */ + for (i = 0; i < OBT_KEYBOARD_NUM_MODKEYS; ++i) + modkeys_keys[i] = 0; + + modmap = XGetModifierMapping(obt_display); + g_assert(modmap->max_keypermod > 0); + + XDisplayKeycodes(obt_display, &min_keycode, &max_keycode); + keymap = XGetKeyboardMapping(obt_display, min_keycode, + max_keycode - min_keycode + 1, + &keysyms_per_keycode); + + alt_l = meta_l = super_l = hyper_l = FALSE; + + /* go through each of the modifier masks (eg ShiftMask, CapsMask...) */ + for (i = 0; i < NUM_MASKS; ++i) { + /* go through each keycode that is bound to the mask */ + for (j = 0; j < modmap->max_keypermod; ++j) { + KeySym sym; + /* get a keycode that is bound to the mask (i) */ + KeyCode keycode = modmap->modifiermap[i*modmap->max_keypermod + j]; + if (keycode) { + /* go through each keysym bound to the given keycode */ + for (k = 0; k < keysyms_per_keycode; ++k) { + sym = keymap[(keycode-min_keycode) * keysyms_per_keycode + + k]; + if (sym != NoSymbol) { + /* bind the key to the mask (e.g. Alt_L => Mod1Mask) */ + set_modkey_mask(nth_mask(i), sym); + } + } + } + } + } + + /* CapsLock, Shift, and Control are special and hard-coded */ + modkeys_keys[OBT_KEYBOARD_MODKEY_CAPSLOCK] = LockMask; + modkeys_keys[OBT_KEYBOARD_MODKEY_SHIFT] = ShiftMask; + modkeys_keys[OBT_KEYBOARD_MODKEY_CONTROL] = ControlMask; +} + +void obt_keyboard_shutdown(void) +{ + XFreeModifiermap(modmap); + modmap = NULL; + XFree(keymap); + keymap = NULL; + started = FALSE; +} + +guint obt_keyboard_keycode_to_modmask(guint keycode) +{ + gint i, j; + guint mask = 0; + + if (keycode == NoSymbol) return 0; + + /* go through each of the modifier masks (eg ShiftMask, CapsMask...) */ + for (i = 0; i < NUM_MASKS; ++i) { + /* go through each keycode that is bound to the mask */ + for (j = 0; j < modmap->max_keypermod; ++j) { + /* compare with a keycode that is bound to the mask (i) */ + if (modmap->modifiermap[i*modmap->max_keypermod + j] == keycode) + mask |= nth_mask(i); + } + } + return mask; +} + +guint obt_keyboard_only_modmasks(guint mask) +{ + mask &= ALL_MASKS; + /* strip off these lock keys. they shouldn't affect key bindings */ + mask &= ~LockMask; /* use the LockMask, not what capslock is bound to, + because you could bind it to something else and it + should work as that modifier then. i think capslock + is weird in xkb. */ + mask &= ~obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_NUMLOCK); + mask &= ~obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SCROLLLOCK); + return mask; +} + +guint obt_keyboard_modkey_to_modmask(ObtModkeysKey key) +{ + return modkeys_keys[key]; +} + +static void set_modkey_mask(guchar mask, KeySym sym) +{ + /* find what key this is, and bind it to the mask */ + + if (sym == XK_Num_Lock) + modkeys_keys[OBT_KEYBOARD_MODKEY_NUMLOCK] |= mask; + else if (sym == XK_Scroll_Lock) + modkeys_keys[OBT_KEYBOARD_MODKEY_SCROLLLOCK] |= mask; + + else if (sym == XK_Super_L && super_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_SUPER] |= mask; + else if (sym == XK_Super_L && !super_l) + /* left takes precident over right, so erase any masks the right + key may have set */ + modkeys_keys[OBT_KEYBOARD_MODKEY_SUPER] = mask, super_l = TRUE; + else if (sym == XK_Super_R && !super_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_SUPER] |= mask; + + else if (sym == XK_Hyper_L && hyper_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_HYPER] |= mask; + else if (sym == XK_Hyper_L && !hyper_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_HYPER] = mask, hyper_l = TRUE; + else if (sym == XK_Hyper_R && !hyper_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_HYPER] |= mask; + + else if (sym == XK_Alt_L && alt_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_ALT] |= mask; + else if (sym == XK_Alt_L && !alt_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_ALT] = mask, alt_l = TRUE; + else if (sym == XK_Alt_R && !alt_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_ALT] |= mask; + + else if (sym == XK_Meta_L && meta_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_META] |= mask; + else if (sym == XK_Meta_L && !meta_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_META] = mask, meta_l = TRUE; + else if (sym == XK_Meta_R && !meta_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_META] |= mask; + + /* CapsLock, Shift, and Control are special and hard-coded */ +} + +KeyCode obt_keyboard_keysym_to_keycode(KeySym sym) +{ + gint i, j; + + /* go through each keycode and look for the keysym */ + for (i = min_keycode; i <= max_keycode; ++i) + for (j = 0; j < keysyms_per_keycode; ++j) + if (sym == keymap[(i-min_keycode) * keysyms_per_keycode + j]) + return i; + return 0; +} + +gchar *obt_keyboard_keycode_to_string(guint keycode) +{ + KeySym sym; + + if ((sym = XKeycodeToKeysym(obt_display, keycode, 0)) != NoSymbol) + return g_locale_to_utf8(XKeysymToString(sym), -1, NULL, NULL, NULL); + return NULL; +} + +gunichar obt_keyboard_keycode_to_unichar(guint keycode) +{ + gunichar unikey = 0; + char *key; + + if ((key = obt_keyboard_keycode_to_string(keycode)) != NULL && + /* don't accept keys that aren't a single letter, like "space" */ + key[1] == '\0') + { + unikey = g_utf8_get_char_validated(key, -1); + if (unikey == (gunichar)-1 || unikey == (gunichar)-2 || unikey == 0) + unikey = 0; + } + g_free(key); + return unikey; +} diff --git a/obt/keyboard.h b/obt/keyboard.h new file mode 100644 index 00000000..dd28cb86 --- /dev/null +++ b/obt/keyboard.h @@ -0,0 +1,70 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/keyboard.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_keyboard_h +#define __obt_keyboard_h + +#include <glib.h> +#include <X11/Xlib.h> + +G_BEGIN_DECLS + +/*! These keys are bound to the modifier masks in any fashion, + except for CapsLock, Shift, and Control. */ +typedef enum { + OBT_KEYBOARD_MODKEY_CAPSLOCK, + OBT_KEYBOARD_MODKEY_NUMLOCK, + OBT_KEYBOARD_MODKEY_SCROLLLOCK, + OBT_KEYBOARD_MODKEY_SHIFT, + OBT_KEYBOARD_MODKEY_CONTROL, + OBT_KEYBOARD_MODKEY_SUPER, + OBT_KEYBOARD_MODKEY_HYPER, + OBT_KEYBOARD_MODKEY_META, + OBT_KEYBOARD_MODKEY_ALT, + + OBT_KEYBOARD_NUM_MODKEYS +} ObtModkeysKey; + +void obt_keyboard_reload(void); + +/*! Get the modifier mask(s) for a KeyCode. (eg. a keycode bound to Alt_L could + return a mask of (Mod1Mask | Mask3Mask)) */ +guint obt_keyboard_keycode_to_modmask(guint keycode); + +/*! Strip off all modifiers except for the modifier keys. This strips stuff + like Button1Mask, and also LockMask, NumlockMask, and ScrolllockMask */ +guint obt_keyboard_only_modmasks(guint mask); + +/*! Get the modifier masks for a modifier key. This includes both the left and + right keys when there are both. */ +guint obt_keyboard_modkey_to_modmask(ObtModkeysKey key); + +/*! Convert a KeySym to a KeyCode, because the X function is terrible - says + valgrind. */ +KeyCode obt_keyboard_keysym_to_keycode(KeySym sym); + +/*! Give the string form of a KeyCode */ +gchar *obt_keyboard_keycode_to_string(guint keycode); + +/*! Translate a KeyCode to the unicode character it represents */ +gunichar obt_keyboard_keycode_to_unichar(guint keycode); + + +G_END_DECLS + +#endif /* __obt_keyboard_h */ diff --git a/obt/mainloop.c b/obt/mainloop.c new file mode 100644 index 00000000..691c6875 --- /dev/null +++ b/obt/mainloop.c @@ -0,0 +1,685 @@ +/* -*- 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/display.h" +#include "obt/util.h" + +#include <stdio.h> +#include <stdlib.h> +#include <sys/select.h> +#include <signal.h> + +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); +static void calc_max_fd(ObtMainLoop *loop); + +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(void) +{ + ObtMainLoop *loop; + + loop = g_new0(ObtMainLoop, 1); + loop->ref = 1; + FD_ZERO(&loop->fd_set); + loop->fd_x = -1; + loop->fd_max = -1; + + 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; + + if (!loop->x_handlers) { + g_assert(obt_display); /* is the display open? */ + + loop->display = obt_display; + loop->fd_x = ConnectionNumber(loop->display); + FD_SET(loop->fd_x, &loop->fd_set); + calc_max_fd(loop); + } + + 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); + } + } + + if (!loop->x_handlers) { + FD_CLR(loop->fd_x, &loop->fd_set); + calc_max_fd(loop); + } +} + +/*** 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); + calc_max_fd(loop); +} + +/*** 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)) { + ObtMainLoopTimer *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) +{ + ObtMainLoopTimer *t = g_new(ObtMainLoopTimer, 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..f455d629 --- /dev/null +++ b/obt/mainloop.h @@ -0,0 +1,81 @@ +/* -*- 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 <X11/Xlib.h> +#include <glib.h> + +G_BEGIN_DECLS + +typedef struct _ObtMainLoop ObtMainLoop; + +ObtMainLoop *obt_main_loop_new(void); +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); + +G_END_DECLS + +#endif diff --git a/obt/obt-4.0.pc.in b/obt/obt-4.0.pc.in new file mode 100644 index 00000000..840de161 --- /dev/null +++ b/obt/obt-4.0.pc.in @@ -0,0 +1,14 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +xcflags=@X_CFLAGS@ +xlibs=@X_LIBS@ + +Name: Obt +Description: Openbox Toolkit Library +Version: @OBT_VERSION@ +Requires: glib-2.0 +Libs: -L${libdir} -lobrender ${xlibs} +Cflags: -I${includedir}/openbox/@OBT_VERSION@ ${xcflags} diff --git a/obt/parse.c b/obt/parse.c new file mode 100644 index 00000000..d181b679 --- /dev/null +++ b/obt/parse.c @@ -0,0 +1,413 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/parse.c for the Openbox window manager + 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/parse.h" +#include "obt/paths.h" + +#include <glib.h> + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +struct Callback { + gchar *tag; + ObtParseCallback func; + gpointer data; +}; + +struct _ObtParseInst { + gint ref; + ObtPaths *xdg_paths; + GHashTable *callbacks; + xmlDocPtr doc; + xmlNodePtr root; + gchar *path; +}; + +static void destfunc(struct Callback *c) +{ + g_free(c->tag); + g_free(c); +} + +ObtParseInst* obt_parse_instance_new(void) +{ + ObtParseInst *i = g_new(ObtParseInst, 1); + i->ref = 1; + i->xdg_paths = obt_paths_new(); + i->callbacks = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, + (GDestroyNotify)destfunc); + i->doc = NULL; + i->root = NULL; + i->path = NULL; + return i; +} + +void obt_parse_instance_ref(ObtParseInst *i) +{ + ++i->ref; +} + +void obt_parse_instance_unref(ObtParseInst *i) +{ + if (i && --i->ref == 0) { + obt_paths_unref(i->xdg_paths); + g_hash_table_destroy(i->callbacks); + g_free(i); + } +} + +xmlDocPtr obt_parse_doc(ObtParseInst *i) +{ + g_assert(i->doc); /* a doc is open? */ + return i->doc; +} + +xmlNodePtr obt_parse_root(ObtParseInst *i) +{ + g_assert(i->doc); /* a doc is open? */ + return i->root; +} + +void obt_parse_register(ObtParseInst *i, const gchar *tag, + ObtParseCallback func, gpointer data) +{ + struct Callback *c; + + if ((c = g_hash_table_lookup(i->callbacks, tag))) { + g_error("Tag '%s' already registered", tag); + return; + } + + c = g_new(struct Callback, 1); + c->tag = g_strdup(tag); + c->func = func; + c->data = data; + g_hash_table_insert(i->callbacks, c->tag, c); +} + +static gboolean load_file(ObtParseInst *i, + const gchar *domain, + const gchar *filename, + const gchar *root_node, + GSList *paths) +{ + GSList *it; + gboolean r = FALSE; + + g_assert(i->doc == NULL); /* another doc isn't open already? */ + + for (it = paths; !r && it; it = g_slist_next(it)) { + gchar *path; + struct stat s; + + if (!domain && !filename) /* given a full path to the file */ + path = g_strdup(it->data); + else + path = g_build_filename(it->data, domain, filename, NULL); + + if (stat(path, &s) >= 0) { + /* XML_PARSE_BLANKS is needed apparently, or the tree can end up + with extra nodes in it. */ + i->doc = xmlReadFile(path, NULL, (XML_PARSE_NOBLANKS | + XML_PARSE_RECOVER)); + if (i->doc) { + i->root = xmlDocGetRootElement(i->doc); + if (!i->root) { + xmlFreeDoc(i->doc); + i->doc = NULL; + g_message("%s is an empty XML document", path); + } + else if (xmlStrcmp(i->root->name, + (const xmlChar*)root_node)) { + xmlFreeDoc(i->doc); + i->doc = NULL; + i->root = NULL; + g_message("XML document %s is of wrong type. Root " + "node is not '%s'", path, root_node); + } + else { + i->path = g_strdup(path); + r = TRUE; /* ok! */ + } + } + } + + g_free(path); + } + + return r; +} + +gboolean obt_parse_load_file(ObtParseInst *i, + const gchar *path, + const gchar *root_node) +{ + GSList *paths; + gboolean r; + + paths = g_slist_append(NULL, g_strdup(path)); + + r = load_file(i, NULL, NULL, root_node, paths); + + while (paths) { + g_free(paths->data); + paths = g_slist_delete_link(paths, paths); + } + return r; +} + +gboolean obt_parse_load_config_file(ObtParseInst *i, + const gchar *domain, + const gchar *filename, + const gchar *root_node) +{ + GSList *it, *paths = NULL; + gboolean r; + + for (it = obt_paths_config_dirs(i->xdg_paths); it; it = g_slist_next(it)) + paths = g_slist_append(paths, g_strdup(it->data)); + + r = load_file(i, domain, filename, root_node, paths); + + while (paths) { + g_free(paths->data); + paths = g_slist_delete_link(paths, paths); + } + return r; +} + +gboolean obt_parse_load_data_file(ObtParseInst *i, + const gchar *domain, + const gchar *filename, + const gchar *root_node) +{ + GSList *it, *paths = NULL; + gboolean r; + + for (it = obt_paths_data_dirs(i->xdg_paths); it; it = g_slist_next(it)) + paths = g_slist_append(paths, g_strdup(it->data)); + + r = load_file(i, domain, filename, root_node, paths); + + while (paths) { + g_free(paths->data); + paths = g_slist_delete_link(paths, paths); + } + return r; +} + +gboolean obt_parse_load_theme_file(ObtParseInst *i, + const gchar *theme, + const gchar *domain, + const gchar *filename, + const gchar *root_node) +{ + GSList *it, *paths = NULL; + gboolean r; + + /* use ~/.themes for backwards compatibility */ + paths = g_slist_append + (paths, g_build_filename(g_get_home_dir(), ".themes", theme, NULL)); + + for (it = obt_paths_data_dirs(i->xdg_paths); it; it = g_slist_next(it)) + paths = g_slist_append + (paths, g_build_filename(it->data, "themes", theme, NULL)); + + r = load_file(i, domain, filename, root_node, paths); + + while (paths) { + g_free(paths->data); + paths = g_slist_delete_link(paths, paths); + } + return r; +} + + +gboolean obt_parse_load_mem(ObtParseInst *i, + gpointer data, guint len, const gchar *root_node) +{ + gboolean r = FALSE; + + g_assert(i->doc == NULL); /* another doc isn't open already? */ + + i->doc = xmlParseMemory(data, len); + if (i) { + i->root = xmlDocGetRootElement(i->doc); + if (!i->root) { + xmlFreeDoc(i->doc); + i->doc = NULL; + g_message("Given memory is an empty document"); + } + else if (xmlStrcmp(i->root->name, (const xmlChar*)root_node)) { + xmlFreeDoc(i->doc); + i->doc = NULL; + i->root = NULL; + g_message("XML Document in given memory is of wrong " + "type. Root node is not '%s'\n", root_node); + } + else + r = TRUE; /* ok ! */ + } + return r; +} + +void obt_parse_close(ObtParseInst *i) +{ + if (i && i->doc) { + xmlFreeDoc(i->doc); + g_free(i->path); + i->doc = NULL; + i->root = NULL; + i->path = NULL; + } +} + +void obt_parse_tree(ObtParseInst *i, xmlNodePtr node) +{ + g_assert(i->doc); /* a doc is open? */ + + while (node) { + struct Callback *c = g_hash_table_lookup(i->callbacks, node->name); + if (c) c->func(node, c->data); + node = node->next; + } +} + +void obt_parse_tree_from_root(ObtParseInst *i) +{ + obt_parse_tree(i, i->root->children); +} + +gchar *obt_parse_node_string(xmlNodePtr node) +{ + xmlChar *c = xmlNodeGetContent(node); + gchar *s = g_strdup(c ? (gchar*)c : ""); + xmlFree(c); + return s; +} + +gint obt_parse_node_int(xmlNodePtr node) +{ + xmlChar *c = xmlNodeGetContent(node); + gint i = c ? atoi((gchar*)c) : 0; + xmlFree(c); + return i; +} + +gboolean obt_parse_node_bool(xmlNodePtr node) +{ + xmlChar *c = xmlNodeGetContent(node); + gboolean b = FALSE; + if (c && !xmlStrcasecmp(c, (const xmlChar*) "true")) + b = TRUE; + else if (c && !xmlStrcasecmp(c, (const xmlChar*) "yes")) + b = TRUE; + else if (c && !xmlStrcasecmp(c, (const xmlChar*) "on")) + b = TRUE; + xmlFree(c); + return b; +} + +gboolean obt_parse_node_contains(xmlNodePtr node, const gchar *val) +{ + xmlChar *c = xmlNodeGetContent(node); + gboolean r; + r = !xmlStrcasecmp(c, (const xmlChar*) val); + xmlFree(c); + return r; +} + +xmlNodePtr obt_parse_find_node(xmlNodePtr node, const gchar *tag) +{ + while (node) { + if (!xmlStrcmp(node->name, (const xmlChar*) tag)) + return node; + node = node->next; + } + return NULL; +} + +gboolean obt_parse_attr_bool(xmlNodePtr node, const gchar *name, + gboolean *value) +{ + xmlChar *c = xmlGetProp(node, (const xmlChar*) name); + gboolean r = FALSE; + if (c) { + if (!xmlStrcasecmp(c, (const xmlChar*) "true")) + *value = TRUE, r = TRUE; + else if (!xmlStrcasecmp(c, (const xmlChar*) "yes")) + *value = TRUE, r = TRUE; + else if (!xmlStrcasecmp(c, (const xmlChar*) "on")) + *value = TRUE, r = TRUE; + else if (!xmlStrcasecmp(c, (const xmlChar*) "false")) + *value = FALSE, r = TRUE; + else if (!xmlStrcasecmp(c, (const xmlChar*) "no")) + *value = FALSE, r = TRUE; + else if (!xmlStrcasecmp(c, (const xmlChar*) "off")) + *value = FALSE, r = TRUE; + } + xmlFree(c); + return r; +} + +gboolean obt_parse_attr_int(xmlNodePtr node, const gchar *name, gint *value) +{ + xmlChar *c = xmlGetProp(node, (const xmlChar*) name); + gboolean r = FALSE; + if (c) { + *value = atoi((gchar*)c); + r = TRUE; + } + xmlFree(c); + return r; +} + +gboolean obt_parse_attr_string(xmlNodePtr node, const gchar *name, + gchar **value) +{ + xmlChar *c = xmlGetProp(node, (const xmlChar*) name); + gboolean r = FALSE; + if (c) { + *value = g_strdup((gchar*)c); + r = TRUE; + } + xmlFree(c); + return r; +} + +gboolean obt_parse_attr_contains(xmlNodePtr node, const gchar *name, + const gchar *val) +{ + xmlChar *c = xmlGetProp(node, (const xmlChar*) name); + gboolean r = FALSE; + if (c) + r = !xmlStrcasecmp(c, (const xmlChar*) val); + xmlFree(c); + return r; +} diff --git a/obt/parse.h b/obt/parse.h new file mode 100644 index 00000000..acc3f5c6 --- /dev/null +++ b/obt/parse.h @@ -0,0 +1,85 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/parse.h for the Openbox window manager + 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_parse_h +#define __obt_parse_h + +#include <libxml/parser.h> +#include <glib.h> + +G_BEGIN_DECLS + +typedef struct _ObtParseInst ObtParseInst; + +typedef void (*ObtParseCallback)(xmlNodePtr node, gpointer data); + +ObtParseInst* obt_parse_instance_new(void); +void obt_parse_instance_ref(ObtParseInst *inst); +void obt_parse_instance_unref(ObtParseInst *inst); + +gboolean obt_parse_load_file(ObtParseInst *inst, + const gchar *path, + const gchar *root_node); +gboolean obt_parse_load_config_file(ObtParseInst *inst, + const gchar *domain, + const gchar *filename, + const gchar *root_node); +gboolean obt_parse_load_data_file(ObtParseInst *inst, + const gchar *domain, + const gchar *filename, + const gchar *root_node); +gboolean obt_parse_load_theme_file(ObtParseInst *inst, + const gchar *theme, + const gchar *domain, + const gchar *filename, + const gchar *root_node); +gboolean obt_parse_load_mem(ObtParseInst *inst, + gpointer data, guint len, const gchar *root_node); + +xmlDocPtr obt_parse_doc(ObtParseInst *inst); +xmlNodePtr obt_parse_root(ObtParseInst *inst); + +void obt_parse_close(ObtParseInst *inst); + +void obt_parse_register(ObtParseInst *inst, const gchar *tag, + ObtParseCallback func, gpointer data); +void obt_parse_tree(ObtParseInst *i, xmlNodePtr node); +void obt_parse_tree_from_root(ObtParseInst *i); + + +/* helpers */ + +xmlNodePtr obt_parse_find_node(xmlNodePtr node, const gchar *name); + +gboolean obt_parse_node_contains (xmlNodePtr node, const gchar *val); +gchar *obt_parse_node_string (xmlNodePtr node); +gint obt_parse_node_int (xmlNodePtr node); +gboolean obt_parse_node_bool (xmlNodePtr node); + +gboolean obt_parse_attr_contains (xmlNodePtr node, const gchar *name, + const gchar *val); +gboolean obt_parse_attr_string (xmlNodePtr node, const gchar *name, + gchar **value); +gboolean obt_parse_attr_int (xmlNodePtr node, const gchar *name, + gint *value); +gboolean obt_parse_attr_bool (xmlNodePtr node, const gchar *name, + gboolean *value); + +G_END_DECLS + +#endif diff --git a/obt/paths.c b/obt/paths.c new file mode 100644 index 00000000..61004998 --- /dev/null +++ b/obt/paths.c @@ -0,0 +1,247 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/paths.c for the Openbox window manager + 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/paths.h" +#include "obt/util.h" + +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#ifdef HAVE_STRING_H +# include <string.h> +#endif + +struct _ObtPaths +{ + gint ref; + gchar *config_home; + gchar *data_home; + gchar *cache_home; + GSList *config_dirs; + GSList *data_dirs; +}; + +static gint slist_path_cmp(const gchar *a, const gchar *b) +{ + return strcmp(a, b); +} + +typedef GSList* (*GSListFunc) (gpointer list, gconstpointer data); + +static GSList* slist_path_add(GSList *list, gpointer data, GSListFunc func) +{ + g_assert(func); + + if (!data) + return list; + + if (!g_slist_find_custom(list, data, (GCompareFunc) slist_path_cmp)) + list = func(list, data); + else + g_free(data); + + return list; +} + +static GSList* split_paths(const gchar *paths) +{ + GSList *list = NULL; + gchar **spl, **it; + + if (!paths) + return NULL; + spl = g_strsplit(paths, ":", -1); + for (it = spl; *it; ++it) + list = slist_path_add(list, *it, (GSListFunc) g_slist_append); + g_free(spl); + return list; +} + +ObtPaths* obt_paths_new(void) +{ + ObtPaths *p; + const gchar *path; + + p = g_new0(ObtPaths, 1); + p->ref = 1; + + path = g_getenv("XDG_CONFIG_HOME"); + if (path && path[0] != '\0') /* not unset or empty */ + p->config_home = g_build_filename(path, NULL); + else + p->config_home = g_build_filename(g_get_home_dir(), ".config", NULL); + + path = g_getenv("XDG_DATA_HOME"); + if (path && path[0] != '\0') /* not unset or empty */ + p->data_home = g_build_filename(path, NULL); + else + p->data_home = g_build_filename(g_get_home_dir(), ".local", + "share", NULL); + + path = g_getenv("XDG_CACHE_HOME"); + if (path && path[0] != '\0') /* not unset or empty */ + p->cache_home = g_build_filename(path, NULL); + else + p->cache_home = g_build_filename(g_get_home_dir(), ".cache", NULL); + + path = g_getenv("XDG_CONFIG_DIRS"); + if (path && path[0] != '\0') /* not unset or empty */ + p->config_dirs = split_paths(path); + else { + p->config_dirs = slist_path_add(p->config_dirs, + g_strdup(CONFIGDIR), + (GSListFunc) g_slist_append); + p->config_dirs = slist_path_add(p->config_dirs, + g_build_filename + (G_DIR_SEPARATOR_S, + "etc", "xdg", NULL), + (GSListFunc) g_slist_append); + } + p->config_dirs = slist_path_add(p->config_dirs, + g_strdup(p->config_home), + (GSListFunc) g_slist_prepend); + + path = g_getenv("XDG_DATA_DIRS"); + if (path && path[0] != '\0') /* not unset or empty */ + p->data_dirs = split_paths(path); + else { + p->data_dirs = slist_path_add(p->data_dirs, + g_strdup(DATADIR), + (GSListFunc) g_slist_append); + p->data_dirs = slist_path_add(p->data_dirs, + g_build_filename + (G_DIR_SEPARATOR_S, + "usr", "local", "share", NULL), + (GSListFunc) g_slist_append); + p->data_dirs = slist_path_add(p->data_dirs, + g_build_filename + (G_DIR_SEPARATOR_S, + "usr", "share", NULL), + (GSListFunc) g_slist_append); + } + p->data_dirs = slist_path_add(p->data_dirs, + g_strdup(p->data_home), + (GSListFunc) g_slist_prepend); + return p; +} + +void obt_paths_ref(ObtPaths *p) +{ + ++p->ref; +} + +void obt_paths_unref(ObtPaths *p) +{ + if (p && --p->ref == 0) { + GSList *it; + + for (it = p->config_dirs; it; it = g_slist_next(it)) + g_free(it->data); + g_slist_free(p->config_dirs); + for (it = p->data_dirs; it; it = g_slist_next(it)) + g_free(it->data); + g_slist_free(p->data_dirs); + g_free(p->config_home); + g_free(p->data_home); + g_free(p->cache_home); + + obt_free0(p, ObtPaths, 1); + } +} + +gchar *obt_paths_expand_tilde(const gchar *f) +{ + gchar **spl; + gchar *ret; + + if (!f) + return NULL; + spl = g_strsplit(f, "~", 0); + ret = g_strjoinv(g_get_home_dir(), spl); + g_strfreev(spl); + return ret; +} + +gboolean obt_paths_mkdir(const gchar *path, gint mode) +{ + gboolean ret = TRUE; + + g_return_val_if_fail(path != NULL, FALSE); + g_return_val_if_fail(path[0] != '\0', FALSE); + + if (!g_file_test(path, G_FILE_TEST_IS_DIR)) + if (mkdir(path, mode) == -1) + ret = FALSE; + + return ret; +} + +gboolean obt_paths_mkdir_path(const gchar *path, gint mode) +{ + gboolean ret = TRUE; + + g_return_val_if_fail(path != NULL, FALSE); + g_return_val_if_fail(path[0] == '/', FALSE); + + if (!g_file_test(path, G_FILE_TEST_IS_DIR)) { + gchar *c, *e; + + c = g_strdup(path); + e = c; + while ((e = strchr(e + 1, '/'))) { + *e = '\0'; + if (!(ret = obt_paths_mkdir(c, mode))) + goto parse_mkdir_path_end; + *e = '/'; + } + ret = obt_paths_mkdir(c, mode); + + parse_mkdir_path_end: + g_free(c); + } + + return ret; +} + +const gchar* obt_paths_config_home(ObtPaths *p) +{ + return p->config_home; +} + +const gchar* obt_paths_data_home(ObtPaths *p) +{ + return p->data_home; +} + +const gchar* obt_paths_cache_home(ObtPaths *p) +{ + return p->cache_home; +} + +GSList* obt_paths_config_dirs(ObtPaths *p) +{ + return p->config_dirs; +} + +GSList* obt_paths_data_dirs(ObtPaths *p) +{ + return p->data_dirs; +} diff --git a/obt/paths.h b/obt/paths.h new file mode 100644 index 00000000..8753d4f6 --- /dev/null +++ b/obt/paths.h @@ -0,0 +1,44 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/paths.h for the Openbox window manager + 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_paths_h +#define __obt_paths_h + +#include <glib.h> + +G_BEGIN_DECLS + +typedef struct _ObtPaths ObtPaths; + +ObtPaths* obt_paths_new(void); +void obt_paths_ref(ObtPaths *p); +void obt_paths_unref(ObtPaths *p); + +const gchar* obt_paths_config_home(ObtPaths *p); +const gchar* obt_paths_data_home(ObtPaths *p); +const gchar* obt_paths_cache_home(ObtPaths *p); +GSList* obt_paths_config_dirs(ObtPaths *p); +GSList* obt_paths_data_dirs(ObtPaths *p); + +gchar *obt_paths_expand_tilde(const gchar *f); +gboolean obt_paths_mkdir(const gchar *path, gint mode); +gboolean obt_paths_mkdir_path(const gchar *path, gint mode); + +G_END_DECLS + +#endif diff --git a/obt/prop.c b/obt/prop.c new file mode 100644 index 00000000..185bf537 --- /dev/null +++ b/obt/prop.c @@ -0,0 +1,498 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/prop.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/prop.h" +#include "obt/display.h" + +#include <X11/Xatom.h> +#ifdef HAVE_STRING_H +# include <string.h> +#endif + +Atom prop_atoms[OBT_PROP_NUM_ATOMS]; +gboolean prop_started = FALSE; + +#define CREATE_NAME(var, name) (prop_atoms[OBT_PROP_##var] = \ + XInternAtom((obt_display), (name), FALSE)) +#define CREATE(var) CREATE_NAME(var, #var) +#define CREATE_(var) CREATE_NAME(var, "_" #var) + +void obt_prop_startup(void) +{ + if (prop_started) return; + prop_started = TRUE; + + g_assert(obt_display); + + CREATE(CARDINAL); + CREATE(WINDOW); + CREATE(PIXMAP); + CREATE(ATOM); + CREATE(STRING); + CREATE_NAME(UTF8, "UTF8_STRING"); + + CREATE(MANAGER); + + CREATE(WM_COLORMAP_WINDOWS); + CREATE(WM_PROTOCOLS); + CREATE(WM_STATE); + CREATE(WM_CHANGE_STATE); + CREATE(WM_DELETE_WINDOW); + CREATE(WM_TAKE_FOCUS); + CREATE(WM_NAME); + CREATE(WM_ICON_NAME); + CREATE(WM_CLASS); + CREATE(WM_WINDOW_ROLE); + CREATE(WM_CLIENT_MACHINE); + CREATE(WM_COMMAND); + CREATE(WM_CLIENT_LEADER); + CREATE(WM_TRANSIENT_FOR); + CREATE_(MOTIF_WM_HINTS); + CREATE_(MOTIF_WM_INFO); + + CREATE(SM_CLIENT_ID); + + CREATE_(NET_WM_FULL_PLACEMENT); + + CREATE_(NET_SUPPORTED); + CREATE_(NET_CLIENT_LIST); + CREATE_(NET_CLIENT_LIST_STACKING); + CREATE_(NET_NUMBER_OF_DESKTOPS); + CREATE_(NET_DESKTOP_GEOMETRY); + CREATE_(NET_DESKTOP_VIEWPORT); + CREATE_(NET_CURRENT_DESKTOP); + CREATE_(NET_DESKTOP_NAMES); + CREATE_(NET_ACTIVE_WINDOW); +/* CREATE_(NET_RESTACK_WINDOW);*/ + CREATE_(NET_WORKAREA); + CREATE_(NET_SUPPORTING_WM_CHECK); + CREATE_(NET_DESKTOP_LAYOUT); + CREATE_(NET_SHOWING_DESKTOP); + + CREATE_(NET_CLOSE_WINDOW); + CREATE_(NET_WM_MOVERESIZE); + CREATE_(NET_MOVERESIZE_WINDOW); + CREATE_(NET_REQUEST_FRAME_EXTENTS); + CREATE_(NET_RESTACK_WINDOW); + + CREATE_(NET_STARTUP_ID); + + CREATE_(NET_WM_NAME); + CREATE_(NET_WM_VISIBLE_NAME); + CREATE_(NET_WM_ICON_NAME); + CREATE_(NET_WM_VISIBLE_ICON_NAME); + CREATE_(NET_WM_DESKTOP); + CREATE_(NET_WM_WINDOW_TYPE); + CREATE_(NET_WM_STATE); + CREATE_(NET_WM_STRUT); + CREATE_(NET_WM_STRUT_PARTIAL); + CREATE_(NET_WM_ICON); + CREATE_(NET_WM_ICON_GEOMETRY); + CREATE_(NET_WM_PID); + CREATE_(NET_WM_ALLOWED_ACTIONS); + CREATE_(NET_WM_USER_TIME); +/* CREATE_(NET_WM_USER_TIME_WINDOW); */ + CREATE_(KDE_NET_WM_FRAME_STRUT); + CREATE_(NET_FRAME_EXTENTS); + + CREATE_(NET_WM_PING); +#ifdef SYNC + CREATE_(NET_WM_SYNC_REQUEST); + CREATE_(NET_WM_SYNC_REQUEST_COUNTER); +#endif + + CREATE_(NET_WM_WINDOW_TYPE_DESKTOP); + CREATE_(NET_WM_WINDOW_TYPE_DOCK); + CREATE_(NET_WM_WINDOW_TYPE_TOOLBAR); + CREATE_(NET_WM_WINDOW_TYPE_MENU); + CREATE_(NET_WM_WINDOW_TYPE_UTILITY); + CREATE_(NET_WM_WINDOW_TYPE_SPLASH); + CREATE_(NET_WM_WINDOW_TYPE_DIALOG); + CREATE_(NET_WM_WINDOW_TYPE_NORMAL); + CREATE_(NET_WM_WINDOW_TYPE_POPUP_MENU); + + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOPLEFT] = 0; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOP] = 1; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOPRIGHT] = 2; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_RIGHT] = 3; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT] = 4; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOM] = 5; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT] = 6; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_LEFT] = 7; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_MOVE] = 8; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_KEYBOARD] = 9; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_MOVE_KEYBOARD] = 10; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_CANCEL] = 11; + + CREATE_(NET_WM_ACTION_MOVE); + CREATE_(NET_WM_ACTION_RESIZE); + CREATE_(NET_WM_ACTION_MINIMIZE); + CREATE_(NET_WM_ACTION_SHADE); + CREATE_(NET_WM_ACTION_MAXIMIZE_HORZ); + CREATE_(NET_WM_ACTION_MAXIMIZE_VERT); + CREATE_(NET_WM_ACTION_FULLSCREEN); + CREATE_(NET_WM_ACTION_CHANGE_DESKTOP); + CREATE_(NET_WM_ACTION_CLOSE); + CREATE_(NET_WM_ACTION_ABOVE); + CREATE_(NET_WM_ACTION_BELOW); + + CREATE_(NET_WM_STATE_MODAL); +/* CREATE_(NET_WM_STATE_STICKY);*/ + CREATE_(NET_WM_STATE_MAXIMIZED_VERT); + CREATE_(NET_WM_STATE_MAXIMIZED_HORZ); + CREATE_(NET_WM_STATE_SHADED); + CREATE_(NET_WM_STATE_SKIP_TASKBAR); + CREATE_(NET_WM_STATE_SKIP_PAGER); + CREATE_(NET_WM_STATE_HIDDEN); + CREATE_(NET_WM_STATE_FULLSCREEN); + CREATE_(NET_WM_STATE_ABOVE); + CREATE_(NET_WM_STATE_BELOW); + CREATE_(NET_WM_STATE_DEMANDS_ATTENTION); + + prop_atoms[OBT_PROP_NET_WM_STATE_ADD] = 1; + prop_atoms[OBT_PROP_NET_WM_STATE_REMOVE] = 0; + prop_atoms[OBT_PROP_NET_WM_STATE_TOGGLE] = 2; + + prop_atoms[OBT_PROP_NET_WM_ORIENTATION_HORZ] = 0; + prop_atoms[OBT_PROP_NET_WM_ORIENTATION_VERT] = 1; + prop_atoms[OBT_PROP_NET_WM_TOPLEFT] = 0; + prop_atoms[OBT_PROP_NET_WM_TOPRIGHT] = 1; + prop_atoms[OBT_PROP_NET_WM_BOTTOMRIGHT] = 2; + prop_atoms[OBT_PROP_NET_WM_BOTTOMLEFT] = 3; + + CREATE_(KDE_WM_CHANGE_STATE); + CREATE_(KDE_NET_WM_WINDOW_TYPE_OVERRIDE); + +/* + CREATE_NAME(ROOTPMAPId, "_XROOTPMAP_ID"); + CREATE_NAME(ESETROOTId, "ESETROOT_PMAP_ID"); +*/ + + CREATE_(OPENBOX_PID); + CREATE_(OB_THEME); + CREATE_(OB_CONFIG_FILE); + CREATE_(OB_WM_ACTION_UNDECORATE); + CREATE_(OB_WM_STATE_UNDECORATED); + CREATE_(OB_CONTROL); +} + +Atom obt_prop_atom(ObtPropAtom a) +{ + g_assert(prop_started); + g_assert(a < OBT_PROP_NUM_ATOMS); + return prop_atoms[a]; +} + +static gboolean get_prealloc(Window win, Atom prop, Atom type, gint size, + guchar *data, gulong num) +{ + gboolean ret = FALSE; + gint res; + guchar *xdata = NULL; + Atom ret_type; + gint ret_size; + gulong ret_items, bytes_left; + glong num32 = 32 / size * num; /* num in 32-bit elements */ + + res = XGetWindowProperty(obt_display, win, prop, 0l, num32, + FALSE, type, &ret_type, &ret_size, + &ret_items, &bytes_left, &xdata); + if (res == Success && ret_items && xdata) { + if (ret_size == size && ret_items >= num) { + guint i; + for (i = 0; i < num; ++i) + switch (size) { + case 8: + data[i] = xdata[i]; + break; + case 16: + ((guint16*)data)[i] = ((gushort*)xdata)[i]; + break; + case 32: + ((guint32*)data)[i] = ((gulong*)xdata)[i]; + break; + default: + g_assert_not_reached(); /* unhandled size */ + } + ret = TRUE; + } + XFree(xdata); + } + return ret; +} + +static gboolean get_all(Window win, Atom prop, Atom type, gint size, + guchar **data, guint *num) +{ + gboolean ret = FALSE; + gint res; + guchar *xdata = NULL; + Atom ret_type; + gint ret_size; + gulong ret_items, bytes_left; + + res = XGetWindowProperty(obt_display, win, prop, 0l, G_MAXLONG, + FALSE, type, &ret_type, &ret_size, + &ret_items, &bytes_left, &xdata); + if (res == Success) { + if (ret_size == size && ret_items > 0) { + guint i; + + *data = g_malloc(ret_items * (size / 8)); + for (i = 0; i < ret_items; ++i) + switch (size) { + case 8: + (*data)[i] = xdata[i]; + break; + case 16: + ((guint16*)*data)[i] = ((gushort*)xdata)[i]; + break; + case 32: + ((guint32*)*data)[i] = ((gulong*)xdata)[i]; + break; + default: + g_assert_not_reached(); /* unhandled size */ + } + *num = ret_items; + ret = TRUE; + } + XFree(xdata); + } + return ret; +} + +static gboolean get_stringlist(Window win, Atom prop, gchar ***list, gint *nstr) +{ + XTextProperty tprop; + gboolean ret = FALSE; + + if (XGetTextProperty(obt_display, win, &tprop, prop) && tprop.nitems) { + if (XTextPropertyToStringList(&tprop, list, nstr)) + ret = TRUE; + XFree(tprop.value); + } + return ret; +} + +gboolean obt_prop_get32(Window win, Atom prop, Atom type, guint32 *ret) +{ + return get_prealloc(win, prop, type, 32, (guchar*)ret, 1); +} + +gboolean obt_prop_get_array32(Window win, Atom prop, Atom type, guint32 **ret, + guint *nret) +{ + return get_all(win, prop, type, 32, (guchar**)ret, nret); +} + +gboolean obt_prop_get_string_locale(Window win, Atom prop, gchar **ret) +{ + gchar **list; + gint nstr; + gchar *s; + + if (get_stringlist(win, prop, &list, &nstr) && nstr) { + s = g_locale_to_utf8(list[0], -1, NULL, NULL, NULL); + XFreeStringList(list); + if (s) { + *ret = s; + return TRUE; + } + } + return FALSE; +} + +gboolean obt_prop_get_strings_locale(Window win, Atom prop, gchar ***ret) +{ + GSList *strs = NULL, *it; + gchar *raw, *p; + guint num, i, count = 0; + + if (get_all(win, prop, OBT_PROP_ATOM(STRING), 8, + (guchar**)&raw, &num)) + { + p = raw; + while (p < raw + num) { + ++count; + strs = g_slist_append(strs, p); + p += strlen(p) + 1; /* next string */ + } + + *ret = g_new0(gchar*, count + 1); + (*ret)[count] = NULL; /* null terminated list */ + + for (i = 0, it = strs; it; ++i, it = g_slist_next(it)) { + (*ret)[i] = g_locale_to_utf8(it->data, -1, NULL, NULL, NULL); + /* make sure translation did not fail */ + if (!(*ret)[i]) + (*ret)[i] = g_strdup(""); + } + g_free(raw); + g_slist_free(strs); + return TRUE; + } + return FALSE; +} + +gboolean obt_prop_get_string_utf8(Window win, Atom prop, gchar **ret) +{ + gchar *raw; + gchar *str; + guint num; + + if (get_all(win, prop, OBT_PROP_ATOM(UTF8), 8, + (guchar**)&raw, &num)) + { + str = g_strndup(raw, num); /* grab the first string from the list */ + g_free(raw); + if (g_utf8_validate(str, -1, NULL)) { + *ret = str; + return TRUE; + } + g_free(str); + } + return FALSE; +} + +gboolean obt_prop_get_strings_utf8(Window win, Atom prop, gchar ***ret) +{ + GSList *strs = NULL, *it; + gchar *raw, *p; + guint num, i, count = 0; + + if (get_all(win, prop, OBT_PROP_ATOM(UTF8), 8, + (guchar**)&raw, &num)) + { + p = raw; + while (p < raw + num) { + ++count; + strs = g_slist_append(strs, p); + p += strlen(p) + 1; /* next string */ + } + + *ret = g_new0(gchar*, count + 1); + + for (i = 0, it = strs; it; ++i, it = g_slist_next(it)) { + if (g_utf8_validate(it->data, -1, NULL)) + (*ret)[i] = g_strdup(it->data); + else + (*ret)[i] = g_strdup(""); + } + g_free(raw); + g_slist_free(strs); + return TRUE; + } + return FALSE; +} + +void obt_prop_set32(Window win, Atom prop, Atom type, gulong val) +{ + XChangeProperty(obt_display, win, prop, type, 32, PropModeReplace, + (guchar*)&val, 1); +} + +void obt_prop_set_array32(Window win, Atom prop, Atom type, gulong *val, + guint num) +{ + XChangeProperty(obt_display, win, prop, type, 32, PropModeReplace, + (guchar*)val, num); +} + +void obt_prop_set_string_locale(Window win, Atom prop, const gchar *val) +{ + gchar const *s[2] = { val, NULL }; + obt_prop_set_strings_locale(win, prop, s); +} + +void obt_prop_set_strings_locale(Window win, Atom prop, const gchar **strs) +{ + gint i, count; + gchar **lstrs; + XTextProperty tprop; + + /* count the strings in strs, and convert them to the locale format */ + for (count = 0; strs[count]; ++count); + lstrs = g_new0(char*, count); + for (i = 0; i < count; ++i) { + lstrs[i] = g_locale_from_utf8(strs[i], -1, NULL, NULL, NULL); + if (!lstrs[i]) { + lstrs[i] = g_strdup(""); /* make it an empty string */ + g_warning("Unable to translate string '%s' from UTF8 to locale " + "format", strs[i]); + } + } + + + XStringListToTextProperty(lstrs, count, &tprop); + XSetTextProperty(obt_display, win, &tprop, prop); + XFree(tprop.value); +} + +void obt_prop_set_string_utf8(Window win, Atom prop, const gchar *val) +{ + XChangeProperty(obt_display, win, prop, OBT_PROP_ATOM(UTF8), 8, + PropModeReplace, (const guchar*)val, strlen(val)); +} + +void obt_prop_set_strings_utf8(Window win, Atom prop, const gchar **strs) +{ + GString *str; + gchar const **s; + + str = g_string_sized_new(0); + for (s = strs; *s; ++s) { + str = g_string_append(str, *s); + str = g_string_append_c(str, '\0'); + } + XChangeProperty(obt_display, win, prop, obt_prop_atom(OBT_PROP_UTF8), 8, + PropModeReplace, (guchar*)str->str, str->len); + g_string_free(str, TRUE); +} + +void obt_prop_erase(Window win, Atom prop) +{ + XDeleteProperty(obt_display, win, prop); +} + +void obt_prop_message(gint screen, Window about, Atom messagetype, + glong data0, glong data1, glong data2, glong data3, + glong data4, glong mask) +{ + obt_prop_message_to(obt_root(screen), about, messagetype, + data0, data1, data2, data3, data4, mask); +} + +void obt_prop_message_to(Window to, Window about, + Atom messagetype, + glong data0, glong data1, glong data2, glong data3, + glong data4, glong mask) +{ + XEvent ce; + ce.xclient.type = ClientMessage; + ce.xclient.message_type = messagetype; + ce.xclient.display = obt_display; + ce.xclient.window = about; + ce.xclient.format = 32; + ce.xclient.data.l[0] = data0; + ce.xclient.data.l[1] = data1; + ce.xclient.data.l[2] = data2; + ce.xclient.data.l[3] = data3; + ce.xclient.data.l[4] = data4; + XSendEvent(obt_display, to, FALSE, mask, &ce); +} diff --git a/obt/prop.h b/obt/prop.h new file mode 100644 index 00000000..ae22b6fd --- /dev/null +++ b/obt/prop.h @@ -0,0 +1,282 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/prop.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_prop_h +#define __obt_prop_h + +#include <X11/Xlib.h> +#include <glib.h> + +G_BEGIN_DECLS + +typedef enum { + /* types */ + OBT_PROP_CARDINAL, /*!< The atom which represents the Cardinal data type */ + OBT_PROP_WINDOW, /*!< The atom which represents window ids */ + OBT_PROP_PIXMAP, /*!< The atom which represents pixmap ids */ + OBT_PROP_ATOM, /*!< The atom which represents atom values */ + OBT_PROP_STRING, /*!< The atom which represents ascii strings */ + OBT_PROP_UTF8, /*!< The atom which represents utf8-encoded strings */ + + /* selection stuff */ + OBT_PROP_MANAGER, + + /* window hints */ + OBT_PROP_WM_COLORMAP_WINDOWS, + OBT_PROP_WM_PROTOCOLS, + OBT_PROP_WM_STATE, + OBT_PROP_WM_DELETE_WINDOW, + OBT_PROP_WM_TAKE_FOCUS, + OBT_PROP_WM_CHANGE_STATE, + OBT_PROP_WM_NAME, + OBT_PROP_WM_ICON_NAME, + OBT_PROP_WM_CLASS, + OBT_PROP_WM_WINDOW_ROLE, + OBT_PROP_WM_CLIENT_MACHINE, + OBT_PROP_WM_COMMAND, + OBT_PROP_WM_CLIENT_LEADER, + OBT_PROP_WM_TRANSIENT_FOR, + OBT_PROP_MOTIF_WM_HINTS, + OBT_PROP_MOTIF_WM_INFO, + + /* SM atoms */ + OBT_PROP_SM_CLIENT_ID, + + /* NETWM atoms */ + + /* Atoms that are used inside messages - these don't go in net_supported */ + + OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOPLEFT, + OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOP, + OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOPRIGHT, + OBT_PROP_NET_WM_MOVERESIZE_SIZE_RIGHT, + OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT, + OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOM, + OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT, + OBT_PROP_NET_WM_MOVERESIZE_SIZE_LEFT, + OBT_PROP_NET_WM_MOVERESIZE_MOVE, + OBT_PROP_NET_WM_MOVERESIZE_SIZE_KEYBOARD, + OBT_PROP_NET_WM_MOVERESIZE_MOVE_KEYBOARD, + OBT_PROP_NET_WM_MOVERESIZE_CANCEL, + + OBT_PROP_NET_WM_STATE_ADD, + OBT_PROP_NET_WM_STATE_REMOVE, + OBT_PROP_NET_WM_STATE_TOGGLE, + + OBT_PROP_NET_WM_ORIENTATION_HORZ, + OBT_PROP_NET_WM_ORIENTATION_VERT, + OBT_PROP_NET_WM_TOPLEFT, + OBT_PROP_NET_WM_TOPRIGHT, + OBT_PROP_NET_WM_BOTTOMRIGHT, + OBT_PROP_NET_WM_BOTTOMLEFT, + + OBT_PROP_NET_WM_WINDOW_TYPE_POPUP_MENU, + + OBT_PROP_PRIVATE_PADDING1, + OBT_PROP_PRIVATE_PADDING2, + OBT_PROP_PRIVATE_PADDING3, + OBT_PROP_PRIVATE_PADDING4, + OBT_PROP_PRIVATE_PADDING5, + OBT_PROP_PRIVATE_PADDING6, + OBT_PROP_PRIVATE_PADDING7, + OBT_PROP_PRIVATE_PADDING8, + OBT_PROP_PRIVATE_PADDING9, + OBT_PROP_PRIVATE_PADDING10, + OBT_PROP_PRIVATE_PADDING11, + OBT_PROP_PRIVATE_PADDING12, + + /* Everything below here must go in net_supported on the root window */ + + /* root window properties */ + OBT_PROP_NET_SUPPORTED, + OBT_PROP_NET_CLIENT_LIST, + OBT_PROP_NET_CLIENT_LIST_STACKING, + OBT_PROP_NET_NUMBER_OF_DESKTOPS, + OBT_PROP_NET_DESKTOP_GEOMETRY, + OBT_PROP_NET_DESKTOP_VIEWPORT, + OBT_PROP_NET_CURRENT_DESKTOP, + OBT_PROP_NET_DESKTOP_NAMES, + OBT_PROP_NET_ACTIVE_WINDOW, +/* Atom net_restack_window;*/ + OBT_PROP_NET_WORKAREA, + OBT_PROP_NET_SUPPORTING_WM_CHECK, + OBT_PROP_NET_DESKTOP_LAYOUT, + OBT_PROP_NET_SHOWING_DESKTOP, + + /* root window messages */ + OBT_PROP_NET_CLOSE_WINDOW, + OBT_PROP_NET_WM_MOVERESIZE, + OBT_PROP_NET_MOVERESIZE_WINDOW, + OBT_PROP_NET_REQUEST_FRAME_EXTENTS, + OBT_PROP_NET_RESTACK_WINDOW, + + /* helpful hints to apps that aren't used for anything */ + OBT_PROP_NET_WM_FULL_PLACEMENT, + + /* startup-notification extension */ + OBT_PROP_NET_STARTUP_ID, + + /* application window properties */ + OBT_PROP_NET_WM_NAME, + OBT_PROP_NET_WM_VISIBLE_NAME, + OBT_PROP_NET_WM_ICON_NAME, + OBT_PROP_NET_WM_VISIBLE_ICON_NAME, + OBT_PROP_NET_WM_DESKTOP, + OBT_PROP_NET_WM_WINDOW_TYPE, + OBT_PROP_NET_WM_STATE, + OBT_PROP_NET_WM_STRUT, + OBT_PROP_NET_WM_STRUT_PARTIAL, + OBT_PROP_NET_WM_ICON, + OBT_PROP_NET_WM_ICON_GEOMETRY, + OBT_PROP_NET_WM_PID, + OBT_PROP_NET_WM_ALLOWED_ACTIONS, + OBT_PROP_NET_WM_USER_TIME, +/* OBT_PROP_NET_WM_USER_TIME_WINDOW, */ + OBT_PROP_NET_FRAME_EXTENTS, + + /* application protocols */ + OBT_PROP_NET_WM_PING, +#ifdef SYNC + OBT_PROP_NET_WM_SYNC_REQUEST, + OBT_PROP_NET_WM_SYNC_REQUEST_COUNTER, +#endif + + OBT_PROP_NET_WM_WINDOW_TYPE_DESKTOP, + OBT_PROP_NET_WM_WINDOW_TYPE_DOCK, + OBT_PROP_NET_WM_WINDOW_TYPE_TOOLBAR, + OBT_PROP_NET_WM_WINDOW_TYPE_MENU, + OBT_PROP_NET_WM_WINDOW_TYPE_UTILITY, + OBT_PROP_NET_WM_WINDOW_TYPE_SPLASH, + OBT_PROP_NET_WM_WINDOW_TYPE_DIALOG, + OBT_PROP_NET_WM_WINDOW_TYPE_NORMAL, + + OBT_PROP_NET_WM_ACTION_MOVE, + OBT_PROP_NET_WM_ACTION_RESIZE, + OBT_PROP_NET_WM_ACTION_MINIMIZE, + OBT_PROP_NET_WM_ACTION_SHADE, +/* OBT_PROP_NET_WM_ACTION_STICK,*/ + OBT_PROP_NET_WM_ACTION_MAXIMIZE_HORZ, + OBT_PROP_NET_WM_ACTION_MAXIMIZE_VERT, + OBT_PROP_NET_WM_ACTION_FULLSCREEN, + OBT_PROP_NET_WM_ACTION_CHANGE_DESKTOP, + OBT_PROP_NET_WM_ACTION_CLOSE, + OBT_PROP_NET_WM_ACTION_ABOVE, + OBT_PROP_NET_WM_ACTION_BELOW, + + OBT_PROP_NET_WM_STATE_MODAL, +/* OBT_PROP_NET_WM_STATE_STICKY,*/ + OBT_PROP_NET_WM_STATE_MAXIMIZED_VERT, + OBT_PROP_NET_WM_STATE_MAXIMIZED_HORZ, + OBT_PROP_NET_WM_STATE_SHADED, + OBT_PROP_NET_WM_STATE_SKIP_TASKBAR, + OBT_PROP_NET_WM_STATE_SKIP_PAGER, + OBT_PROP_NET_WM_STATE_HIDDEN, + OBT_PROP_NET_WM_STATE_FULLSCREEN, + OBT_PROP_NET_WM_STATE_ABOVE, + OBT_PROP_NET_WM_STATE_BELOW, + OBT_PROP_NET_WM_STATE_DEMANDS_ATTENTION, + + /* KDE atoms */ + + OBT_PROP_KDE_WM_CHANGE_STATE, + OBT_PROP_KDE_NET_WM_FRAME_STRUT, + OBT_PROP_KDE_NET_WM_WINDOW_TYPE_OVERRIDE, + +/* + OBT_PROP_ROOTPMAPID, + OBT_PROP_ESETROOTID, +*/ + + /* Openbox specific atoms */ + + OBT_PROP_OB_WM_ACTION_UNDECORATE, + OBT_PROP_OB_WM_STATE_UNDECORATED, + OBT_PROP_OPENBOX_PID, /* this is depreecated in favour of ob_control */ + OBT_PROP_OB_THEME, + OBT_PROP_OB_CONFIG_FILE, + OBT_PROP_OB_CONTROL, + + OBT_PROP_NUM_ATOMS +} ObtPropAtom; + +Atom obt_prop_atom(ObtPropAtom a); + +gboolean obt_prop_get32(Window win, Atom prop, Atom type, guint32 *ret); +gboolean obt_prop_get_array32(Window win, Atom prop, Atom type, guint32 **ret, + guint *nret); +gboolean obt_prop_get_string_locale(Window win, Atom prop, gchar **ret); +gboolean obt_prop_get_string_utf8(Window win, Atom prop, gchar **ret); +gboolean obt_prop_get_strings_locale(Window win, Atom prop, gchar ***ret); +gboolean obt_prop_get_strings_utf8(Window win, Atom prop, gchar ***ret); + +void obt_prop_set32(Window win, Atom prop, Atom type, gulong val); +void obt_prop_set_array32(Window win, Atom prop, Atom type, gulong *val, + guint num); +void obt_prop_set_string_locale(Window win, Atom prop, const gchar *val); +void obt_prop_set_string_utf8(Window win, Atom prop, const gchar *val); +void obt_prop_set_strings_locale(Window win, Atom prop, const gchar **strs); +void obt_prop_set_strings_utf8(Window win, Atom prop, const gchar **strs); + +void obt_prop_erase(Window win, Atom prop); + +void obt_prop_message(gint screen, Window about, Atom messagetype, + glong data0, glong data1, glong data2, glong data3, + glong data4, glong mask); +void obt_prop_message_to(Window to, Window about, Atom messagetype, + glong data0, glong data1, glong data2, glong data3, + glong data4, glong mask); + +#define OBT_PROP_ATOM(prop) obt_prop_atom(OBT_PROP_##prop) + +#define OBT_PROP_GET32(win, prop, type, ret) \ + (obt_prop_get32(win, OBT_PROP_ATOM(prop), OBT_PROP_ATOM(type), ret)) +#define OBT_PROP_GETA32(win, prop, type, ret, nret) \ + (obt_prop_get_array32(win, OBT_PROP_ATOM(prop), OBT_PROP_ATOM(type), \ + ret, nret)) +#define OBT_PROP_GETS(win, prop, type, ret) \ + (obt_prop_get_string_##type(win, OBT_PROP_ATOM(prop), ret)) +#define OBT_PROP_GETSS(win, prop, type, ret) \ + (obt_prop_get_strings_##type(win, OBT_PROP_ATOM(prop), ret)) + +#define OBT_PROP_SET32(win, prop, type, val) \ + (obt_prop_set32(win, OBT_PROP_ATOM(prop), OBT_PROP_ATOM(type), val)) +#define OBT_PROP_SETA32(win, prop, type, val, num) \ + (obt_prop_set_array32(win, OBT_PROP_ATOM(prop), OBT_PROP_ATOM(type), \ + val, num)) +#define OBT_PROP_SETS(win, prop, type, val) \ + (obt_prop_set_string_##type(win, OBT_PROP_ATOM(prop), val)) +#define OBT_PROP_SETSS(win, prop, type, strs) \ + (obt_prop_set_strings_##type(win, OBT_PROP_ATOM(prop), strs)) + +#define OBT_PROP_ERASE(win, prop) (obt_prop_erase(win, OBT_PROP_ATOM(prop))) + +#define OBT_PROP_MSG(screen, about, msgtype, data0, data1, data2, data3, \ + data4) \ + (obt_prop_message(screen, about, OBT_PROP_ATOM(msgtype), \ + data0, data1, data2, data3, data4, \ + SubstructureNotifyMask | SubstructureRedirectMask)) + +#define OBT_PROP_MSG_TO(to, about, msgtype, data0, data1, data2, data3, \ + data4, mask) \ + (obt_prop_message_to(to, about, OBT_PROP_ATOM(msgtype), \ + data0, data1, data2, data3, data4, mask)) + +G_END_DECLS + +#endif /* __obt_prop_h */ diff --git a/obt/util.h b/obt/util.h new file mode 100644 index 00000000..ff44d364 --- /dev/null +++ b/obt/util.h @@ -0,0 +1,37 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/util.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_util_h +#define __obt_util_h + +#include <glib.h> + +#ifdef HAVE_STRING_H +# include <string.h> /* for memset() */ +#endif + +G_BEGIN_DECLS + +/* Util funcs */ +#define obt_free g_free +#define obt_free0(p, type, num) memset((p), 0, sizeof(type) * (num)), g_free(p) + +G_END_DECLS + + +#endif /*__obt_util_h*/ diff --git a/obt/version.h.in b/obt/version.h.in new file mode 100644 index 00000000..8adfcf8d --- /dev/null +++ b/obt/version.h.in @@ -0,0 +1,15 @@ +#ifndef obt__version_h +#define obt__version_h + +#define OBT_MAJOR_VERSION @OBT_MAJOR_VERSION@ +#define OBT_MINOR_VERSION @OBT_MINOR_VERSION@ +#define OBT_MICRO_VERSION @OBT_MICRO_VERSION@ +#define OBT_VERSION OBT_MAJOR_VERSION.OBT_MINOR_VERSION.OBT_MICRO_VERSION + +#define OBT_CHECK_VERSION(major,minor,micro) \ + (OBT_MAJOR_VERSION > (major) || \ + (OBT_MAJOR_VERSION == (major) && OBT_MINOR_VERSION > (minor)) || \ + (OBT_MAJOR_VERSION == (major) && OBT_MINOR_VERSION == (minor) && \ + OBT_MICRO_VERSION >= (micro))) + +#endif diff --git a/obt/xevent.c b/obt/xevent.c new file mode 100644 index 00000000..1cc32a94 --- /dev/null +++ b/obt/xevent.c @@ -0,0 +1,134 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/xevent.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/xevent.h" +#include "obt/mainloop.h" +#include "obt/util.h" + +typedef struct _ObtXEventBinding ObtXEventBinding; + +struct _ObtXEventHandler +{ + gint ref; + ObtMainLoop *loop; + + /* An array of hash tables where the key is the window, and the value is + the ObtXEventBinding */ + GHashTable **bindings; + gint num_event_types; /* the length of the bindings array */ +}; + +struct _ObtXEventBinding +{ + Window win; + ObtXEventCallback func; + gpointer data; +}; + +static void xevent_handler(const XEvent *e, gpointer data); +static guint window_hash(Window *w) { return *w; } +static gboolean window_comp(Window *w1, Window *w2) { return *w1 == *w2; } + +ObtXEventHandler* xevent_new(void) +{ + ObtXEventHandler *h; + + h = g_new0(ObtXEventHandler, 1); + h->ref = 1; + + return h; +} + +void xevent_ref(ObtXEventHandler *h) +{ + ++h->ref; +} + +void xevent_unref(ObtXEventHandler *h) +{ + if (h && --h->ref == 0) { + gint i; + + if (h->loop) + obt_main_loop_x_remove(h->loop, xevent_handler); + for (i = 0; i < h->num_event_types; ++i) + g_hash_table_destroy(h->bindings[i]); + g_free(h->bindings); + + obt_free0(h, ObtXEventHandler, 1); + } +} + +void xevent_register(ObtXEventHandler *h, ObtMainLoop *loop) +{ + h->loop = loop; + obt_main_loop_x_add(loop, xevent_handler, h, NULL); +} + +void xevent_set_handler(ObtXEventHandler *h, gint type, Window win, + ObtXEventCallback func, gpointer data) +{ + ObtXEventBinding *b; + + g_assert(func); + + /* make sure we have a spot for the event */ + if (type + 1 < h->num_event_types) { + gint i; + h->bindings = g_renew(GHashTable*, h->bindings, type + 1); + for (i = h->num_event_types; i < type + 1; ++i) + h->bindings[i] = g_hash_table_new_full((GHashFunc)window_hash, + (GEqualFunc)window_comp, + NULL, g_free); + h->num_event_types = type + 1; + } + + b = g_new(ObtXEventBinding, 1); + b->win = win; + b->func = func; + b->data = data; + g_hash_table_replace(h->bindings[type], &b->win, b); +} + +void xevent_remove_handler(ObtXEventHandler *h, gint type, Window win) +{ + g_assert(type < h->num_event_types); + g_assert(win); + + g_hash_table_remove(h->bindings[type], &win); +} + +static void xevent_handler(const XEvent *e, gpointer data) +{ + ObtXEventHandler *h; + ObtXEventBinding *b; + + h = data; + + if (e->type < h->num_event_types) { + const gint all = OBT_XEVENT_ALL_WINDOWS; + /* run the all_windows handler first */ + b = g_hash_table_lookup(h->bindings[e->xany.type], &all); + if (b) b->func(e, b->data); + /* then run the per-window handler */ + b = g_hash_table_lookup(h->bindings[e->xany.type], &e->xany.window); + if (b) b->func(e, b->data); + } + else + g_message("Unhandled X Event type %d", e->xany.type); +} diff --git a/obt/xevent.h b/obt/xevent.h new file mode 100644 index 00000000..ec0b66e5 --- /dev/null +++ b/obt/xevent.h @@ -0,0 +1,48 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/xevent.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_xevent_h +#define __obt_xevent_h + +#include <X11/Xlib.h> +#include <glib.h> + +G_BEGIN_DECLS + +struct _ObtMainLoop; + +typedef struct _ObtXEventHandler ObtXEventHandler; + +typedef void (*ObtXEventCallback) (const XEvent *e, gpointer data); + +ObtXEventHandler* xevent_new(void); +void xevent_ref(ObtXEventHandler *h); +void xevent_unref(ObtXEventHandler *h); + +void xevent_register(ObtXEventHandler *h, + struct _ObtMainLoop *loop); + +#define OBT_XEVENT_ALL_WINDOWS None + +void xevent_set_handler(ObtXEventHandler *h, gint type, Window win, + ObtXEventCallback func, gpointer data); +void xevent_remove_handler(ObtXEventHandler *h, gint type, Window win); + +G_END_DECLS + +#endif /*__obt_xevent_h*/ |
