end the other action when starting a new interactive action while one was in place
[mikachu/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) {
116         if (!grab_keyboard(TRUE))
117             return;
118         if (!grab_pointer(TRUE, None)) {
119             grab_keyboard(FALSE);
120             return;
121         }
122         interactive_grab = TRUE;
123     } else if (action != grabbed_action) {
124         /* finish it */
125         if (grabbed_action->func == action_cycle_windows) {
126             grabbed_action->data.cycle.final = TRUE;
127         }
128         if (grabbed_action->func == action_desktop_dir) {
129             grabbed_action->data.desktopdir.final = TRUE;
130         }
131         if (grabbed_action->func == action_send_to_desktop_dir) {
132             grabbed_action->data.sendtodir.final = TRUE;
133         }
134         grabbed_action->func(&grabbed_action->data);
135     }
136
137     grabbed_state = state;
138     grabbed_client = client;
139     grabbed_action = action;
140     grabbed_context = context;
141 }
142
143 gboolean keyboard_process_interactive_grab(const XEvent *e,
144                                            ObClient **client,
145                                            ObFrameContext *context)
146 {
147     gboolean handled = FALSE;
148     gboolean done = FALSE;
149
150     if (interactive_grab) {
151         *client = grabbed_client;
152         *context = grabbed_context;
153
154         if ((e->type == KeyRelease && 
155              !(grabbed_state & e->xkey.state)))
156             done = TRUE;
157         else if (e->type == KeyPress) {
158             if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN))
159                 done = TRUE;
160             else if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) {
161                 if (grabbed_action->func == action_cycle_windows) {
162                     grabbed_action->data.cycle.cancel = TRUE;
163                 }
164                 if (grabbed_action->func == action_desktop_dir) {
165                     grabbed_action->data.desktopdir.cancel = TRUE;
166                 }
167                 if (grabbed_action->func == action_send_to_desktop_dir)
168                 {
169                     grabbed_action->data.sendtodir.cancel = TRUE;
170                 }
171                 done = TRUE;
172             }
173         }
174         if (done) { 
175             if (grabbed_action->func == action_cycle_windows) {
176                 grabbed_action->data.cycle.final = TRUE;
177             }
178             if (grabbed_action->func == action_desktop_dir) {
179                 grabbed_action->data.desktopdir.final = TRUE;
180             }
181             if (grabbed_action->func == action_send_to_desktop_dir) {
182                 grabbed_action->data.sendtodir.final = TRUE;
183             }
184
185             grabbed_action->func(&grabbed_action->data);
186
187             interactive_grab = FALSE;
188             grab_keyboard(FALSE);
189             grab_pointer(FALSE, None);
190             keyboard_reset_chains();
191
192             handled = TRUE;
193         }
194     }
195
196     return handled;
197 }
198
199 void keyboard_event(ObClient *client, const XEvent *e)
200 {
201     KeyBindingTree *p;
202
203     g_assert(e->type == KeyPress);
204
205     if (e->xkey.keycode == config_keyboard_reset_keycode &&
206         e->xkey.state == config_keyboard_reset_state)
207     {
208         keyboard_reset_chains();
209         return;
210     }
211
212     if (curpos == NULL)
213         p = keyboard_firstnode;
214     else
215         p = curpos->first_child;
216     while (p) {
217         if (p->key == e->xkey.keycode &&
218             p->state == e->xkey.state) {
219             if (p->first_child != NULL) { /* part of a chain */
220                 if (chain_timer) timer_stop(chain_timer);
221                 /* 5 second timeout for chains */
222                 chain_timer = timer_start(5000*1000, chain_timeout,
223                                           NULL);
224                 curpos = p;
225                 grab_keys(TRUE);
226             } else {
227                 GSList *it;
228                 for (it = p->actions; it; it = it->next) {
229                     ObAction *act = it->data;
230                     if (act->func != NULL) {
231                         act->data.any.c = client;
232
233                         if (act->func == action_cycle_windows)
234                         {
235                             act->data.cycle.final = FALSE;
236                             act->data.cycle.cancel = FALSE;
237                         }
238                         if (act->func == action_desktop_dir)
239                         {
240                             act->data.desktopdir.final = FALSE;
241                             act->data.desktopdir.cancel = FALSE;
242                         }
243                         if (act->func == action_send_to_desktop_dir)
244                         {
245                             act->data.sendtodir.final = FALSE;
246                             act->data.sendtodir.cancel = FALSE;
247                         }
248
249                         if (act->func == action_moveresize)
250                         {
251                             screen_pointer_pos(&act->data.moveresize.x,
252                                                &act->data.moveresize.y);
253                         }
254
255                         if ((act->func == action_cycle_windows ||
256                              act->func == action_desktop_dir ||
257                              act->func == action_send_to_desktop_dir))
258                         {
259                             keyboard_interactive_grab(e->xkey.state, client,
260                                                       0, act);
261                         }
262
263                         if (act->func == action_showmenu)
264                         {
265                             act->data.showmenu.x = e->xkey.x_root;
266                             act->data.showmenu.y = e->xkey.y_root;
267                         }
268
269                         act->data.any.c = client;
270                         act->func(&act->data);
271                     }
272                 }
273
274                 keyboard_reset_chains();
275             }
276             break;
277         }
278         p = p->next_sibling;
279     }
280 }
281
282 void keyboard_startup()
283 {
284     grab_keys(TRUE);
285 }
286
287 void keyboard_shutdown()
288 {
289     tree_destroy(keyboard_firstnode);
290     keyboard_firstnode = NULL;
291     grab_keys(FALSE);
292 }
293