]> Dogcows Code - chaz/openbox/blob - openbox/keyboard.c
make either key work for people who have super_l and super_r (or whatever ones) bound...
[chaz/openbox] / openbox / keyboard.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 keyboard.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "mainloop.h"
21 #include "focus.h"
22 #include "screen.h"
23 #include "frame.h"
24 #include "openbox.h"
25 #include "event.h"
26 #include "grab.h"
27 #include "client.h"
28 #include "action.h"
29 #include "prop.h"
30 #include "menuframe.h"
31 #include "config.h"
32 #include "keytree.h"
33 #include "keyboard.h"
34 #include "moveresize.h"
35 #include "popup.h"
36 #include "gettext.h"
37
38 #include <glib.h>
39
40 typedef struct {
41 gboolean active;
42 guint state;
43 ObClient *client;
44 ObAction *action;
45 } ObInteractiveState;
46
47 KeyBindingTree *keyboard_firstnode = NULL;
48 static ObPopup *popup = NULL;
49 static ObInteractiveState istate;
50 static KeyBindingTree *curpos;
51
52 static void grab_keys(gboolean grab)
53 {
54 KeyBindingTree *p;
55
56 ungrab_all_keys(RootWindow(ob_display, ob_screen));
57
58 if (grab) {
59 p = curpos ? curpos->first_child : keyboard_firstnode;
60 while (p) {
61 grab_key(p->key, p->state, RootWindow(ob_display, ob_screen),
62 GrabModeAsync);
63 p = p->next_sibling;
64 }
65 if (curpos)
66 grab_key(config_keyboard_reset_keycode,
67 config_keyboard_reset_state,
68 RootWindow(ob_display, ob_screen), GrabModeAsync);
69 }
70 }
71
72 static gboolean chain_timeout(gpointer data)
73 {
74 keyboard_reset_chains(0);
75 return FALSE; /* don't repeat */
76 }
77
78 static void set_curpos(KeyBindingTree *newpos)
79 {
80 if (curpos != newpos) {
81 grab_keys(FALSE);
82 curpos = newpos;
83 grab_keys(TRUE);
84 }
85
86 if (curpos != NULL) {
87 gchar *text = NULL;
88 GList *it;
89
90 for (it = curpos->keylist; it; it = g_list_next(it)) {
91 gchar *oldtext = text;
92 if (text == NULL)
93 text = g_strdup(it->data);
94 else
95 text = g_strconcat(text, " - ", it->data, NULL);
96 g_free(oldtext);
97 }
98
99 popup_position(popup, NorthWestGravity, 10, 10);
100 /* 1 second delay for the popup to show */
101 popup_delay_show(popup, G_USEC_PER_SEC, text);
102 g_free(text);
103 } else {
104 popup_hide(popup);
105 }
106 }
107
108 void keyboard_reset_chains(gint break_chroots)
109 {
110 KeyBindingTree *p;
111
112 for (p = curpos; p; p = p->parent) {
113 if (p->chroot) {
114 if (break_chroots == 0) break; /* stop here */
115 if (break_chroots > 0)
116 --break_chroots;
117 }
118 }
119 set_curpos(p);
120 }
121
122 void keyboard_unbind_all()
123 {
124 tree_destroy(keyboard_firstnode);
125 keyboard_firstnode = NULL;
126 }
127
128 void keyboard_chroot(GList *keylist)
129 {
130 /* try do it in the existing tree. if we can't that means it is an empty
131 chroot binding. so add it to the tree then. */
132 if (!tree_chroot(keyboard_firstnode, keylist)) {
133 KeyBindingTree *tree;
134 if (!(tree = tree_build(keylist)))
135 return;
136 tree_chroot(tree, keylist);
137 tree_assimilate(tree);
138 }
139 }
140
141 gboolean keyboard_bind(GList *keylist, ObAction *action)
142 {
143 KeyBindingTree *tree, *t;
144 gboolean conflict;
145 gboolean mods = TRUE;
146
147 g_assert(keylist != NULL);
148 g_assert(action != NULL);
149
150 if (!(tree = tree_build(keylist)))
151 return FALSE;
152
153 if ((t = tree_find(tree, &conflict)) != NULL) {
154 /* already bound to something, use the existing tree */
155 tree_destroy(tree);
156 tree = NULL;
157 } else
158 t = tree;
159
160 if (conflict) {
161 g_message(_("Conflict with key binding in config file"));
162 tree_destroy(tree);
163 return FALSE;
164 }
165
166 /* find if every key in this chain has modifiers, and also find the
167 bottom node of the tree */
168 while (t->first_child) {
169 if (!t->state)
170 mods = FALSE;
171 t = t->first_child;
172 }
173
174 /* when there are no modifiers in the binding, then the action cannot
175 be interactive */
176 if (!mods && action->data.any.interactive) {
177 action->data.any.interactive = FALSE;
178 action->data.inter.final = TRUE;
179 }
180
181 /* set the action */
182 t->actions = g_slist_append(t->actions, action);
183 /* assimilate this built tree into the main tree. assimilation
184 destroys/uses the tree */
185 if (tree) tree_assimilate(tree);
186
187 return TRUE;
188 }
189
190 static void keyboard_interactive_end(guint state, gboolean cancel, Time time,
191 gboolean ungrab)
192 {
193 GSList *alist;
194
195 g_assert(istate.active);
196
197 /* ungrab first so they won't be NotifyWhileGrabbed */
198 if (ungrab)
199 ungrab_keyboard();
200
201 /* set this before running the actions so they know the keyboard is not
202 grabbed */
203 istate.active = FALSE;
204
205 alist = g_slist_append(NULL, istate.action);
206 action_run_interactive(alist, istate.client, state, time, cancel, TRUE);
207 g_slist_free(alist);
208 }
209
210 static void keyboard_interactive_end_client(ObClient *client, gpointer data)
211 {
212 if (istate.active && istate.client == client)
213 istate.client = NULL;
214 }
215
216
217 void keyboard_interactive_cancel()
218 {
219 keyboard_interactive_end(0, TRUE, event_curtime, TRUE);
220 }
221
222 gboolean keyboard_interactive_grab(guint state, ObClient *client,
223 ObAction *action)
224 {
225 g_assert(action->data.any.interactive);
226
227 if (!istate.active) {
228 if (!grab_keyboard())
229 return FALSE;
230 } else if (action->func != istate.action->func) {
231 keyboard_interactive_end(state, TRUE, action->data.any.time, FALSE);
232 }
233
234 istate.active = TRUE;
235 istate.state = state;
236 istate.client = client;
237 istate.action = action;
238
239 return TRUE;
240 }
241
242 gboolean keyboard_process_interactive_grab(const XEvent *e, ObClient **client)
243 {
244 gboolean handled = FALSE;
245 gboolean done = FALSE;
246 gboolean cancel = FALSE;
247
248 if (istate.active) {
249 if ((e->type == KeyRelease && !(istate.state & e->xkey.state))) {
250 done = TRUE;
251 handled = TRUE;
252 } else if (e->type == KeyPress) {
253 /*if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN))
254 done = TRUE;
255 else */if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) {
256 cancel = done = TRUE;
257 handled = TRUE;
258 }
259 } else if (e->type == ButtonPress) {
260 cancel = TRUE;
261 done = TRUE;
262 handled = FALSE;
263 }
264
265 if (done)
266 keyboard_interactive_end(e->xkey.state, cancel, e->xkey.time,TRUE);
267
268 if (handled)
269 *client = istate.client;
270 }
271
272 return handled;
273 }
274
275 void keyboard_event(ObClient *client, const XEvent *e)
276 {
277 KeyBindingTree *p;
278
279 g_assert(e->type == KeyPress);
280
281 if (e->xkey.keycode == config_keyboard_reset_keycode &&
282 e->xkey.state == config_keyboard_reset_state)
283 {
284 ob_main_loop_timeout_remove(ob_main_loop, chain_timeout);
285 keyboard_reset_chains(-1);
286 return;
287 }
288
289 if (curpos == NULL)
290 p = keyboard_firstnode;
291 else
292 p = curpos->first_child;
293 while (p) {
294 if (p->key == e->xkey.keycode &&
295 /* tricksy.
296 if you have Super_L and Super_R bound to different modXmasks,
297 this makes either mod mask count as Super
298
299 e.g. Super_L is mod1 and Super_R is mod2, so..
300 p->state = 00011
301 key state is just Super_L so 00001
302 00011 | 00001 == 00011 == p->state
303 */
304 (p->state | e->xkey.state) == p->state)
305 {
306 /* if we hit a key binding, then close any open menus and run it */
307 if (menu_frame_visible)
308 menu_frame_hide_all();
309
310 if (p->first_child != NULL) { /* part of a chain */
311 ob_main_loop_timeout_remove(ob_main_loop, chain_timeout);
312 /* 3 second timeout for chains */
313 ob_main_loop_timeout_add(ob_main_loop, 3 * G_USEC_PER_SEC,
314 chain_timeout, NULL,
315 g_direct_equal, NULL);
316 set_curpos(p);
317 } else if (p->chroot) /* an empty chroot */
318 set_curpos(p);
319 else {
320 keyboard_reset_chains(0);
321
322 /* If we don't have the keyboard grabbed, then ungrab it with
323 XUngrabKeyboard, so that there is not a passive grab left
324 on from the KeyPress. If the grab is left on, and focus
325 moves during that time, it will be NotifyWhileGrabbed, and
326 applications like to ignore those! */
327 if (!keyboard_interactively_grabbed())
328 XUngrabKeyboard(ob_display, e->xkey.time);
329
330 action_run_key(p->actions, client, e->xkey.state,
331 e->xkey.x_root, e->xkey.y_root,
332 e->xkey.time);
333 }
334 break;
335 }
336 p = p->next_sibling;
337 }
338 }
339
340 gboolean keyboard_interactively_grabbed()
341 {
342 return istate.active;
343 }
344
345 void keyboard_startup(gboolean reconfig)
346 {
347 grab_keys(TRUE);
348 popup = popup_new(FALSE);
349
350 if (!reconfig)
351 client_add_destroy_notify(keyboard_interactive_end_client, NULL);
352 }
353
354 void keyboard_shutdown(gboolean reconfig)
355 {
356 if (!reconfig)
357 client_remove_destroy_notify(keyboard_interactive_end_client);
358
359 if (istate.active)
360 keyboard_interactive_cancel();
361
362 ob_main_loop_timeout_remove(ob_main_loop, chain_timeout);
363
364 keyboard_unbind_all();
365 set_curpos(NULL);
366
367 popup_free(popup);
368 popup = NULL;
369 }
370
This page took 0.052754 seconds and 5 git commands to generate.