X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=c%2Fkbind.c;fp=c%2Fkbind.c;h=399ec83003a4c1f423ea203cd4819e4ebe01c133;hb=f8a47de5ec444c452093371e3db16857eb39a490;hp=0000000000000000000000000000000000000000;hpb=8ba0586bcbdc7fe9648f1063812126d71a041670;p=chaz%2Fopenbox diff --git a/c/kbind.c b/c/kbind.c new file mode 100644 index 00000000..399ec830 --- /dev/null +++ b/c/kbind.c @@ -0,0 +1,354 @@ +#include "focus.h" +#include "openbox.h" +#include "hooks.h" +#include "kbind.h" + +#include +#ifdef HAVE_STRING_H +# include +#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; +}