add the chainQuitKey option to teh <keyboard> section of the rc3
[dana/openbox.git] / openbox / keyboard.c
1 #include "focus.h"
2 #include "screen.h"
3 #include "frame.h"
4 #include "openbox.h"
5 #include "event.h"
6 #include "grab.h"
7 #include "client.h"
8 #include "action.h"
9 #include "prop.h"
10 #include "timer.h"
11 #include "config.h"
12 #include "keytree.h"
13 #include "keyboard.h"
14 #include "translate.h"
15
16 #include <glib.h>
17
18 KeyBindingTree *keyboard_firstnode;
19
20 static KeyBindingTree *curpos;
21 static ObTimer *chain_timer;
22 static gboolean interactive_grab;
23 static guint grabbed_state;
24 static ObClient *grabbed_client;
25 static ObAction *grabbed_action;
26 static ObFrameContext grabbed_context;
27
28 static void grab_for_window(Window win, gboolean grab)
29 {
30     KeyBindingTree *p;
31
32     ungrab_all_keys(win);
33
34     if (grab) {
35         p = curpos ? curpos->first_child : keyboard_firstnode;
36         while (p) {
37             grab_key(p->key, p->state, win, GrabModeAsync);
38             p = p->next_sibling;
39         }
40         if (curpos)
41             grab_key(config_keyboard_reset_keycode,
42                      config_keyboard_reset_state,
43                      win, GrabModeAsync);
44     }
45 }
46
47 void keyboard_grab_for_client(ObClient *c, gboolean grab)
48 {
49     grab_for_window(c->window, grab);
50 }
51
52 static void grab_keys(gboolean grab)
53 {
54     GList *it;
55
56     grab_for_window(screen_support_win, grab);
57     for (it = client_list; it; it = g_list_next(it))
58         grab_for_window(((ObClient*)it->data)->frame->window, grab);
59 }
60
61 void keyboard_reset_chains()
62 {
63     if (chain_timer) {
64         timer_stop(chain_timer);
65         chain_timer = NULL;
66     }
67     if (curpos) {
68         curpos = NULL;
69         grab_keys(TRUE);
70     }
71 }
72
73 static void chain_timeout(ObTimer *t, void *data)
74 {
75     keyboard_reset_chains();
76 }
77
78 gboolean keyboard_bind(GList *keylist, ObAction *action)
79 {
80     KeyBindingTree *tree, *t;
81     gboolean conflict;
82
83     g_assert(keylist != NULL);
84     g_assert(action != NULL);
85
86     if (!(tree = tree_build(keylist)))
87         return FALSE;
88
89     if ((t = tree_find(tree, &conflict)) != NULL) {
90         /* already bound to something, use the existing tree */
91         tree_destroy(tree);
92         tree = NULL;
93     } else
94         t = tree;
95     while (t->first_child) t = t->first_child;
96
97     if (conflict) {
98         g_warning("conflict with binding");
99         tree_destroy(tree);
100         return FALSE;
101     }
102
103     /* set the action */
104     t->actions = g_slist_append(t->actions, action);
105     /* assimilate this built tree into the main tree. assimilation
106        destroys/uses the tree */
107     if (tree) tree_assimilate(tree);
108
109     return TRUE;
110 }
111
112 void keyboard_interactive_grab(guint state, ObClient *client,
113                                ObFrameContext context, ObAction *action)
114 {
115     if (!interactive_grab && grab_keyboard(TRUE)) {
116         interactive_grab = TRUE;
117         grabbed_state = state;
118         grabbed_client = client;
119         grabbed_action = action;
120         grabbed_context = context;
121         grab_pointer(TRUE, None);
122     }
123 }
124
125 gboolean keyboard_process_interactive_grab(const XEvent *e,
126                                            ObClient **client,
127                                            ObFrameContext *context)
128 {
129     gboolean handled = FALSE;
130     gboolean done = FALSE;
131
132     if (interactive_grab) {
133         *client = grabbed_client;
134         *context = grabbed_context;
135
136         if ((e->type == KeyRelease && 
137              !(grabbed_state & e->xkey.state)))
138             done = TRUE;
139         else if (e->type == KeyPress) {
140             if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN))
141                 done = TRUE;
142             else if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) {
143                 if (grabbed_action->func == action_cycle_windows) {
144                     grabbed_action->data.cycle.cancel = TRUE;
145                 }
146                 if (grabbed_action->func == action_desktop_dir) {
147                     grabbed_action->data.desktopdir.cancel = TRUE;
148                 }
149                 if (grabbed_action->func == action_send_to_desktop_dir)
150                 {
151                     grabbed_action->data.sendtodir.cancel = TRUE;
152                 }
153                 done = TRUE;
154             }
155         }
156         if (done) { 
157             if (grabbed_action->func == action_cycle_windows) {
158                 grabbed_action->data.cycle.final = TRUE;
159             }
160             if (grabbed_action->func == action_desktop_dir) {
161                 grabbed_action->data.desktopdir.final = TRUE;
162             }
163             if (grabbed_action->func == action_send_to_desktop_dir) {
164                 grabbed_action->data.sendtodir.final = TRUE;
165             }
166
167             grabbed_action->func(&grabbed_action->data);
168
169             interactive_grab = FALSE;
170             grab_keyboard(FALSE);
171             grab_pointer(FALSE, None);
172             keyboard_reset_chains();
173
174             handled = TRUE;
175         }
176     }
177
178     return handled;
179 }
180
181 void keyboard_event(ObClient *client, const XEvent *e)
182 {
183     KeyBindingTree *p;
184
185     g_assert(e->type == KeyPress);
186
187     if (e->xkey.keycode == config_keyboard_reset_keycode &&
188         e->xkey.state == config_keyboard_reset_state)
189     {
190         keyboard_reset_chains();
191         return;
192     }
193
194     if (curpos == NULL)
195         p = keyboard_firstnode;
196     else
197         p = curpos->first_child;
198     while (p) {
199         if (p->key == e->xkey.keycode &&
200             p->state == e->xkey.state) {
201             if (p->first_child != NULL) { /* part of a chain */
202                 if (chain_timer) timer_stop(chain_timer);
203                 /* 5 second timeout for chains */
204                 chain_timer = timer_start(5000*1000, chain_timeout,
205                                           NULL);
206                 curpos = p;
207                 grab_keys(TRUE);
208             } else {
209                 GSList *it;
210                 for (it = p->actions; it; it = it->next) {
211                     ObAction *act = it->data;
212                     if (act->func != NULL) {
213                         act->data.any.c = client;
214
215                         if (act->func == action_cycle_windows)
216                         {
217                             act->data.cycle.final = FALSE;
218                             act->data.cycle.cancel = FALSE;
219                         }
220                         if (act->func == action_desktop_dir)
221                         {
222                             act->data.desktopdir.final = FALSE;
223                             act->data.desktopdir.cancel = FALSE;
224                         }
225                         if (act->func == action_send_to_desktop_dir)
226                         {
227                             act->data.sendtodir.final = FALSE;
228                             act->data.sendtodir.cancel = FALSE;
229                         }
230
231                         if (act->func == action_moveresize)
232                         {
233                             screen_pointer_pos(&act->data.moveresize.x,
234                                                &act->data.moveresize.y);
235                         }
236
237                         if ((act->func == action_cycle_windows ||
238                              act->func == action_desktop_dir ||
239                              act->func == action_send_to_desktop_dir))
240                         {
241                             keyboard_interactive_grab(e->xkey.state, client,
242                                                       0, act);
243                         }
244
245                         if (act->func == action_showmenu)
246                         {
247                             act->data.showmenu.x =
248                                 e->xkey.x_root;
249                             act->data.showmenu.y =
250                                 e->xkey.y_root;
251                         }
252
253                         act->data.any.c = client;
254                         act->func(&act->data);
255                     }
256                 }
257
258                 keyboard_reset_chains();
259             }
260             break;
261         }
262         p = p->next_sibling;
263     }
264 }
265
266 void keyboard_startup()
267 {
268     grab_keys(TRUE);
269 }
270
271 void keyboard_shutdown()
272 {
273     tree_destroy(keyboard_firstnode);
274     keyboard_firstnode = NULL;
275     grab_keys(FALSE);
276 }
277