merge the C branch into HEAD
[openbox.git] / c / kbind.c
blob399ec83003a4c1f423ea203cd4819e4ebe01c133
1 #include "focus.h"
2 #include "openbox.h"
3 #include "hooks.h"
4 #include "kbind.h"
6 #include <glib.h>
7 #ifdef HAVE_STRING_H
8 # include <string.h>
9 #endif
11 typedef struct KeyBindingTree {
12 guint state;
13 guint key;
14 GList *keylist;
16 /* the next binding in the tree at the same level */
17 struct KeyBindingTree *next_sibling;
18 /* the first child of this binding (next binding in a chained sequence).*/
19 struct KeyBindingTree *first_child;
20 } KeyBindingTree;
23 static KeyBindingTree *firstnode, *curpos;
24 static guint reset_key, reset_state;
25 static gboolean grabbed, user_grabbed;
27 guint kbind_translate_modifier(char *str)
29 if (!strcmp("Mod1", str)) return Mod1Mask;
30 else if (!strcmp("Mod2", str)) return Mod2Mask;
31 else if (!strcmp("Mod3", str)) return Mod3Mask;
32 else if (!strcmp("Mod4", str)) return Mod4Mask;
33 else if (!strcmp("Mod5", str)) return Mod5Mask;
34 else if (!strcmp("C", str)) return ControlMask;
35 else if (!strcmp("S", str)) return ShiftMask;
36 g_warning("Invalid modifier '%s' in binding.", str);
37 return 0;
40 static gboolean translate(char *str, guint *state, guint *keycode)
42 char **parsed;
43 char *l;
44 int i;
45 gboolean ret = FALSE;
46 KeySym sym;
48 parsed = g_strsplit(str, "-", -1);
50 /* first, find the key (last token) */
51 l = NULL;
52 for (i = 0; parsed[i] != NULL; ++i)
53 l = parsed[i];
54 if (l == NULL)
55 goto translation_fail;
57 /* figure out the mod mask */
58 *state = 0;
59 for (i = 0; parsed[i] != l; ++i) {
60 guint m = kbind_translate_modifier(parsed[i]);
61 if (!m) goto translation_fail;
62 *state |= m;
65 /* figure out the keycode */
66 sym = XStringToKeysym(l);
67 if (sym == NoSymbol) {
68 g_warning("Invalid key name '%s' in key binding.", l);
69 goto translation_fail;
71 *keycode = XKeysymToKeycode(ob_display, sym);
72 if (!keycode) {
73 g_warning("Key '%s' does not exist on the display.", l);
74 goto translation_fail;
77 ret = TRUE;
79 translation_fail:
80 g_strfreev(parsed);
81 return ret;
84 static void destroytree(KeyBindingTree *tree)
86 KeyBindingTree *c;
88 while (tree) {
89 destroytree(tree->next_sibling);
90 c = tree->first_child;
91 if (c == NULL) {
92 GList *it;
93 for (it = tree->keylist; it != NULL; it = it->next)
94 g_free(it->data);
95 g_list_free(tree->keylist);
97 g_free(tree);
98 tree = c;
102 static KeyBindingTree *buildtree(GList *keylist)
104 GList *it;
105 KeyBindingTree *ret = NULL, *p;
107 if (g_list_length(keylist) <= 0)
108 return NULL; /* nothing in the list.. */
110 for (it = g_list_last(keylist); it != NULL; it = it->prev) {
111 p = ret;
112 ret = g_new(KeyBindingTree, 1);
113 ret->next_sibling = NULL;
114 if (p == NULL) {
115 GList *it;
117 /* this is the first built node, the bottom node of the tree */
118 ret->keylist = g_list_copy(keylist); /* shallow copy */
119 for (it = ret->keylist; it != NULL; it = it->next) /* deep copy */
120 it->data = g_strdup(it->data);
122 ret->first_child = p;
123 if (!translate(it->data, &ret->state, &ret->key)) {
124 destroytree(ret);
125 return NULL;
128 return ret;
131 static void assimilate(KeyBindingTree *node)
133 KeyBindingTree *a, *b, *tmp, *last;
135 if (firstnode == NULL) {
136 /* there are no nodes at this level yet */
137 firstnode = node;
138 } else {
139 a = firstnode;
140 last = a;
141 b = node;
142 while (a) {
143 last = a;
144 if (!(a->state == b->state && a->key == b->key)) {
145 a = a->next_sibling;
146 } else {
147 tmp = b;
148 b = b->first_child;
149 g_free(tmp);
150 a = a->first_child;
153 if (!(last->state == b->state && last->key == a->key))
154 last->next_sibling = b;
155 else {
156 last->first_child = b->first_child;
157 g_free(b);
162 KeyBindingTree *find(KeyBindingTree *search, gboolean *conflict)
164 KeyBindingTree *a, *b;
166 *conflict = FALSE;
168 a = firstnode;
169 b = search;
170 while (a && b) {
171 if (!(a->state == b->state && a->key == b->key)) {
172 a = a->next_sibling;
173 } else {
174 if ((a->first_child == NULL) == (b->first_child == NULL)) {
175 if (a->first_child == NULL) {
176 /* found it! (return the actual node, not the search's) */
177 return a;
179 } else {
180 *conflict = TRUE;
181 return NULL; /* the chain status' don't match (conflict!) */
183 b = b->first_child;
184 a = a->first_child;
187 return NULL; // it just isn't in here
190 static void grab_keys(gboolean grab)
192 if (!grab) {
193 XUngrabKey(ob_display, AnyKey, AnyModifier, ob_root);
194 } else {
195 KeyBindingTree *p = firstnode;
196 while (p) {
197 XGrabKey(ob_display, p->key, p->state, ob_root, FALSE,
198 GrabModeAsync, GrabModeSync);
199 p = p->next_sibling;
204 void reset_chains()
206 /* XXX kill timer */
207 curpos = NULL;
208 if (grabbed) {
209 grabbed = FALSE;
210 g_message("reset chains. user: %d", user_grabbed);
211 if (!user_grabbed)
212 XUngrabKeyboard(ob_display, CurrentTime);
216 void kbind_fire(guint state, guint key, gboolean press)
218 EventData *data;
219 struct Client *c = focus_client;
220 GQuark context = c != NULL ? g_quark_try_string("client")
221 : g_quark_try_string("root");
223 if (user_grabbed) {
224 data = eventdata_new_key(press ? Key_Press : Key_Release,
225 context, c, state, key, NULL);
226 g_assert(data != NULL);
227 hooks_fire_keyboard(data);
228 eventdata_free(data);
231 if (key == reset_key && state == reset_state) {
232 reset_chains();
233 XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
234 } else {
235 KeyBindingTree *p;
236 if (curpos == NULL)
237 p = firstnode;
238 else
239 p = curpos->first_child;
240 while (p) {
241 if (p->key == key && p->state == state) {
242 if (p->first_child != NULL) { /* part of a chain */
243 /* XXX TIMER */
244 if (!grabbed && !user_grabbed) {
245 /*grab should never fail because we should have a sync
246 grab at this point */
247 XGrabKeyboard(ob_display, ob_root, 0, GrabModeAsync,
248 GrabModeSync, CurrentTime);
250 grabbed = TRUE;
251 curpos = p;
252 XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
253 } else {
254 data = eventdata_new_key(press ? Key_Press : Key_Release,
255 context, c, state, key,
256 p->keylist);
257 g_assert(data != NULL);
258 hooks_fire(data);
259 eventdata_free(data);
261 XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
262 reset_chains();
264 break;
266 p = p->next_sibling;
271 gboolean kbind_add(GList *keylist)
273 KeyBindingTree *tree, *t;
274 gboolean conflict;
276 if (!(tree = buildtree(keylist)))
277 return FALSE; /* invalid binding requested */
279 t = find(tree, &conflict);
280 if (conflict) {
281 /* conflicts with another binding */
282 destroytree(tree);
283 return FALSE;
286 if (t != NULL) {
287 /* already bound to something */
288 destroytree(tree);
289 } else {
290 /* grab the server here to make sure no key pressed go missed */
291 XGrabServer(ob_display);
292 XSync(ob_display, FALSE);
294 grab_keys(FALSE);
296 /* assimilate this built tree into the main tree */
297 assimilate(tree); // assimilation destroys/uses the tree
299 grab_keys(TRUE);
301 XUngrabServer(ob_display);
302 XFlush(ob_display);
305 return TRUE;
308 void kbind_clearall()
310 grab_keys(FALSE);
311 destroytree(firstnode);
312 firstnode = NULL;
313 grab_keys(TRUE);
316 void kbind_startup()
318 gboolean b;
320 curpos = firstnode = NULL;
321 grabbed = user_grabbed = FALSE;
323 b = translate("C-G", &reset_state, &reset_key);
324 g_assert(b);
327 void kbind_shutdown()
329 if (grabbed || user_grabbed) {
330 grabbed = FALSE;
331 kbind_grab_keyboard(FALSE);
333 grab_keys(FALSE);
334 destroytree(firstnode);
335 firstnode = NULL;
338 gboolean kbind_grab_keyboard(gboolean grab)
340 gboolean ret = TRUE;
342 if (!grab)
343 g_message("grab_keyboard(false). grabbed: %d", grabbed);
345 user_grabbed = grab;
346 if (!grabbed) {
347 if (grab)
348 ret = XGrabKeyboard(ob_display, ob_root, 0, GrabModeAsync,
349 GrabModeAsync, CurrentTime) == GrabSuccess;
350 else
351 XUngrabKeyboard(ob_display, CurrentTime);
353 return ret;