Index: openbox/keytree.h =================================================================== --- openbox/keytree.h (revision 5500) +++ openbox/keytree.h (working copy) @@ -37,7 +37,5 @@ void tree_destroy(KeyBindingTree *tree); KeyBindingTree *tree_build(GList *keylist); -void tree_assimilate(KeyBindingTree *node); -KeyBindingTree *tree_find(KeyBindingTree *search, gboolean *conflict); #endif Index: openbox/keytree.c =================================================================== --- openbox/keytree.c (revision 5500) +++ openbox/keytree.c (working copy) @@ -48,7 +48,7 @@ GList *it; KeyBindingTree *ret = NULL, *p; - if (g_list_length(keylist) <= 0) + if (keylist == NULL) return NULL; /* nothing in the list.. */ for (it = g_list_last(keylist); it; it = g_list_previous(it)) { @@ -70,62 +70,3 @@ } return ret; } - -void tree_assimilate(KeyBindingTree *node) -{ - KeyBindingTree *a, *b, *tmp, *last; - - if (keyboard_firstnode == NULL) { - /* there are no nodes at this level yet */ - keyboard_firstnode = node; - } else { - a = keyboard_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 = keyboard_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 */ -} Index: openbox/keyboard.h =================================================================== --- openbox/keyboard.h (revision 5500) +++ openbox/keyboard.h (working copy) @@ -34,7 +34,7 @@ void keyboard_startup(gboolean reconfig); void keyboard_shutdown(gboolean reconfig); -gboolean keyboard_bind(GList *keylist, ObAction *action); +void keyboard_bind(GList *keylist, ObAction *action); void keyboard_unbind_all(); void keyboard_event(struct _ObClient *client, const XEvent *e); @@ -48,4 +48,7 @@ void keyboard_grab_for_client(struct _ObClient *c, gboolean grab); +void keyboard_chroot(); +void keyboard_unchroot(); + #endif Index: openbox/keyboard.c =================================================================== --- openbox/keyboard.c (revision 5500) +++ openbox/keyboard.c (working copy) @@ -36,6 +36,9 @@ #include KeyBindingTree *keyboard_firstnode; +static KeyBindingTree *curpos; +static GSList *chroots; +static KeyBindingTree *chrootq; typedef struct { guint state; @@ -46,8 +49,6 @@ static GSList *interactive_states; -static KeyBindingTree *curpos; - static void grab_for_window(Window win, gboolean grab) { KeyBindingTree *p; @@ -81,6 +82,13 @@ grab_for_window(((ObClient*)it->data)->window, grab); } +static void set_curpos(KeyBindingTree *newpos) +{ + grab_keys(FALSE); + curpos = newpos; + grab_keys(TRUE); +} + static gboolean chain_timeout(gpointer data) { keyboard_reset_chains(); @@ -92,68 +100,115 @@ { ob_main_loop_timeout_remove(ob_main_loop, chain_timeout); - if (curpos) { - grab_keys(FALSE); - curpos = NULL; - grab_keys(TRUE); - } + if (curpos) + set_curpos(NULL); } +void keyboard_chroot() +{ + if (chrootq == NULL || chroots != NULL && chroots->data == chrootq) return; + chroots = g_slist_prepend(chroots, keyboard_firstnode); + keyboard_firstnode = chrootq; + set_curpos(NULL); +} + +void keyboard_unchroot() +{ + if (chroots == NULL) return; + keyboard_firstnode = chroots->data; + chroots = g_slist_delete_link(chroots, chroots); + set_curpos(NULL); +} + void keyboard_unbind_all() { + /* unchrooting does a lot of unecessary key (un)grabbing, + but it is obvious that it's correct! */ + while (chroots) + keyboard_unchroot(); tree_destroy(keyboard_firstnode); keyboard_firstnode = NULL; grab_keys(FALSE); curpos = NULL; + chrootq = NULL; } -gboolean keyboard_bind(GList *keylist, ObAction *action) +static KeyBindingTree *make_nodes_and_find_leaf(KeyBindingTree *b) { - KeyBindingTree *tree, *t; - gboolean conflict; - gboolean mods = TRUE; + g_assert(b); + g_assert(b->next_sibling == NULL); + if (keyboard_firstnode == NULL) { + keyboard_firstnode = b; + } else { + KeyBindingTree *last; + KeyBindingTree *a = keyboard_firstnode; + do { + last = a; + if (a->state == b->state && a->key == b->key) { + KeyBindingTree *tmp = b; + b = b->first_child; + g_assert(b->next_sibling == NULL); + g_free(tmp); + a = a->first_child; + } else { + a = a->next_sibling; + } + } while (a && b); + g_assert(last); - g_assert(keylist != NULL); - g_assert(action != NULL); - - if (!(tree = tree_build(keylist))) - return FALSE; - - if ((t = tree_find(tree, &conflict)) != NULL) { - /* already bound to something, use the existing tree */ - tree_destroy(tree); - tree = NULL; - } else - t = tree; - - if (conflict) { - g_warning("conflict with binding"); - tree_destroy(tree); - return FALSE; + if (b == NULL) { + /* b is a prefix of something inside a */ + return last; + } else if (last->state == b->state && last->key == b->key) { + /* something inside of a is a prefix of b */ + KeyBindingTree *tmp = b; + g_assert(last->first_child == NULL); + last->first_child = b = b->first_child; + g_free(tmp); + } else { + /* neither of the above are true */ + last->next_sibling = b; + } } - /* find if every key in this chain has modifiers, and also find the - bottom node of the tree */ - while (t->first_child) { - if (!t->state) - mods = FALSE; - t = t->first_child; + while (b->first_child) { + b = b->first_child; + g_assert(b->next_sibling == NULL); } + return b; +} /* when there are no modifiers in the binding, then the action cannot be interactive */ - if (!mods && action->data.any.interactive) { +gboolean can_be_interactive(KeyBindingTree *tree) { + for (; tree; tree = tree->first_child) + if (!tree->state) + return FALSE; + return TRUE; +} + +void keyboard_bind(GList *keylist, ObAction *action) +{ + KeyBindingTree *tree; + + g_assert(keylist != NULL); + g_assert(action != NULL); + + if (!(tree = tree_build(keylist))) + return; + + if (!can_be_interactive(tree) && action->data.any.interactive) { action->data.any.interactive = FALSE; action->data.inter.final = TRUE; } - /* set the action */ - t->actions = g_slist_append(t->actions, action); - /* assimilate this built tree into the main tree. assimilation - destroys/uses the tree */ - if (tree) tree_assimilate(tree); + /* consumes the allocation(s) made in tree_build */ + tree = make_nodes_and_find_leaf(tree); - return TRUE; + /* append the action */ + tree->actions = g_slist_append(tree->actions, action); + + return; } gboolean keyboard_interactive_grab(guint state, ObClient *client, @@ -267,21 +322,23 @@ if (p->key == e->xkey.keycode && p->state == e->xkey.state) { + if (p->actions) { + chrootq = p->first_child; + + action_run_key(p->actions, client, e->xkey.state, + e->xkey.x_root, e->xkey.y_root); + } + if (p->first_child != NULL) { /* part of a chain */ ob_main_loop_timeout_remove(ob_main_loop, chain_timeout); /* 5 second timeout for chains */ - ob_main_loop_timeout_add(ob_main_loop, 5 * G_USEC_PER_SEC, + ob_main_loop_timeout_add(ob_main_loop, 1 * G_USEC_PER_SEC, chain_timeout, NULL, NULL); - grab_keys(FALSE); - curpos = p; - grab_keys(TRUE); + set_curpos(p); } else { - keyboard_reset_chains(); + } - action_run_key(p->actions, client, e->xkey.state, - e->xkey.x_root, e->xkey.y_root); - } break; } p = p->next_sibling; @@ -317,4 +374,3 @@ keyboard_unbind_all(); } - Index: openbox/action.h =================================================================== --- openbox/action.h (revision 5500) +++ openbox/action.h (working copy) @@ -336,5 +336,9 @@ void action_show_desktop(union ActionData *data); /* Any */ void action_unshow_desktop(union ActionData *data); +/* Any */ +void action_chroot(union ActionData *data); +/* Any */ +void action_unchroot(union ActionData *data); #endif Index: openbox/action.c =================================================================== --- openbox/action.c (revision 5500) +++ openbox/action.c (working copy) @@ -876,6 +876,16 @@ setup_action_growtoedge_east }, { + "chroot", + action_chroot, + NULL + }, + { + "unchroot", + action_unchroot, + NULL + }, + { NULL, NULL, NULL @@ -1737,3 +1747,14 @@ { screen_show_desktop(FALSE); } + +void action_chroot(union ActionData *data) +{ + keyboard_chroot(); +} + +void action_unchroot(union ActionData *data) +{ + keyboard_unchroot(); +} +/* action_chroot and action_unchroot: in keyboard.c */