From: Dana Jansens Date: Tue, 18 Mar 2003 08:38:33 +0000 (+0000) Subject: keyboard bindings are functional X-Git-Url: https://git.brokenzipper.com/gitweb?a=commitdiff_plain;h=739c958ac58154f8b1896113701e1c7f580d4cde;p=chaz%2Fopenbox keyboard bindings are functional --- diff --git a/plugins/keyboard/Makefile.am b/plugins/keyboard/Makefile.am index 78ff2ccf..983fd39a 100644 --- a/plugins/keyboard/Makefile.am +++ b/plugins/keyboard/Makefile.am @@ -2,14 +2,14 @@ plugindir=$(libdir)/openbox/plugins CPPFLAGS=$(XFT_CFLAGS) $(GLIB_CFLAGS) @CPPFLAGS@ \ -DPLUGINDIR=\"$(plugindir)\" \ --DG_LOG_DOMAIN=\"Openbox-Plugin\" +-DG_LOG_DOMAIN=\"Plugin-Keyboard\" -plugin_LTLIBRARIES=focus.la +plugin_LTLIBRARIES=keyboard.la -focus_la_LDFLAGS=-module -avoid-version -focus_la_SOURCES=focus.c +keyboard_la_LDFLAGS=-module -avoid-version +keyboard_la_SOURCES=keyboard.c tree.c translate.c keyaction.c -noinst_HEADERS= +noinst_HEADERS=keyboard.h tree.h translate.h keyaction.h MAINTAINERCLEANFILES= Makefile.in diff --git a/plugins/keyboard/keyaction.c b/plugins/keyboard/keyaction.c new file mode 100644 index 00000000..e3b9aca4 --- /dev/null +++ b/plugins/keyboard/keyaction.c @@ -0,0 +1,159 @@ +#include "keyaction.h" +#include + +void keyaction_set_none(KeyAction *a, guint index) +{ + a->type[index] = DataType_Bool; +} + +void keyaction_set_bool(KeyAction *a, guint index, gboolean b) +{ + a->type[index] = DataType_Bool; + a->data[index].b = b; +} + +void keyaction_set_int(KeyAction *a, guint index, int i) +{ + a->type[index] = DataType_Int; + a->data[index].i = i; +} + +void keyaction_set_uint(KeyAction *a, guint index, guint u) +{ + a->type[index] = DataType_Uint; + a->data[index].u = u; +} + +void keyaction_set_string(KeyAction *a, guint index, char *s) +{ + a->type[index] = DataType_String; + a->data[index].s = g_strdup(s); +} + +void keyaction_free(KeyAction *a) +{ + guint i; + + for (i = 0; i < 2; ++i) + if (a->type[i] == DataType_String) + g_free(a->data[i].s); +} + +void keyaction_do(KeyAction *a, Client *c) +{ + switch (a->action) { + case Action_Execute: + g_assert(a->type[0] == DataType_String); + action_execute(a->data[0].s); + break; + case Action_Iconify: + if (c != NULL) action_iconify(c); + break; + case Action_Raise: + if (c != NULL) action_raise(c); + break; + case Action_Lower: + if (c != NULL) action_lower(c); + break; + case Action_Close: + if (c != NULL) action_close(c); + break; + case Action_Shade: + if (c != NULL) action_shade(c); + break; + case Action_Unshade: + if (c != NULL) action_unshade(c); + break; + case Action_ToggleShade: + if (c != NULL) action_toggle_shade(c); + break; + case Action_ToggleOmnipresent: + if (c != NULL) action_toggle_omnipresent(c); + break; + case Action_MoveRelative: + g_assert(a->type[0] == DataType_Int); + g_assert(a->type[1] == DataType_Int); + if (c != NULL) action_move_relative(c, a->data[0].i, a->data[1].i); + break; + case Action_ResizeRelative: + g_assert(a->type[0] == DataType_Int); + g_assert(a->type[1] == DataType_Int); + if (c != NULL) action_resize_relative(c, a->data[0].i, a->data[1].i); + break; + case Action_MaximizeFull: + if (c != NULL) action_maximize_full(c); + break; + case Action_UnmaximizeFull: + if (c != NULL) action_unmaximize_full(c); + break; + case Action_ToggleMaximizeFull: + if (c != NULL) action_toggle_maximize_full(c); + break; + case Action_MaximizeHorz: + if (c != NULL) action_maximize_horz(c); + break; + case Action_UnmaximizeHorz: + if (c != NULL) action_unmaximize_horz(c); + break; + case Action_ToggleMaximizeHorz: + if (c != NULL) action_toggle_maximize_horz(c); + break; + case Action_MaximizeVert: + if (c != NULL) action_maximize_vert(c); + break; + case Action_UnmaximizeVert: + if (c != NULL) action_unmaximize_vert(c); + break; + case Action_ToggleMaximizeVert: + if (c != NULL) action_toggle_maximize_vert(c); + break; + case Action_SendToDesktop: + g_assert(a->type[0] == DataType_Uint); + if (c != NULL) action_send_to_desktop(c, a->data[0].u); + break; + case Action_SendToNextDesktop: + g_assert(a->type[0] == DataType_Bool); + g_assert(a->type[1] == DataType_Bool); + if (c != NULL) action_send_to_next_desktop(c, a->data[0].b, + a->data[1].b); + break; + case Action_SendToPreviousDesktop: + g_assert(a->type[0] == DataType_Bool); + g_assert(a->type[1] == DataType_Bool); + if (c != NULL) action_send_to_previous_desktop(c, a->data[0].b, + a->data[1].b); + break; + case Action_Desktop: + g_assert(a->type[0] == DataType_Uint); + action_desktop(a->data[0].u); + break; + case Action_NextDesktop: + g_assert(a->type[0] == DataType_Bool); + action_next_desktop(a->data[0].b); + break; + case Action_PreviousDesktop: + g_assert(a->type[0] == DataType_Bool); + action_previous_desktop(a->data[0].b); + break; + case Action_NextDesktopColumn: + g_assert(a->type[0] == DataType_Bool); + action_next_desktop_column(a->data[0].b); + break; + case Action_PreviousDesktopColumn: + g_assert(a->type[0] == DataType_Bool); + action_previous_desktop_column(a->data[0].b); + break; + case Action_NextDesktopRow: + g_assert(a->type[0] == DataType_Bool); + action_next_desktop_row(a->data[0].b); + break; + case Action_PreviousDesktopRow: + g_assert(a->type[0] == DataType_Bool); + action_previous_desktop_row(a->data[0].b); + break; + case Action_ToggleDecorations: + if (c != NULL) action_toggle_decorations(c); + break; + } +} + diff --git a/plugins/keyboard/keyaction.h b/plugins/keyboard/keyaction.h new file mode 100644 index 00000000..e4ae977c --- /dev/null +++ b/plugins/keyboard/keyaction.h @@ -0,0 +1,36 @@ +#ifndef __plugin_keyboard_action_h +#define __plugin_keyboard_action_h + +#include "../../kernel/action.h" + +typedef enum { + DataType_Bool, + DataType_Int, + DataType_Uint, + DataType_String +} KeyActionDataType; + +typedef union { + gboolean b; + int i; + guint u; + char *s; +} KeyActionData; + +typedef struct { + Action action; + KeyActionDataType type[2]; + KeyActionData data[2]; +} KeyAction; + +void keyaction_set_none(KeyAction *a, guint index); +void keyaction_set_bool(KeyAction *a, guint index, gboolean bool); +void keyaction_set_int(KeyAction *a, guint index, int i); +void keyaction_set_uint(KeyAction *a, guint index, guint uint); +void keyaction_set_string(KeyAction *a, guint index, char *string); + +void keyaction_free(KeyAction *a); + +void keyaction_do(KeyAction *a, Client *c); + +#endif diff --git a/plugins/keyboard/keyboard.c b/plugins/keyboard/keyboard.c index 3e621849..4a090dc3 100644 --- a/plugins/keyboard/keyboard.c +++ b/plugins/keyboard/keyboard.c @@ -1,7 +1,185 @@ +#include "../../kernel/focus.h" #include "../../kernel/dispatch.h" +#include "../../kernel/openbox.h" +#include "../../kernel/action.h" +#include "tree.h" +#include "keyboard.h" +#include "keyaction.h" +#include + +KeyBindingTree *firstnode; + +static KeyBindingTree *curpos; +static guint reset_key, reset_state; +static gboolean grabbed; + +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; + } + } +} + +static void reset_chains() +{ + /* XXX kill timer */ + curpos = NULL; + if (grabbed) { + grabbed = FALSE; + XUngrabKeyboard(ob_display, CurrentTime); + } +} + +static void clearall() +{ + grab_keys(FALSE); + tree_destroy(firstnode); + firstnode = NULL; + grab_keys(TRUE); +} + +static gboolean bind(GList *keylist, KeyAction *action) +{ + KeyBindingTree *tree, *t; + gboolean conflict; + + if (!(tree = tree_build(keylist))) { + g_warning("invalid binding"); + return FALSE; + } + + t = tree_find(tree, &conflict); + if (conflict) { + g_warning("conflict with binding"); + tree_destroy(tree); + return FALSE; + } + if (t != NULL) { + /* already bound to something */ + g_warning("keychain is already bound"); + tree_destroy(tree); + return FALSE; + } + + /* grab the server here to make sure no key pressed go missed */ + XGrabServer(ob_display); + XSync(ob_display, FALSE); + + grab_keys(FALSE); + + /* set the function */ + t = tree; + while (t->first_child) t = t->first_child; + t->action.action = action->action; + t->action.type[0] = action->type[0]; + t->action.type[1] = action->type[1]; + t->action.data[0] = action->data[0]; + t->action.data[1] = action->data[1]; + + /* assimilate this built tree into the main tree */ + tree_assimilate(tree); /* assimilation destroys/uses the tree */ + + grab_keys(TRUE); + + XUngrabServer(ob_display); + XFlush(ob_display); + + return TRUE; +} static void press(ObEvent *e, void *foo) { + if (e->data.x.e->xkey.keycode == reset_key && + e->data.x.e->xkey.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 == e->data.x.e->xkey.keycode && + p->state == e->data.x.e->xkey.state) { + if (p->first_child != NULL) { /* part of a chain */ + /* XXX TIMER */ + if (!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 { + keyaction_do(&p->action, focus_client); + + XAllowEvents(ob_display, AsyncKeyboard, CurrentTime); + reset_chains(); + } + break; + } + p = p->next_sibling; + } + } +} + +static void binddef() +{ + GList *list = g_list_append(NULL, NULL); + KeyAction a; + + list->data = "C-Right"; + a.action = Action_NextDesktop; + keyaction_set_bool(&a, 0, TRUE); + keyaction_set_none(&a, 1); + bind(list, &a); + + list->data = "C-Left"; + a.action = Action_PreviousDesktop; + keyaction_set_bool(&a, 0, TRUE); + keyaction_set_none(&a, 1); + bind(list, &a); + + list->data = "C-1"; + a.action = Action_Desktop; + keyaction_set_uint(&a, 0, 0); + keyaction_set_none(&a, 1); + bind(list, &a); + + list->data = "C-2"; + a.action = Action_Desktop; + keyaction_set_uint(&a, 0, 1); + keyaction_set_none(&a, 1); + bind(list, &a); + + list->data = "C-3"; + a.action = Action_Desktop; + keyaction_set_uint(&a, 0, 2); + keyaction_set_none(&a, 1); + bind(list, &a); + + list->data = "C-4"; + a.action = Action_Desktop; + keyaction_set_uint(&a, 0, 3); + keyaction_set_none(&a, 1); + bind(list, &a); + + list->data = "C-space"; + a.action = Action_Execute; + keyaction_set_string(&a, 0, "xterm"); + keyaction_set_none(&a, 1); + bind(list, &a); } void plugin_startup() @@ -9,10 +187,12 @@ void plugin_startup() dispatch_register(Event_X_KeyPress, (EventHandler)press, NULL); /* XXX parse config file! */ + binddef(); } void plugin_shutdown() { dispatch_register(0, (EventHandler)press, NULL); + clearall(); } diff --git a/plugins/keyboard/keyboard.h b/plugins/keyboard/keyboard.h new file mode 100644 index 00000000..b183fa38 --- /dev/null +++ b/plugins/keyboard/keyboard.h @@ -0,0 +1,23 @@ +#ifndef __plugin_keyboard_keybaord_h +#define __plugin_keyboard_keybaord_h + +#include "keyaction.h" +#include + +typedef struct KeyBindingTree { + guint state; + guint key; + GList *keylist; + KeyAction action; + + /* 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; + +extern KeyBindingTree *firstnode; + +guint keyboard_translate_modifier(char *str); + +#endif diff --git a/plugins/keyboard/translate.c b/plugins/keyboard/translate.c new file mode 100644 index 00000000..75c077fd --- /dev/null +++ b/plugins/keyboard/translate.c @@ -0,0 +1,61 @@ +#include "../../kernel/openbox.h" +#include "keyboard.h" +#include +#include + +guint keyboard_translate_modifier(char *str) +{ + if (!strcmp("Mod1", str) || !strcmp("A", str)) return Mod1Mask; + else if (!strcmp("Mod2", str)) return Mod2Mask; + else if (!strcmp("Mod3", str)) return Mod3Mask; + else if (!strcmp("Mod4", str) || !strcmp("W", 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; +} + +gboolean translate_key(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 = keyboard_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; +} diff --git a/plugins/keyboard/translate.h b/plugins/keyboard/translate.h new file mode 100644 index 00000000..e0d0bae3 --- /dev/null +++ b/plugins/keyboard/translate.h @@ -0,0 +1,9 @@ +#ifndef __plugin_keyboard_translate_h +#define __plugin_keyboard_translate_h + +#include + +guint translate_modifier(char *str); +gboolean translate_key(char *str, guint *state, guint *keycode); + +#endif diff --git a/plugins/keyboard/tree.c b/plugins/keyboard/tree.c new file mode 100644 index 00000000..b7f51888 --- /dev/null +++ b/plugins/keyboard/tree.c @@ -0,0 +1,111 @@ +#include "keyboard.h" +#include "translate.h" +#include "keyaction.h" +#include + +void tree_destroy(KeyBindingTree *tree) +{ + KeyBindingTree *c; + + while (tree) { + tree_destroy(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); + keyaction_free(&tree->action); + } + g_free(tree); + tree = c; + } +} + +KeyBindingTree *tree_build(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_key(it->data, &ret->state, &ret->key)) { + tree_destroy(ret); + return NULL; + } + } + return ret; +} + +void tree_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 == b->key)) + last->next_sibling = b; + else { + last->first_child = b->first_child; + g_free(b); + } + } +} + +KeyBindingTree *tree_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 */ +} diff --git a/plugins/keyboard/tree.h b/plugins/keyboard/tree.h new file mode 100644 index 00000000..ab84643f --- /dev/null +++ b/plugins/keyboard/tree.h @@ -0,0 +1,12 @@ +#ifndef __plugin_keyboard_tree_h +#define __plugin_keyboard_tree_h + +#include "keyboard.h" +#include + +void tree_destroy(KeyBindingTree *tree); +KeyBindingTree *tree_build(GList *keylist); +void tree_assimilate(KeyBindingTree *node); +KeyBindingTree *tree_find(KeyBindingTree *search, gboolean *conflict); + +#endif