]> Dogcows Code - chaz/openbox/blobdiff - c/kbind.c
merge the C branch into HEAD
[chaz/openbox] / c / kbind.c
diff --git a/c/kbind.c b/c/kbind.c
new file mode 100644 (file)
index 0000000..399ec83
--- /dev/null
+++ b/c/kbind.c
@@ -0,0 +1,354 @@
+#include "focus.h"
+#include "openbox.h"
+#include "hooks.h"
+#include "kbind.h"
+
+#include <glib.h>
+#ifdef HAVE_STRING_H
+#  include <string.h>
+#endif
+
+typedef struct KeyBindingTree {
+    guint state;
+    guint key;
+    GList *keylist;
+
+    /* the next binding in the tree at the same level */
+    struct KeyBindingTree *next_sibling; 
+    /* the first child of this binding (next binding in a chained sequence).*/
+    struct KeyBindingTree *first_child;
+} KeyBindingTree;
+
+
+static KeyBindingTree *firstnode, *curpos;
+static guint reset_key, reset_state;
+static gboolean grabbed, user_grabbed;
+
+guint kbind_translate_modifier(char *str)
+{
+    if (!strcmp("Mod1", str)) return Mod1Mask;
+    else if (!strcmp("Mod2", str)) return Mod2Mask;
+    else if (!strcmp("Mod3", str)) return Mod3Mask;
+    else if (!strcmp("Mod4", str)) return Mod4Mask;
+    else if (!strcmp("Mod5", str)) return Mod5Mask;
+    else if (!strcmp("C", str)) return ControlMask;
+    else if (!strcmp("S", str)) return ShiftMask;
+    g_warning("Invalid modifier '%s' in binding.", str);
+    return 0;
+}
+
+static gboolean translate(char *str, guint *state, guint *keycode)
+{
+    char **parsed;
+    char *l;
+    int i;
+    gboolean ret = FALSE;
+    KeySym sym;
+
+    parsed = g_strsplit(str, "-", -1);
+    
+    /* first, find the key (last token) */
+    l = NULL;
+    for (i = 0; parsed[i] != NULL; ++i)
+       l = parsed[i];
+    if (l == NULL)
+       goto translation_fail;
+
+    /* figure out the mod mask */
+    *state = 0;
+    for (i = 0; parsed[i] != l; ++i) {
+       guint m = kbind_translate_modifier(parsed[i]);
+       if (!m) goto translation_fail;
+       *state |= m;
+    }
+
+    /* figure out the keycode */
+    sym = XStringToKeysym(l);
+    if (sym == NoSymbol) {
+       g_warning("Invalid key name '%s' in key binding.", l);
+       goto translation_fail;
+    }
+    *keycode = XKeysymToKeycode(ob_display, sym);
+    if (!keycode) {
+       g_warning("Key '%s' does not exist on the display.", l); 
+       goto translation_fail;
+    }
+
+    ret = TRUE;
+
+translation_fail:
+    g_strfreev(parsed);
+    return ret;
+}
+
+static void destroytree(KeyBindingTree *tree)
+{
+    KeyBindingTree *c;
+
+    while (tree) {
+       destroytree(tree->next_sibling);
+       c = tree->first_child;
+       if (c == NULL) {
+           GList *it;
+           for (it = tree->keylist; it != NULL; it = it->next)
+               g_free(it->data);
+           g_list_free(tree->keylist);
+       }
+       g_free(tree);
+       tree = c;
+    }
+}
+
+static KeyBindingTree *buildtree(GList *keylist)
+{
+    GList *it;
+    KeyBindingTree *ret = NULL, *p;
+
+    if (g_list_length(keylist) <= 0)
+       return NULL; /* nothing in the list.. */
+
+    for (it = g_list_last(keylist); it != NULL; it = it->prev) {
+       p = ret;
+       ret = g_new(KeyBindingTree, 1);
+       ret->next_sibling = NULL;
+       if (p == NULL) {
+           GList *it;
+
+           /* this is the first built node, the bottom node of the tree */
+           ret->keylist = g_list_copy(keylist); /* shallow copy */
+           for (it = ret->keylist; it != NULL; it = it->next) /* deep copy */
+               it->data = g_strdup(it->data);
+       }
+       ret->first_child = p;
+       if (!translate(it->data, &ret->state, &ret->key)) {
+           destroytree(ret);
+           return NULL;
+       }
+    }
+    return ret;
+}
+
+static void assimilate(KeyBindingTree *node)
+{
+    KeyBindingTree *a, *b, *tmp, *last;
+
+    if (firstnode == NULL) {
+       /* there are no nodes at this level yet */
+       firstnode = node;
+    } else {
+       a = firstnode;
+       last = a;
+       b = node;
+       while (a) {
+           last = a;
+           if (!(a->state == b->state && a->key == b->key)) {
+               a = a->next_sibling;
+           } else {
+               tmp = b;
+               b = b->first_child;
+               g_free(tmp);
+               a = a->first_child;
+           }
+       }
+       if (!(last->state == b->state && last->key == a->key))
+           last->next_sibling = b;
+       else {
+           last->first_child = b->first_child;
+           g_free(b);
+       }
+    }
+}
+
+KeyBindingTree *find(KeyBindingTree *search, gboolean *conflict)
+{
+    KeyBindingTree *a, *b;
+
+    *conflict = FALSE;
+
+    a = firstnode;
+    b = search;
+    while (a && b) {
+       if (!(a->state == b->state && a->key == b->key)) {
+           a = a->next_sibling;
+       } else {
+           if ((a->first_child == NULL) == (b->first_child == NULL)) {
+               if (a->first_child == NULL) {
+                   /* found it! (return the actual node, not the search's) */
+                   return a;
+               }
+           } else {
+               *conflict = TRUE;
+               return NULL; /* the chain status' don't match (conflict!) */
+           }
+           b = b->first_child;
+           a = a->first_child;
+       }
+    }
+    return NULL; // it just isn't in here
+}
+
+static void grab_keys(gboolean grab)
+{
+    if (!grab) {
+       XUngrabKey(ob_display, AnyKey, AnyModifier, ob_root);
+    } else {
+       KeyBindingTree *p = firstnode;
+       while (p) {
+           XGrabKey(ob_display, p->key, p->state, ob_root, FALSE,
+                    GrabModeAsync, GrabModeSync);
+           p = p->next_sibling;
+       }
+    }
+}
+
+void reset_chains()
+{
+    /* XXX kill timer */
+    curpos = NULL;
+    if (grabbed) {
+       grabbed = FALSE;
+       g_message("reset chains. user: %d", user_grabbed);
+       if (!user_grabbed)
+           XUngrabKeyboard(ob_display, CurrentTime);
+    }
+}
+
+void kbind_fire(guint state, guint key, gboolean press)
+{
+    EventData *data;
+    struct Client *c = focus_client;
+    GQuark context = c != NULL ? g_quark_try_string("client")
+                              : g_quark_try_string("root");
+
+    if (user_grabbed) {
+       data = eventdata_new_key(press ? Key_Press : Key_Release,
+                                context, c, state, key, NULL);
+       g_assert(data != NULL);
+       hooks_fire_keyboard(data);
+       eventdata_free(data);
+    }
+
+    if (key == reset_key && state == reset_state) {
+       reset_chains();
+       XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
+    } else {
+       KeyBindingTree *p;
+       if (curpos == NULL)
+           p = firstnode;
+       else
+           p = curpos->first_child;
+       while (p) {
+           if (p->key == key && p->state == state) {
+               if (p->first_child != NULL) { /* part of a chain */
+                   /* XXX TIMER */
+                   if (!grabbed && !user_grabbed) {
+                       /*grab should never fail because we should have a sync
+                         grab at this point */
+                       XGrabKeyboard(ob_display, ob_root, 0, GrabModeAsync, 
+                                     GrabModeSync, CurrentTime);
+                   }
+                   grabbed = TRUE;
+                   curpos = p;
+                   XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
+               } else {
+                   data = eventdata_new_key(press ? Key_Press : Key_Release,
+                                            context, c, state, key,
+                                            p->keylist);
+                   g_assert(data != NULL);
+                   hooks_fire(data);
+                   eventdata_free(data);
+
+                   XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
+                   reset_chains();
+               }
+               break;
+           }
+           p = p->next_sibling;
+       }
+    }
+}
+
+gboolean kbind_add(GList *keylist)
+{
+    KeyBindingTree *tree, *t;
+    gboolean conflict;
+
+    if (!(tree = buildtree(keylist)))
+       return FALSE; /* invalid binding requested */
+
+    t = find(tree, &conflict);
+    if (conflict) {
+       /* conflicts with another binding */
+       destroytree(tree);
+       return FALSE;
+    }
+
+    if (t != NULL) {
+       /* already bound to something */
+       destroytree(tree);
+    } else {
+       /* grab the server here to make sure no key pressed go missed */
+       XGrabServer(ob_display);
+       XSync(ob_display, FALSE);
+
+       grab_keys(FALSE);
+
+       /* assimilate this built tree into the main tree */
+       assimilate(tree); // assimilation destroys/uses the tree
+
+       grab_keys(TRUE); 
+
+       XUngrabServer(ob_display);
+       XFlush(ob_display);
+    }
+    return TRUE;
+}
+
+void kbind_clearall()
+{
+    grab_keys(FALSE);
+    destroytree(firstnode);
+    firstnode = NULL;
+    grab_keys(TRUE);
+}
+
+void kbind_startup()
+{
+    gboolean b;
+
+    curpos = firstnode = NULL;
+    grabbed = user_grabbed = FALSE;
+
+    b = translate("C-G", &reset_state, &reset_key);
+    g_assert(b);
+}
+
+void kbind_shutdown()
+{
+    if (grabbed || user_grabbed) {
+       grabbed = FALSE;
+       kbind_grab_keyboard(FALSE);
+    }
+    grab_keys(FALSE);
+    destroytree(firstnode);
+    firstnode = NULL;
+}
+
+gboolean kbind_grab_keyboard(gboolean grab)
+{
+    gboolean ret = TRUE;
+
+    if (!grab)
+       g_message("grab_keyboard(false). grabbed: %d", grabbed);
+
+    user_grabbed = grab;
+    if (!grabbed) {
+       if (grab)
+           ret = XGrabKeyboard(ob_display, ob_root, 0, GrabModeAsync, 
+                               GrabModeAsync, CurrentTime) == GrabSuccess;
+       else
+           XUngrabKeyboard(ob_display, CurrentTime);
+    }
+    return ret;
+}
This page took 0.02861 seconds and 4 git commands to generate.