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 "translate.h"
35 #include "moveresize.h"
48 KeyBindingTree
*keyboard_firstnode
= NULL
;
49 static ObPopup
*popup
= NULL
;
50 static ObInteractiveState istate
;
51 static KeyBindingTree
*curpos
;
53 static void grab_keys(gboolean grab
)
57 ungrab_all_keys(RootWindow(ob_display
, ob_screen
));
60 p
= curpos
? curpos
->first_child
: keyboard_firstnode
;
62 grab_key(p
->key
, p
->state
, RootWindow(ob_display
, ob_screen
),
67 grab_key(config_keyboard_reset_keycode
,
68 config_keyboard_reset_state
,
69 RootWindow(ob_display
, ob_screen
), GrabModeAsync
);
73 static gboolean
chain_timeout(gpointer data
)
75 keyboard_reset_chains(0);
76 return FALSE
; /* don't repeat */
79 static void set_curpos(KeyBindingTree
*newpos
)
81 if (curpos
!= newpos
) {
91 for (it
= curpos
->keylist
; it
; it
= g_list_next(it
)) {
92 gchar
*oldtext
= text
;
94 text
= g_strdup(it
->data
);
96 text
= g_strconcat(text
, " - ", it
->data
, NULL
);
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
);
109 void keyboard_reset_chains(gint break_chroots
)
113 for (p
= curpos
; p
; p
= p
->parent
) {
115 if (break_chroots
== 0) break; /* stop here */
116 if (break_chroots
> 0)
123 void keyboard_unbind_all()
125 tree_destroy(keyboard_firstnode
);
126 keyboard_firstnode
= NULL
;
129 void keyboard_chroot(GList
*keylist
)
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
)))
137 tree_chroot(tree
, keylist
);
138 tree_assimilate(tree
);
142 gboolean
keyboard_bind(GList
*keylist
, ObAction
*action
)
144 KeyBindingTree
*tree
, *t
;
146 gboolean mods
= TRUE
;
148 g_assert(keylist
!= NULL
);
149 g_assert(action
!= NULL
);
151 if (!(tree
= tree_build(keylist
)))
154 if ((t
= tree_find(tree
, &conflict
)) != NULL
) {
155 /* already bound to something, use the existing tree */
162 g_message(_("Conflict with key binding in config file"));
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
) {
175 /* when there are no modifiers in the binding, then the action cannot
177 if (!mods
&& action
->data
.any
.interactive
) {
178 action
->data
.any
.interactive
= FALSE
;
179 action
->data
.inter
.final
= TRUE
;
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
);
191 static void keyboard_interactive_end(guint state
, gboolean cancel
, Time time
,
196 g_assert(istate
.active
);
198 /* ungrab first so they won't be NotifyWhileGrabbed */
202 /* set this before running the actions so they know the keyboard is not
204 istate
.active
= FALSE
;
206 alist
= g_slist_append(NULL
, istate
.action
);
207 action_run_interactive(alist
, istate
.client
, state
, time
, cancel
, TRUE
);
211 static void keyboard_interactive_end_client(ObClient
*client
, gpointer data
)
213 if (istate
.active
&& istate
.client
== client
)
214 istate
.client
= NULL
;
218 void keyboard_interactive_cancel()
220 keyboard_interactive_end(0, TRUE
, event_curtime
, TRUE
);
223 gboolean
keyboard_interactive_grab(guint state
, ObClient
*client
,
226 g_assert(action
->data
.any
.interactive
);
228 if (!istate
.active
) {
229 if (!grab_keyboard())
231 } else if (action
->func
!= istate
.action
->func
) {
232 keyboard_interactive_end(state
, TRUE
, action
->data
.any
.time
, FALSE
);
235 istate
.active
= TRUE
;
236 istate
.state
= state
;
237 istate
.client
= client
;
238 istate
.action
= action
;
243 gboolean
keyboard_process_interactive_grab(const XEvent
*e
, ObClient
**client
)
245 gboolean handled
= FALSE
;
246 gboolean done
= FALSE
;
247 gboolean cancel
= FALSE
;
250 if ((e
->type
== KeyRelease
&& !(istate
.state
& e
->xkey
.state
))) {
253 } else if (e
->type
== KeyPress
) {
254 /*if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN))
256 else */if (e
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
)) {
257 cancel
= done
= TRUE
;
260 } else if (e
->type
== ButtonPress
) {
267 keyboard_interactive_end(e
->xkey
.state
, cancel
, e
->xkey
.time
,TRUE
);
270 *client
= istate
.client
;
276 void keyboard_event(ObClient
*client
, const XEvent
*e
)
280 g_assert(e
->type
== KeyPress
);
282 if (e
->xkey
.keycode
== config_keyboard_reset_keycode
&&
283 e
->xkey
.state
== config_keyboard_reset_state
)
285 ob_main_loop_timeout_remove(ob_main_loop
, chain_timeout
);
286 keyboard_reset_chains(-1);
291 p
= keyboard_firstnode
;
293 p
= curpos
->first_child
;
295 if (p
->key
== e
->xkey
.keycode
&&
296 p
->state
== e
->xkey
.state
)
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();
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
,
307 g_direct_equal
, NULL
);
309 } else if (p
->chroot
) /* an empty chroot */
312 keyboard_reset_chains(0);
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
);
322 action_run_key(p
->actions
, client
, e
->xkey
.state
,
323 e
->xkey
.x_root
, e
->xkey
.y_root
,
332 gboolean
keyboard_interactively_grabbed()
334 return istate
.active
;
337 void keyboard_startup(gboolean reconfig
)
340 popup
= popup_new(FALSE
);
343 client_add_destroy_notify(keyboard_interactive_end_client
, NULL
);
346 void keyboard_shutdown(gboolean reconfig
)
349 client_remove_destroy_notify(keyboard_interactive_end_client
);
352 keyboard_interactive_cancel();
354 ob_main_loop_timeout_remove(ob_main_loop
, chain_timeout
);
356 keyboard_unbind_all();