keyboard bindings are functional
[dana/openbox.git] / plugins / keyboard / keyboard.c
1 #include "../../kernel/focus.h"
2 #include "../../kernel/dispatch.h"
3 #include "../../kernel/openbox.h"
4 #include "../../kernel/action.h"
5 #include "tree.h"
6 #include "keyboard.h"
7 #include "keyaction.h"
8 #include <glib.h>
9
10 KeyBindingTree *firstnode;
11
12 static KeyBindingTree *curpos;
13 static guint reset_key, reset_state;
14 static gboolean grabbed;
15
16 static void grab_keys(gboolean grab)
17 {
18     if (!grab) {
19         XUngrabKey(ob_display, AnyKey, AnyModifier, ob_root);
20     } else {
21         KeyBindingTree *p = firstnode;
22         while (p) {
23             XGrabKey(ob_display, p->key, p->state, ob_root, FALSE,
24                      GrabModeAsync, GrabModeSync);
25             p = p->next_sibling;
26         }
27     }
28 }
29
30 static void reset_chains()
31 {
32     /* XXX kill timer */
33     curpos = NULL;
34     if (grabbed) {
35         grabbed = FALSE;
36         XUngrabKeyboard(ob_display, CurrentTime);
37     }
38 }
39
40 static void clearall()
41 {
42     grab_keys(FALSE);
43     tree_destroy(firstnode);
44     firstnode = NULL;
45     grab_keys(TRUE);
46 }
47
48 static gboolean bind(GList *keylist, KeyAction *action)
49 {
50     KeyBindingTree *tree, *t;
51     gboolean conflict;
52
53     if (!(tree = tree_build(keylist))) {
54         g_warning("invalid binding");
55         return FALSE;
56     }
57
58     t = tree_find(tree, &conflict);
59     if (conflict) {
60         g_warning("conflict with binding");
61         tree_destroy(tree);
62         return FALSE;
63     }
64     if (t != NULL) {
65         /* already bound to something */
66         g_warning("keychain is already bound");
67         tree_destroy(tree);
68         return FALSE;
69     }
70
71     /* grab the server here to make sure no key pressed go missed */
72     XGrabServer(ob_display);
73     XSync(ob_display, FALSE);
74
75     grab_keys(FALSE);
76
77     /* set the function */
78     t = tree;
79     while (t->first_child) t = t->first_child;
80     t->action.action = action->action;
81     t->action.type[0] = action->type[0];
82     t->action.type[1] = action->type[1];
83     t->action.data[0] = action->data[0];
84     t->action.data[1] = action->data[1];
85
86     /* assimilate this built tree into the main tree */
87     tree_assimilate(tree); /* assimilation destroys/uses the tree */
88
89     grab_keys(TRUE); 
90
91     XUngrabServer(ob_display);
92     XFlush(ob_display);
93
94     return TRUE;
95 }
96
97 static void press(ObEvent *e, void *foo)
98 {
99     if (e->data.x.e->xkey.keycode == reset_key &&
100         e->data.x.e->xkey.state == reset_state) {
101         reset_chains();
102         XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
103     } else {
104         KeyBindingTree *p;
105         if (curpos == NULL)
106             p = firstnode;
107         else
108             p = curpos->first_child;
109         while (p) {
110             if (p->key == e->data.x.e->xkey.keycode &&
111                 p->state == e->data.x.e->xkey.state) {
112                 if (p->first_child != NULL) { /* part of a chain */
113                     /* XXX TIMER */
114                     if (!grabbed) {
115                         /*grab should never fail because we should have a
116                           sync grab at this point */
117                         XGrabKeyboard(ob_display, ob_root, 0,
118                                       GrabModeAsync, GrabModeSync,
119                                       CurrentTime);
120                     }
121                     grabbed = TRUE;
122                     curpos = p;
123                     XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
124                 } else {
125                     keyaction_do(&p->action, focus_client);
126
127                     XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
128                     reset_chains();
129                 }
130                 break;
131             }
132             p = p->next_sibling;
133         }
134     }
135 }
136
137 static void binddef()
138 {
139     GList *list = g_list_append(NULL, NULL);
140     KeyAction a;
141
142     list->data = "C-Right";
143     a.action = Action_NextDesktop;
144     keyaction_set_bool(&a, 0, TRUE);
145     keyaction_set_none(&a, 1);
146     bind(list, &a);
147
148     list->data = "C-Left";
149     a.action = Action_PreviousDesktop;
150     keyaction_set_bool(&a, 0, TRUE);
151     keyaction_set_none(&a, 1);
152     bind(list, &a);
153
154     list->data = "C-1";
155     a.action = Action_Desktop;
156     keyaction_set_uint(&a, 0, 0);
157     keyaction_set_none(&a, 1);
158     bind(list, &a);
159
160     list->data = "C-2";
161     a.action = Action_Desktop;
162     keyaction_set_uint(&a, 0, 1);
163     keyaction_set_none(&a, 1);
164     bind(list, &a);
165
166     list->data = "C-3";
167     a.action = Action_Desktop;
168     keyaction_set_uint(&a, 0, 2);
169     keyaction_set_none(&a, 1);
170     bind(list, &a);
171
172     list->data = "C-4";
173     a.action = Action_Desktop;
174     keyaction_set_uint(&a, 0, 3);
175     keyaction_set_none(&a, 1);
176     bind(list, &a);
177
178     list->data = "C-space";
179     a.action = Action_Execute;
180     keyaction_set_string(&a, 0, "xterm");
181     keyaction_set_none(&a, 1);
182     bind(list, &a);
183 }
184
185 void plugin_startup()
186 {
187     dispatch_register(Event_X_KeyPress, (EventHandler)press, NULL);
188
189     /* XXX parse config file! */
190     binddef();
191 }
192
193 void plugin_shutdown()
194 {
195     dispatch_register(0, (EventHandler)press, NULL);
196     clearall();
197 }
198