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