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