pass an input context to the interactive input function
[dana/openbox.git] / openbox / actions / desktop.c
1 #include "openbox/actions.h"
2 #include "openbox/screen.h"
3 #include "openbox/client.h"
4 #include "openbox/openbox.h"
5 #include "obt/keyboard.h"
6
7 typedef enum {
8     LAST,
9     RELATIVE,
10     ABSOLUTE
11 } SwitchType;
12
13 typedef struct {
14     SwitchType type;
15     union {
16         struct {
17             guint desktop;
18         } abs;
19
20         struct {
21             gboolean linear;
22             gboolean wrap;
23             ObDirection dir;
24         } rel;
25     } u;
26     gboolean send;
27     gboolean follow;
28     gboolean interactive;
29 } Options;
30
31 static gpointer setup_go_func(xmlNodePtr node,
32                               ObActionsIPreFunc *pre,
33                               ObActionsIInputFunc *input,
34                               ObActionsICancelFunc *cancel,
35                               ObActionsIPostFunc *post);
36 static gpointer setup_send_func(xmlNodePtr node,
37                                 ObActionsIPreFunc *pre,
38                                 ObActionsIInputFunc *input,
39                                 ObActionsICancelFunc *cancel,
40                                 ObActionsIPostFunc *post);
41 static gboolean run_func(ObActionsData *data, gpointer options);
42
43 static gboolean i_pre_func(guint state, gpointer options);
44 static gboolean i_input_func(guint initial_state,
45                              XEvent *e,
46                              ObtIC *ic,
47                              gpointer options,
48                              gboolean *used);
49 static void i_post_func(gpointer options);
50
51 /* 3.4-compatibility */
52 static gpointer setup_go_last_func(xmlNodePtr node);
53 static gpointer setup_send_last_func(xmlNodePtr node);
54 static gpointer setup_go_abs_func(xmlNodePtr node);
55 static gpointer setup_send_abs_func(xmlNodePtr node);
56 static gpointer setup_go_next_func(xmlNodePtr node,
57                                    ObActionsIPreFunc *pre,
58                                    ObActionsIInputFunc *input,
59                                    ObActionsICancelFunc *cancel,
60                                    ObActionsIPostFunc *post);
61 static gpointer setup_send_next_func(xmlNodePtr node,
62                                      ObActionsIPreFunc *pre,
63                                      ObActionsIInputFunc *input,
64                                      ObActionsICancelFunc *cancel,
65                                      ObActionsIPostFunc *post);
66 static gpointer setup_go_prev_func(xmlNodePtr node,
67                                    ObActionsIPreFunc *pre,
68                                    ObActionsIInputFunc *input,
69                                    ObActionsICancelFunc *cancel,
70                                    ObActionsIPostFunc *post);
71 static gpointer setup_send_prev_func(xmlNodePtr node,
72                                      ObActionsIPreFunc *pre,
73                                      ObActionsIInputFunc *input,
74                                      ObActionsICancelFunc *cancel,
75                                      ObActionsIPostFunc *post);
76 static gpointer setup_go_left_func(xmlNodePtr node,
77                                    ObActionsIPreFunc *pre,
78                                    ObActionsIInputFunc *input,
79                                    ObActionsICancelFunc *cancel,
80                                    ObActionsIPostFunc *post);
81 static gpointer setup_send_left_func(xmlNodePtr node,
82                                      ObActionsIPreFunc *pre,
83                                      ObActionsIInputFunc *input,
84                                      ObActionsICancelFunc *cancel,
85                                      ObActionsIPostFunc *post);
86 static gpointer setup_go_right_func(xmlNodePtr node,
87                                     ObActionsIPreFunc *pre,
88                                     ObActionsIInputFunc *input,
89                                     ObActionsICancelFunc *cancel,
90                                     ObActionsIPostFunc *post);
91 static gpointer setup_send_right_func(xmlNodePtr node,
92                                       ObActionsIPreFunc *pre,
93                                       ObActionsIInputFunc *input,
94                                       ObActionsICancelFunc *cancel,
95                                       ObActionsIPostFunc *post);
96 static gpointer setup_go_up_func(xmlNodePtr node,
97                                  ObActionsIPreFunc *pre,
98                                  ObActionsIInputFunc *input,
99                                  ObActionsICancelFunc *cancel,
100                                  ObActionsIPostFunc *post);
101 static gpointer setup_send_up_func(xmlNodePtr node,
102                                    ObActionsIPreFunc *pre,
103                                    ObActionsIInputFunc *input,
104                                    ObActionsICancelFunc *cancel,
105                                    ObActionsIPostFunc *post);
106 static gpointer setup_go_down_func(xmlNodePtr node,
107                                    ObActionsIPreFunc *pre,
108                                    ObActionsIInputFunc *input,
109                                    ObActionsICancelFunc *cancel,
110                                    ObActionsIPostFunc *post);
111 static gpointer setup_send_down_func(xmlNodePtr node,
112                                      ObActionsIPreFunc *pre,
113                                      ObActionsIInputFunc *input,
114                                      ObActionsICancelFunc *cancel,
115                                      ObActionsIPostFunc *post);
116  
117 void action_desktop_startup(void)
118 {
119     actions_register_i("GoToDesktop", setup_go_func, g_free, run_func);
120     actions_register_i("SendToDesktop", setup_send_func, g_free, run_func);
121     /* 3.4-compatibility */
122     actions_register("DesktopLast", setup_go_last_func, g_free, run_func);
123     actions_register("SendToDesktopLast", setup_send_last_func,
124                      g_free, run_func);
125     actions_register("Desktop", setup_go_abs_func, g_free, run_func);
126     actions_register("SendToDesktop", setup_send_abs_func, g_free, run_func);
127     actions_register_i("DesktopNext", setup_go_next_func, g_free, run_func);
128     actions_register_i("SendToDesktopNext", setup_send_next_func,
129                        g_free, run_func);
130     actions_register_i("DesktopPrevious", setup_go_prev_func,
131                        g_free, run_func);
132     actions_register_i("SendToDesktopPrevious", setup_send_prev_func,
133                        g_free, run_func);
134     actions_register_i("DesktopLeft", setup_go_left_func, g_free, run_func);
135     actions_register_i("SendToDesktopLeft", setup_send_left_func,
136                        g_free, run_func);
137     actions_register_i("DesktopRight", setup_go_right_func, g_free, run_func);
138     actions_register_i("SendToDesktopRight", setup_send_right_func,
139                        g_free, run_func);
140     actions_register_i("DesktopUp", setup_go_up_func, g_free, run_func);
141     actions_register_i("SendToDesktopUp", setup_send_up_func,
142                        g_free, run_func);
143     actions_register_i("DesktopDown", setup_go_down_func, g_free, run_func);
144     actions_register_i("SendToDesktopDown", setup_send_down_func,
145                        g_free, run_func);
146 }
147
148 static gpointer setup_func(xmlNodePtr node,
149                            ObActionsIPreFunc *pre,
150                            ObActionsIInputFunc *input,
151                            ObActionsICancelFunc *cancel,
152                            ObActionsIPostFunc *post)
153 {
154     xmlNodePtr n;
155     Options *o;
156
157     o = g_new0(Options, 1);
158     /* don't go anywhere if there are no options given */
159     o->type = ABSOLUTE;
160     o->u.abs.desktop = screen_desktop;
161     /* wrap by default - it's handy! */
162     o->u.rel.wrap = TRUE;
163
164     if ((n = obt_xml_find_node(node, "to"))) {
165         gchar *s = obt_xml_node_string(n);
166         if (!g_ascii_strcasecmp(s, "last"))
167             o->type = LAST;
168         else if (!g_ascii_strcasecmp(s, "next")) {
169             o->type = RELATIVE;
170             o->u.rel.linear = TRUE;
171             o->u.rel.dir = OB_DIRECTION_EAST;
172         }
173         else if (!g_ascii_strcasecmp(s, "previous")) {
174             o->type = RELATIVE;
175             o->u.rel.linear = TRUE;
176             o->u.rel.dir = OB_DIRECTION_WEST;
177         }
178         else if (!g_ascii_strcasecmp(s, "north") ||
179                  !g_ascii_strcasecmp(s, "up")) {
180             o->type = RELATIVE;
181             o->u.rel.dir = OB_DIRECTION_NORTH;
182         }
183         else if (!g_ascii_strcasecmp(s, "south") ||
184                  !g_ascii_strcasecmp(s, "down")) {
185             o->type = RELATIVE;
186             o->u.rel.dir = OB_DIRECTION_SOUTH;
187         }
188         else if (!g_ascii_strcasecmp(s, "west") ||
189                  !g_ascii_strcasecmp(s, "left")) {
190             o->type = RELATIVE;
191             o->u.rel.dir = OB_DIRECTION_WEST;
192         }
193         else if (!g_ascii_strcasecmp(s, "east") ||
194                  !g_ascii_strcasecmp(s, "right")) {
195             o->type = RELATIVE;
196             o->u.rel.dir = OB_DIRECTION_EAST;
197         }
198         else {
199             o->type = ABSOLUTE;
200             o->u.abs.desktop = atoi(s) - 1;
201         }
202         g_free(s);
203     }
204
205     if ((n = obt_xml_find_node(node, "wrap")))
206         o->u.rel.wrap = obt_xml_node_bool(n);
207
208     return o;
209 }
210
211
212 static gpointer setup_go_func(xmlNodePtr node,
213                               ObActionsIPreFunc *pre,
214                               ObActionsIInputFunc *input,
215                               ObActionsICancelFunc *cancel,
216                               ObActionsIPostFunc *post)
217 {
218     Options *o;
219
220     o = setup_func(node, pre, input, cancel, post);
221     if (o->type == RELATIVE) {
222         o->interactive = TRUE;
223         *pre = i_pre_func;
224         *input = i_input_func;
225         *post = i_post_func;
226     }
227
228     return o;
229 }
230
231 static gpointer setup_send_func(xmlNodePtr node,
232                                 ObActionsIPreFunc *pre,
233                                 ObActionsIInputFunc *input,
234                                 ObActionsICancelFunc *cancel,
235                                 ObActionsIPostFunc *post)
236 {
237     xmlNodePtr n;
238     Options *o;
239
240     o = setup_func(node, pre, input, cancel, post);
241     o->send = TRUE;
242     o->follow = TRUE;
243
244     if ((n = obt_xml_find_node(node, "follow")))
245         o->follow = obt_xml_node_bool(n);
246
247     if (o->type == RELATIVE && o->follow) {
248         o->interactive = TRUE;
249         *pre = i_pre_func;
250         *input = i_input_func;
251         *post = i_post_func;
252     }
253
254     return o;
255 }
256
257 /* Always return FALSE because its not interactive */
258 static gboolean run_func(ObActionsData *data, gpointer options)
259 {
260     Options *o = options;
261     guint d;
262
263     switch (o->type) {
264     case LAST:
265         d = screen_last_desktop;
266         break;
267     case ABSOLUTE:
268         d = o->u.abs.desktop;
269         break;
270     case RELATIVE:
271         d = screen_find_desktop(screen_desktop,
272                                 o->u.rel.dir, o->u.rel.wrap, o->u.rel.linear);
273         break;
274     default:
275         g_assert_not_reached();
276     }
277
278     if (d < screen_num_desktops && d != screen_desktop) {
279         gboolean go = TRUE;
280
281         actions_client_move(data, TRUE);
282         if (o->send && data->client && client_normal(data->client)) {
283             client_set_desktop(data->client, d, o->follow, FALSE);
284             go = o->follow;
285         }
286
287         if (go) {
288             screen_set_desktop(d, TRUE);
289             if (data->client)
290                 client_bring_helper_windows(data->client);
291         }
292
293         actions_client_move(data, FALSE);
294     }
295
296     return o->interactive;
297 }
298
299 static gboolean i_input_func(guint initial_state,
300                              XEvent *e,
301                              ObtIC *ic,
302                              gpointer options,
303                              gboolean *used)
304 {
305     guint mods;
306
307     mods = obt_keyboard_only_modmasks(e->xkey.state);
308     if (e->type == KeyRelease) {
309         /* remove from the state the mask of the modifier key being
310            released, if it is a modifier key being released that is */
311         mods &= ~obt_keyboard_keycode_to_modmask(e->xkey.keycode);
312     }
313
314     if (e->type == KeyPress) {
315         /* Escape cancels no matter what */
316         if (ob_keycode_match(e->xkey.keycode, OB_KEY_ESCAPE)) {
317             return FALSE;
318         }
319
320         /* There were no modifiers and they pressed enter */
321         else if (ob_keycode_match(e->xkey.keycode, OB_KEY_RETURN) &&
322                  !initial_state)
323         {
324             return FALSE;
325         }
326     }
327     /* They released the modifiers */
328     else if (e->type == KeyRelease && initial_state && !(mods & initial_state))
329     {
330         return FALSE;
331     }
332
333     return TRUE;
334 }
335
336 static gboolean i_pre_func(guint initial_state, gpointer options)
337 {
338     if (!initial_state) {
339         Options *o = options;
340         o->interactive = FALSE;
341         return FALSE;
342     }
343     else {
344         screen_show_desktop_popup(screen_desktop, TRUE);
345         return TRUE;
346     }
347 }
348
349 static void i_post_func(gpointer options)
350 {
351     screen_hide_desktop_popup();
352 }
353
354 /* 3.4-compatilibity */
355 static gpointer setup_follow(xmlNodePtr node)
356 {
357     xmlNodePtr n;
358     Options *o = g_new0(Options, 1);
359     o->send = TRUE;
360     o->follow = TRUE;
361     if ((n = obt_xml_find_node(node, "follow")))
362         o->follow = obt_xml_node_bool(n);
363     return o;
364 }
365
366 static gpointer setup_go_last_func(xmlNodePtr node)
367 {
368     Options *o = g_new0(Options, 1);
369     o->type = LAST;
370     return o;
371 }
372
373 static gpointer setup_send_last_func(xmlNodePtr node)
374 {
375     Options *o = setup_follow(node);
376     o->type = LAST;
377     return o;
378 }
379
380 static gpointer setup_go_abs_func(xmlNodePtr node)
381 {
382     xmlNodePtr n;
383     Options *o = g_new0(Options, 1);
384     o->type = ABSOLUTE;
385     if ((n = obt_xml_find_node(node, "desktop")))
386         o->u.abs.desktop = obt_xml_node_int(n) - 1;
387     else
388         o->u.abs.desktop = screen_desktop;
389     return o;
390 }
391
392 static gpointer setup_send_abs_func(xmlNodePtr node)
393 {
394     xmlNodePtr n;
395     Options *o = setup_follow(node);
396     o->type = ABSOLUTE;
397     if ((n = obt_xml_find_node(node, "desktop")))
398         o->u.abs.desktop = obt_xml_node_int(n) - 1;
399     else
400         o->u.abs.desktop = screen_desktop;
401     return o;
402 }
403
404 static void setup_rel(Options *o, xmlNodePtr node, gboolean lin,
405                       ObDirection dir,
406                       ObActionsIPreFunc *pre,
407                       ObActionsIInputFunc *input,
408                       ObActionsIPostFunc *post)
409 {
410     xmlNodePtr n;
411
412     o->type = RELATIVE;
413     o->u.rel.linear = lin;
414     o->u.rel.dir = dir;
415     o->u.rel.wrap = TRUE;
416
417     if ((n = obt_xml_find_node(node, "wrap")))
418         o->u.rel.wrap = obt_xml_node_bool(n);
419
420     if (input) {
421         o->interactive = TRUE;
422         *pre = i_pre_func;
423         *input = i_input_func;
424         *post = i_post_func;
425     }
426 }
427
428 static gpointer setup_go_next_func(xmlNodePtr node,
429                                    ObActionsIPreFunc *pre,
430                                    ObActionsIInputFunc *input,
431                                    ObActionsICancelFunc *cancel,
432                                    ObActionsIPostFunc *post)
433 {
434     Options *o = g_new0(Options, 1);
435     setup_rel(o, node, TRUE, OB_DIRECTION_EAST, pre, input, post);
436     return o;
437 }
438
439 static gpointer setup_send_next_func(xmlNodePtr node,
440                                      ObActionsIPreFunc *pre,
441                                      ObActionsIInputFunc *input,
442                                      ObActionsICancelFunc *cancel,
443                                      ObActionsIPostFunc *post)
444 {
445     Options *o = setup_follow(node);
446     setup_rel(o, node, TRUE, OB_DIRECTION_EAST,
447               pre, (o->follow ? input : NULL), post);
448     return o;
449 }
450
451 static gpointer setup_go_prev_func(xmlNodePtr node,
452                                    ObActionsIPreFunc *pre,
453                                    ObActionsIInputFunc *input,
454                                    ObActionsICancelFunc *cancel,
455                                    ObActionsIPostFunc *post)
456 {
457     Options *o = g_new0(Options, 1);
458     setup_rel(o, node, TRUE, OB_DIRECTION_WEST, pre, input, post);
459     return o;
460 }
461
462 static gpointer setup_send_prev_func(xmlNodePtr node,
463                                      ObActionsIPreFunc *pre,
464                                      ObActionsIInputFunc *input,
465                                      ObActionsICancelFunc *cancel,
466                                      ObActionsIPostFunc *post)
467 {
468     Options *o = setup_follow(node);
469     setup_rel(o, node, TRUE, OB_DIRECTION_WEST,
470               pre, (o->follow ? input : NULL), post);
471     return o;
472 }
473
474 static gpointer setup_go_left_func(xmlNodePtr node,
475                                    ObActionsIPreFunc *pre,
476                                    ObActionsIInputFunc *input,
477                                    ObActionsICancelFunc *cancel,
478                                    ObActionsIPostFunc *post)
479 {
480     Options *o = g_new0(Options, 1);
481     setup_rel(o, node, FALSE, OB_DIRECTION_WEST, pre, input, post);
482     return o;
483 }
484
485 static gpointer setup_send_left_func(xmlNodePtr node,
486                                      ObActionsIPreFunc *pre,
487                                      ObActionsIInputFunc *input,
488                                      ObActionsICancelFunc *cancel,
489                                      ObActionsIPostFunc *post)
490 {
491     Options *o = setup_follow(node);
492     setup_rel(o, node, FALSE, OB_DIRECTION_WEST,
493               pre, (o->follow ? input : NULL), post);
494     return o;
495 }
496
497 static gpointer setup_go_right_func(xmlNodePtr node,
498                                     ObActionsIPreFunc *pre,
499                                     ObActionsIInputFunc *input,
500                                     ObActionsICancelFunc *cancel,
501                                     ObActionsIPostFunc *post)
502 {
503     Options *o = g_new0(Options, 1);
504     setup_rel(o, node, FALSE, OB_DIRECTION_EAST, pre, input, post);
505     return o;
506 }
507
508 static gpointer setup_send_right_func(xmlNodePtr node,
509                                       ObActionsIPreFunc *pre,
510                                       ObActionsIInputFunc *input,
511                                       ObActionsICancelFunc *cancel,
512                                       ObActionsIPostFunc *post)
513 {
514     Options *o = setup_follow(node);
515     setup_rel(o, node, FALSE, OB_DIRECTION_EAST,
516               pre, (o->follow ? input : NULL), post);
517     return o;
518 }
519
520 static gpointer setup_go_up_func(xmlNodePtr node,
521                                  ObActionsIPreFunc *pre,
522                                  ObActionsIInputFunc *input,
523                                  ObActionsICancelFunc *cancel,
524                                  ObActionsIPostFunc *post)
525 {
526     Options *o = g_new0(Options, 1);
527     setup_rel(o, node, FALSE, OB_DIRECTION_NORTH, pre, input, post);
528     return o;
529 }
530
531 static gpointer setup_send_up_func(xmlNodePtr node,
532                                    ObActionsIPreFunc *pre,
533                                    ObActionsIInputFunc *input,
534                                    ObActionsICancelFunc *cancel,
535                                    ObActionsIPostFunc *post)
536 {
537     Options *o = setup_follow(node);
538     setup_rel(o, node, FALSE, OB_DIRECTION_NORTH,
539               pre, (o->follow ? input : NULL), post);
540     return o;
541 }
542
543 static gpointer setup_go_down_func(xmlNodePtr node,
544                                    ObActionsIPreFunc *pre,
545                                    ObActionsIInputFunc *input,
546                                    ObActionsICancelFunc *cancel,
547                                    ObActionsIPostFunc *post)
548 {
549     Options *o = g_new0(Options, 1);
550     setup_rel(o, node, FALSE, OB_DIRECTION_SOUTH, pre, input, post);
551     return o;
552 }
553
554 static gpointer setup_send_down_func(xmlNodePtr node,
555                                      ObActionsIPreFunc *pre,
556                                      ObActionsIInputFunc *input,
557                                      ObActionsICancelFunc *cancel,
558                                      ObActionsIPostFunc *post)
559 {
560     Options *o = setup_follow(node);
561     setup_rel(o, node, FALSE, OB_DIRECTION_SOUTH,
562               pre, (o->follow ? input : NULL), post);
563     return o;
564 }