1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 keyboard.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
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.
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.
17 See the COPYING file for a copy of the GNU General Public License.
30 #include "menuframe.h"
34 #include "moveresize.h"
47 KeyBindingTree
*keyboard_firstnode
= NULL
;
48 static ObPopup
*popup
= NULL
;
49 static ObInteractiveState istate
;
50 static KeyBindingTree
*curpos
;
52 static void grab_keys(gboolean grab
)
56 ungrab_all_keys(RootWindow(ob_display
, ob_screen
));
59 p
= curpos
? curpos
->first_child
: keyboard_firstnode
;
61 grab_key(p
->key
, p
->state
, RootWindow(ob_display
, ob_screen
),
66 grab_key(config_keyboard_reset_keycode
,
67 config_keyboard_reset_state
,
68 RootWindow(ob_display
, ob_screen
), GrabModeAsync
);
72 static gboolean
chain_timeout(gpointer data
)
74 keyboard_reset_chains(0);
75 return FALSE
; /* don't repeat */
78 static void set_curpos(KeyBindingTree
*newpos
)
80 if (curpos
!= newpos
) {
90 for (it
= curpos
->keylist
; it
; it
= g_list_next(it
)) {
91 gchar
*oldtext
= text
;
93 text
= g_strdup(it
->data
);
95 text
= g_strconcat(text
, " - ", it
->data
, NULL
);
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
);
108 void keyboard_reset_chains(gint break_chroots
)
112 for (p
= curpos
; p
; p
= p
->parent
) {
114 if (break_chroots
== 0) break; /* stop here */
115 if (break_chroots
> 0)
122 void keyboard_unbind_all()
124 tree_destroy(keyboard_firstnode
);
125 keyboard_firstnode
= NULL
;
128 void keyboard_chroot(GList
*keylist
)
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
)))
136 tree_chroot(tree
, keylist
);
137 tree_assimilate(tree
);
141 gboolean
keyboard_bind(GList
*keylist
, ObAction
*action
)
143 KeyBindingTree
*tree
, *t
;
145 gboolean mods
= TRUE
;
147 g_assert(keylist
!= NULL
);
148 g_assert(action
!= NULL
);
150 if (!(tree
= tree_build(keylist
)))
153 if ((t
= tree_find(tree
, &conflict
)) != NULL
) {
154 /* already bound to something, use the existing tree */
161 g_message(_("Conflict with key binding in config file"));
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
) {
174 /* when there are no modifiers in the binding, then the action cannot
176 if (!mods
&& action
->data
.any
.interactive
) {
177 action
->data
.any
.interactive
= FALSE
;
178 action
->data
.inter
.final
= TRUE
;
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
);
190 static void keyboard_interactive_end(guint state
, gboolean cancel
, Time time
,
195 g_assert(istate
.active
);
197 /* ungrab first so they won't be NotifyWhileGrabbed */
201 /* set this before running the actions so they know the keyboard is not
203 istate
.active
= FALSE
;
205 alist
= g_slist_append(NULL
, istate
.action
);
206 action_run_interactive(alist
, istate
.client
, state
, time
, cancel
, TRUE
);
210 static void keyboard_interactive_end_client(ObClient
*client
, gpointer data
)
212 if (istate
.active
&& istate
.client
== client
)
213 istate
.client
= NULL
;
217 void keyboard_interactive_cancel()
219 keyboard_interactive_end(0, TRUE
, event_curtime
, TRUE
);
222 gboolean
keyboard_interactive_grab(guint state
, ObClient
*client
,
225 g_assert(action
->data
.any
.interactive
);
227 if (!istate
.active
) {
228 if (!grab_keyboard())
230 } else if (action
->func
!= istate
.action
->func
) {
231 keyboard_interactive_end(state
, TRUE
, action
->data
.any
.time
, FALSE
);
234 istate
.active
= TRUE
;
235 istate
.state
= state
;
236 istate
.client
= client
;
237 istate
.action
= action
;
242 gboolean
keyboard_process_interactive_grab(const XEvent
*e
, ObClient
**client
)
244 gboolean handled
= FALSE
;
245 gboolean done
= FALSE
;
246 gboolean cancel
= FALSE
;
249 if ((e
->type
== KeyRelease
&& !(istate
.state
& e
->xkey
.state
))) {
252 } else if (e
->type
== KeyPress
) {
253 /*if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN))
255 else */if (e
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
)) {
256 cancel
= done
= TRUE
;
259 } else if (e
->type
== ButtonPress
) {
266 keyboard_interactive_end(e
->xkey
.state
, cancel
, e
->xkey
.time
,TRUE
);
269 *client
= istate
.client
;
275 void keyboard_event(ObClient
*client
, const XEvent
*e
)
279 g_assert(e
->type
== KeyPress
);
281 if (e
->xkey
.keycode
== config_keyboard_reset_keycode
&&
282 e
->xkey
.state
== config_keyboard_reset_state
)
284 ob_main_loop_timeout_remove(ob_main_loop
, chain_timeout
);
285 keyboard_reset_chains(-1);
290 p
= keyboard_firstnode
;
292 p
= curpos
->first_child
;
294 if (p
->key
== e
->xkey
.keycode
&&
296 if you have Super_L and Super_R bound to different modXmasks,
297 this makes either mod mask count as Super
299 e.g. Super_L is mod1 and Super_R is mod2, so..
301 key state is just Super_L so 00001
302 00011 | 00001 == 00011 == p->state
304 (p
->state
| e
->xkey
.state
) == p
->state
)
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();
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
,
315 g_direct_equal
, NULL
);
317 } else if (p
->chroot
) /* an empty chroot */
320 keyboard_reset_chains(0);
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
);
330 action_run_key(p
->actions
, client
, e
->xkey
.state
,
331 e
->xkey
.x_root
, e
->xkey
.y_root
,
340 gboolean
keyboard_interactively_grabbed()
342 return istate
.active
;
345 void keyboard_startup(gboolean reconfig
)
348 popup
= popup_new(FALSE
);
351 client_add_destroy_notify(keyboard_interactive_end_client
, NULL
);
354 void keyboard_shutdown(gboolean reconfig
)
357 client_remove_destroy_notify(keyboard_interactive_end_client
);
360 keyboard_interactive_cancel();
362 ob_main_loop_timeout_remove(ob_main_loop
, chain_timeout
);
364 keyboard_unbind_all();