]> icculus.org git repositories - dana/openbox.git/blob - openbox/actions/directionalwindows.c
don't strip the xkey event states, and make the menus work with non-US stuff.
[dana/openbox.git] / openbox / actions / directionalwindows.c
1 #include "openbox/actions.h"
2 #include "openbox/event.h"
3 #include "openbox/stacking.h"
4 #include "openbox/window.h"
5 #include "openbox/focus_cycle.h"
6 #include "openbox/openbox.h"
7 #include "openbox/misc.h"
8 #include "gettext.h"
9
10 typedef struct {
11     gboolean interactive;
12     gboolean dialog;
13     gboolean dock_windows;
14     gboolean desktop_windows;
15     ObDirection direction;
16     gboolean bar;
17     gboolean raise;
18     GSList *actions;
19 } Options;
20
21 static gboolean cycling = FALSE;
22
23 static gpointer setup_func(xmlNodePtr node);
24 static gpointer setup_cycle_func(xmlNodePtr node,
25                                  ObActionsIPreFunc *pre,
26                                  ObActionsIInputFunc *input,
27                                  ObActionsICancelFunc *cancel,
28                                  ObActionsIPostFunc *post);
29 static gpointer setup_target_func(xmlNodePtr node);
30 static void     free_func(gpointer options);
31 static gboolean run_func(ObActionsData *data, gpointer options);
32 static gboolean i_input_func(guint initial_state,
33                              XEvent *e,
34                              gpointer options,
35                              gboolean *used);
36 static void     i_cancel_func(gpointer options);
37
38 static void     end_cycle(gboolean cancel, guint state, Options *o);
39
40 /* 3.4-compatibility */
41 static gpointer setup_north_cycle_func(xmlNodePtr node,
42                                        ObActionsIPreFunc *pre,
43                                        ObActionsIInputFunc *in,
44                                        ObActionsICancelFunc *c,
45                                        ObActionsIPostFunc *post);
46 static gpointer setup_south_cycle_func(xmlNodePtr node,
47                                        ObActionsIPreFunc *pre,
48                                        ObActionsIInputFunc *in,
49                                        ObActionsICancelFunc *c,
50                                        ObActionsIPostFunc *post);
51 static gpointer setup_east_cycle_func(xmlNodePtr node,
52                                       ObActionsIPreFunc *pre,
53                                       ObActionsIInputFunc *in,
54                                       ObActionsICancelFunc *c,
55                                       ObActionsIPostFunc *post);
56 static gpointer setup_west_cycle_func(xmlNodePtr node,
57                                       ObActionsIPreFunc *pre,
58                                       ObActionsIInputFunc *in,
59                                       ObActionsICancelFunc *c,
60                                       ObActionsIPostFunc *post);
61 static gpointer setup_northwest_cycle_func(xmlNodePtr node,
62                                            ObActionsIPreFunc *pre,
63                                            ObActionsIInputFunc *in,
64                                            ObActionsICancelFunc *c,
65                                            ObActionsIPostFunc *post);
66 static gpointer setup_northeast_cycle_func(xmlNodePtr node,
67                                            ObActionsIPreFunc *pre,
68                                            ObActionsIInputFunc *in,
69                                            ObActionsICancelFunc *c,
70                                            ObActionsIPostFunc *post);
71 static gpointer setup_southwest_cycle_func(xmlNodePtr node,
72                                            ObActionsIPreFunc *pre,
73                                            ObActionsIInputFunc *in,
74                                            ObActionsICancelFunc *c,
75                                            ObActionsIPostFunc *post);
76 static gpointer setup_southeast_cycle_func(xmlNodePtr node,
77                                            ObActionsIPreFunc *pre,
78                                            ObActionsIInputFunc *in,
79                                            ObActionsICancelFunc *c,
80                                            ObActionsIPostFunc *post);
81 static gpointer setup_north_target_func(xmlNodePtr node);
82 static gpointer setup_south_target_func(xmlNodePtr node);
83 static gpointer setup_east_target_func(xmlNodePtr node);
84 static gpointer setup_west_target_func(xmlNodePtr node);
85 static gpointer setup_northwest_target_func(xmlNodePtr node);
86 static gpointer setup_northeast_target_func(xmlNodePtr node);
87 static gpointer setup_southwest_target_func(xmlNodePtr node);
88 static gpointer setup_southeast_target_func(xmlNodePtr node);
89
90 void action_directionalwindows_startup(void)
91 {
92     actions_register_i("DirectionalCycleWindows", setup_cycle_func, free_func,
93                        run_func);
94     actions_register("DirectionalTargetWindow", setup_target_func, free_func,
95                      run_func);
96     /* 3.4-compatibility */
97     actions_register_i("DirectionalFocusNorth", setup_north_cycle_func,
98                        free_func, run_func);
99     actions_register_i("DirectionalFocusSouth", setup_south_cycle_func,
100                        free_func, run_func);
101     actions_register_i("DirectionalFocusWest", setup_west_cycle_func,
102                        free_func, run_func);
103     actions_register_i("DirectionalFocusEast", setup_east_cycle_func,
104                        free_func, run_func);
105     actions_register_i("DirectionalFocusNorthWest", setup_northwest_cycle_func,
106                        free_func, run_func);
107     actions_register_i("DirectionalFocusNorthEast", setup_northeast_cycle_func,
108                        free_func, run_func);
109     actions_register_i("DirectionalFocusSouthWest", setup_southwest_cycle_func,
110                        free_func, run_func);
111     actions_register_i("DirectionalFocusSouthEast", setup_southeast_cycle_func,
112                        free_func, run_func);
113     actions_register("DirectionalTargetNorth", setup_north_target_func,
114                      free_func, run_func);
115     actions_register("DirectionalTargetSouth", setup_south_target_func,
116                      free_func, run_func);
117     actions_register("DirectionalTargetWest", setup_west_target_func,
118                      free_func, run_func);
119     actions_register("DirectionalTargetEast", setup_east_target_func,
120                      free_func, run_func);
121     actions_register("DirectionalTargetNorthWest", setup_northwest_target_func,
122                      free_func, run_func);
123     actions_register("DirectionalTargetNorthEast", setup_northeast_target_func,
124                      free_func, run_func);
125     actions_register("DirectionalTargetSouthWest", setup_southwest_target_func,
126                      free_func, run_func);
127     actions_register("DirectionalTargetSouthEast", setup_southeast_target_func,
128                      free_func, run_func);
129 }
130
131 static gpointer setup_func(xmlNodePtr node)
132 {
133     xmlNodePtr n;
134     Options *o;
135
136     o = g_new0(Options, 1);
137     o->dialog = TRUE;
138     o->bar = TRUE;
139
140     if ((n = obt_xml_find_node(node, "dialog")))
141         o->dialog = obt_xml_node_bool(n);
142     if ((n = obt_xml_find_node(node, "bar")))
143         o->bar = obt_xml_node_bool(n);
144     if ((n = obt_xml_find_node(node, "raise")))
145         o->raise = obt_xml_node_bool(n);
146     if ((n = obt_xml_find_node(node, "panels")))
147         o->dock_windows = obt_xml_node_bool(n);
148     if ((n = obt_xml_find_node(node, "desktop")))
149         o->desktop_windows = obt_xml_node_bool(n);
150     if ((n = obt_xml_find_node(node, "direction"))) {
151         gchar *s = obt_xml_node_string(n);
152         if (!g_ascii_strcasecmp(s, "north") ||
153             !g_ascii_strcasecmp(s, "up"))
154             o->direction = OB_DIRECTION_NORTH;
155         else if (!g_ascii_strcasecmp(s, "northwest"))
156             o->direction = OB_DIRECTION_NORTHWEST;
157         else if (!g_ascii_strcasecmp(s, "northeast"))
158             o->direction = OB_DIRECTION_NORTHEAST;
159         else if (!g_ascii_strcasecmp(s, "west") ||
160                  !g_ascii_strcasecmp(s, "left"))
161             o->direction = OB_DIRECTION_WEST;
162         else if (!g_ascii_strcasecmp(s, "east") ||
163                  !g_ascii_strcasecmp(s, "right"))
164             o->direction = OB_DIRECTION_EAST;
165         else if (!g_ascii_strcasecmp(s, "south") ||
166                  !g_ascii_strcasecmp(s, "down"))
167             o->direction = OB_DIRECTION_SOUTH;
168         else if (!g_ascii_strcasecmp(s, "southwest"))
169             o->direction = OB_DIRECTION_SOUTHWEST;
170         else if (!g_ascii_strcasecmp(s, "southeast"))
171             o->direction = OB_DIRECTION_SOUTHEAST;
172         g_free(s);
173     }
174
175     if ((n = obt_xml_find_node(node, "finalactions"))) {
176         xmlNodePtr m;
177
178         m = obt_xml_find_node(n->children, "action");
179         while (m) {
180             ObActionsAct *action = actions_parse(m);
181             if (action) o->actions = g_slist_append(o->actions, action);
182             m = obt_xml_find_node(m->next, "action");
183         }
184     }
185     else {
186         o->actions = g_slist_prepend(o->actions,
187                                      actions_parse_string("Focus"));
188         o->actions = g_slist_prepend(o->actions,
189                                      actions_parse_string("Raise"));
190         o->actions = g_slist_prepend(o->actions,
191                                      actions_parse_string("Unshade"));
192     }
193
194     return o;
195 }
196
197 static gpointer setup_cycle_func(xmlNodePtr node,
198                                  ObActionsIPreFunc *pre,
199                                  ObActionsIInputFunc *input,
200                                  ObActionsICancelFunc *cancel,
201                                  ObActionsIPostFunc *post)
202 {
203     Options *o = setup_func(node);
204     o->interactive = TRUE;
205     *input = i_input_func;
206     *cancel = i_cancel_func;
207     return o;
208 }
209
210 static gpointer setup_target_func(xmlNodePtr node)
211 {
212     Options *o = setup_func(node);
213     o->interactive = FALSE;
214     return o;
215 }
216
217 static void free_func(gpointer options)
218 {
219     Options *o = options;
220
221     while (o->actions) {
222         actions_act_unref(o->actions->data);
223         o->actions = g_slist_delete_link(o->actions, o->actions);
224     }
225
226     g_free(o);
227 }
228
229 static gboolean run_func(ObActionsData *data, gpointer options)
230 {
231     Options *o = options;
232
233     if (!o->interactive)
234         end_cycle(FALSE, data->state, o);
235     else {
236         struct _ObClient *ft;
237
238         ft = focus_directional_cycle(o->direction,
239                                      o->dock_windows,
240                                      o->desktop_windows,
241                                      TRUE,
242                                      o->bar,
243                                      o->dialog,
244                                      FALSE, FALSE);
245         cycling = TRUE;
246
247         stacking_restore();
248         if (o->raise && ft) stacking_temp_raise(CLIENT_AS_WINDOW(ft));
249     }
250
251     return o->interactive;
252 }
253
254 static gboolean i_input_func(guint initial_state,
255                              XEvent *e,
256                              gpointer options,
257                              gboolean *used)
258 {
259     guint mods;
260
261     mods = obt_keyboard_only_modmasks(e->xkey.state);
262     if (e->type == KeyRelease) {
263         /* remove from the state the mask of the modifier key being
264            released, if it is a modifier key being released that is */
265         mods &= ~obt_keyboard_keycode_to_modmask(e->xkey.keycode);
266     }
267
268     if (e->type == KeyPress) {
269         /* Escape cancels no matter what */
270         if (ob_keycode_match(e->xkey.keycode, OB_KEY_ESCAPE)) {
271             end_cycle(TRUE, e->xkey.state, options);
272             return FALSE;
273         }
274
275         /* There were no modifiers and they pressed enter */
276         else if (ob_keycode_match(e->xkey.keycode, OB_KEY_RETURN) &&
277                  !initial_state)
278         {
279             end_cycle(FALSE, e->xkey.state, options);
280             return FALSE;
281         }
282     }
283     /* They released the modifiers */
284     else if (e->type == KeyRelease && initial_state &&
285              (mods & initial_state) == 0)
286     {
287         end_cycle(FALSE, e->xkey.state, options);
288         return FALSE;
289     }
290
291     return TRUE;
292 }
293
294 static void i_cancel_func(gpointer options)
295 {
296     /* we get cancelled when we move focus, but we're not cycling anymore, so
297        just ignore that */
298     if (cycling)
299         end_cycle(TRUE, 0, options);
300 }
301
302 static void end_cycle(gboolean cancel, guint state, Options *o)
303 {
304     struct _ObClient *ft;
305
306     ft = focus_directional_cycle(o->direction,
307                                  o->dock_windows,
308                                  o->desktop_windows,
309                                  o->interactive,
310                                  o->bar,
311                                  o->dialog,
312                                  TRUE, cancel);
313     cycling = FALSE;
314
315     if (ft)
316         actions_run_acts(o->actions, OB_USER_ACTION_KEYBOARD_KEY,
317                          state, -1, -1, 0, OB_FRAME_CONTEXT_NONE, ft);
318
319     stacking_restore();
320 }
321
322 /* 3.4-compatibility */
323 static gpointer setup_north_cycle_func(xmlNodePtr node,
324                                        ObActionsIPreFunc *pre,
325                                        ObActionsIInputFunc *input,
326                                        ObActionsICancelFunc *cancel,
327                                        ObActionsIPostFunc *post)
328 {
329     Options *o = setup_cycle_func(node, pre, input, cancel, post);
330     o->direction = OB_DIRECTION_NORTH;
331     return o;
332 }
333
334 static gpointer setup_south_cycle_func(xmlNodePtr node,
335                                        ObActionsIPreFunc *pre,
336                                        ObActionsIInputFunc *input,
337                                        ObActionsICancelFunc *cancel,
338                                        ObActionsIPostFunc *post)
339 {
340     Options *o = setup_cycle_func(node, pre, input, cancel, post);
341     o->direction = OB_DIRECTION_SOUTH;
342     return o;
343 }
344
345 static gpointer setup_east_cycle_func(xmlNodePtr node,
346                                       ObActionsIPreFunc *pre,
347                                       ObActionsIInputFunc *input,
348                                       ObActionsICancelFunc *cancel,
349                                       ObActionsIPostFunc *post)
350 {
351     Options *o = setup_cycle_func(node, pre, input, cancel, post);
352     o->direction = OB_DIRECTION_EAST;
353     return o;
354 }
355
356 static gpointer setup_west_cycle_func(xmlNodePtr node,
357                                       ObActionsIPreFunc *pre,
358                                       ObActionsIInputFunc *input,
359                                       ObActionsICancelFunc *cancel,
360                                       ObActionsIPostFunc *post)
361 {
362     Options *o = setup_cycle_func(node, pre, input, cancel, post);
363     o->direction = OB_DIRECTION_WEST;
364     return o;
365 }
366
367 static gpointer setup_northwest_cycle_func(xmlNodePtr node,
368                                            ObActionsIPreFunc *pre,
369                                            ObActionsIInputFunc *input,
370                                            ObActionsICancelFunc *cancel,
371                                            ObActionsIPostFunc *post)
372 {
373     Options *o = setup_cycle_func(node, pre, input, cancel, post);
374     o->direction = OB_DIRECTION_NORTHWEST;
375     return o;
376 }
377
378 static gpointer setup_northeast_cycle_func(xmlNodePtr node,
379                                            ObActionsIPreFunc *pre,
380                                            ObActionsIInputFunc *input,
381                                            ObActionsICancelFunc *cancel,
382                                            ObActionsIPostFunc *post)
383 {
384     Options *o = setup_cycle_func(node, pre, input, cancel, post);
385     o->direction = OB_DIRECTION_EAST;
386     return o;
387 }
388
389 static gpointer setup_southwest_cycle_func(xmlNodePtr node,
390                                            ObActionsIPreFunc *pre,
391                                            ObActionsIInputFunc *input,
392                                            ObActionsICancelFunc *cancel,
393                                            ObActionsIPostFunc *post)
394 {
395     Options *o = setup_cycle_func(node, pre, input, cancel, post);
396     o->direction = OB_DIRECTION_SOUTHWEST;
397     return o;
398 }
399
400 static gpointer setup_southeast_cycle_func(xmlNodePtr node,
401                                            ObActionsIPreFunc *pre,
402                                            ObActionsIInputFunc *input,
403                                            ObActionsICancelFunc *cancel,
404                                            ObActionsIPostFunc *post)
405 {
406     Options *o = setup_cycle_func(node, pre, input, cancel, post);
407     o->direction = OB_DIRECTION_SOUTHEAST;
408     return o;
409 }
410
411 static gpointer setup_north_target_func(xmlNodePtr node)
412 {
413     Options *o = setup_target_func(node);
414     o->direction = OB_DIRECTION_NORTH;
415     return o;
416 }
417
418 static gpointer setup_south_target_func(xmlNodePtr node)
419 {
420     Options *o = setup_target_func(node);
421     o->direction = OB_DIRECTION_SOUTH;
422     return o;
423 }
424
425 static gpointer setup_east_target_func(xmlNodePtr node)
426 {
427     Options *o = setup_target_func(node);
428     o->direction = OB_DIRECTION_EAST;
429     return o;
430 }
431
432 static gpointer setup_west_target_func(xmlNodePtr node)
433 {
434     Options *o = setup_target_func(node);
435     o->direction = OB_DIRECTION_WEST;
436     return o;
437 }
438
439 static gpointer setup_northwest_target_func(xmlNodePtr node)
440 {
441     Options *o = setup_target_func(node);
442     o->direction = OB_DIRECTION_NORTHWEST;
443     return o;
444 }
445
446 static gpointer setup_northeast_target_func(xmlNodePtr node)
447 {
448     Options *o = setup_target_func(node);
449     o->direction = OB_DIRECTION_NORTHEAST;
450     return o;
451 }
452
453 static gpointer setup_southwest_target_func(xmlNodePtr node)
454 {
455     Options *o = setup_target_func(node);
456     o->direction = OB_DIRECTION_SOUTHWEST;
457     return o;
458 }
459
460 static gpointer setup_southeast_target_func(xmlNodePtr node)
461 {
462     Options *o = setup_target_func(node);
463     o->direction = OB_DIRECTION_SOUTHEAST;
464     return o;
465 }
466