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
--- /dev/null
+#include "keyaction.h"
+#include <glib.h>
+
+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;
+ }
+}
+
--- /dev/null
+#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
+#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 <glib.h>
+
+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()
dispatch_register(Event_X_KeyPress, (EventHandler)press, NULL);
/* XXX parse config file! */
+ binddef();
}
void plugin_shutdown()
{
dispatch_register(0, (EventHandler)press, NULL);
+ clearall();
}
--- /dev/null
+#ifndef __plugin_keyboard_keybaord_h
+#define __plugin_keyboard_keybaord_h
+
+#include "keyaction.h"
+#include <glib.h>
+
+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
--- /dev/null
+#include "../../kernel/openbox.h"
+#include "keyboard.h"
+#include <glib.h>
+#include <string.h>
+
+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;
+}
--- /dev/null
+#ifndef __plugin_keyboard_translate_h
+#define __plugin_keyboard_translate_h
+
+#include <glib.h>
+
+guint translate_modifier(char *str);
+gboolean translate_key(char *str, guint *state, guint *keycode);
+
+#endif
--- /dev/null
+#include "keyboard.h"
+#include "translate.h"
+#include "keyaction.h"
+#include <glib.h>
+
+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 */
+}
--- /dev/null
+#ifndef __plugin_keyboard_tree_h
+#define __plugin_keyboard_tree_h
+
+#include "keyboard.h"
+#include <glib.h>
+
+void tree_destroy(KeyBindingTree *tree);
+KeyBindingTree *tree_build(GList *keylist);
+void tree_assimilate(KeyBindingTree *node);
+KeyBindingTree *tree_find(KeyBindingTree *search, gboolean *conflict);
+
+#endif