6c30d56de3fb9e28df54e36d4bc358c8083a2bb0
[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         KeySym sym = obt_keyboard_keypress_to_keysym(e);
316
317         /* Escape cancels no matter what */
318         if (sym == XK_Escape)
319             return FALSE;
320
321         /* There were no modifiers and they pressed enter */
322         else if (sym == XK_Return && !initial_state)
323             return FALSE;
324     }
325     /* They released the modifiers */
326     else if (e->type == KeyRelease && initial_state && !(mods & initial_state))
327     {
328         return FALSE;
329     }
330
331     return TRUE;
332 }
333
334 static gboolean i_pre_func(guint initial_state, gpointer options)
335 {
336     if (!initial_state) {
337         Options *o = options;
338         o->interactive = FALSE;
339         return FALSE;
340     }
341     else {
342         screen_show_desktop_popup(screen_desktop, TRUE);
343         return TRUE;
344     }
345 }
346
347 static void i_post_func(gpointer options)
348 {
349     screen_hide_desktop_popup();
350 }
351
352 /* 3.4-compatilibity */
353 static gpointer setup_follow(xmlNodePtr node)
354 {
355     xmlNodePtr n;
356     Options *o = g_new0(Options, 1);
357     o->send = TRUE;
358     o->follow = TRUE;
359     if ((n = obt_xml_find_node(node, "follow")))
360         o->follow = obt_xml_node_bool(n);
361     return o;
362 }
363
364 static gpointer setup_go_last_func(xmlNodePtr node)
365 {
366     Options *o = g_new0(Options, 1);
367     o->type = LAST;
368     return o;
369 }
370
371 static gpointer setup_send_last_func(xmlNodePtr node)
372 {
373     Options *o = setup_follow(node);
374     o->type = LAST;
375     return o;
376 }
377
378 static gpointer setup_go_abs_func(xmlNodePtr node)
379 {
380     xmlNodePtr n;
381     Options *o = g_new0(Options, 1);
382     o->type = ABSOLUTE;
383     if ((n = obt_xml_find_node(node, "desktop")))
384         o->u.abs.desktop = obt_xml_node_int(n) - 1;
385     else
386         o->u.abs.desktop = screen_desktop;
387     return o;
388 }
389
390 static gpointer setup_send_abs_func(xmlNodePtr node)
391 {
392     xmlNodePtr n;
393     Options *o = setup_follow(node);
394     o->type = ABSOLUTE;
395     if ((n = obt_xml_find_node(node, "desktop")))
396         o->u.abs.desktop = obt_xml_node_int(n) - 1;
397     else
398         o->u.abs.desktop = screen_desktop;
399     return o;
400 }
401
402 static void setup_rel(Options *o, xmlNodePtr node, gboolean lin,
403                       ObDirection dir,
404                       ObActionsIPreFunc *pre,
405                       ObActionsIInputFunc *input,
406                       ObActionsIPostFunc *post)
407 {
408     xmlNodePtr n;
409
410     o->type = RELATIVE;
411     o->u.rel.linear = lin;
412     o->u.rel.dir = dir;
413     o->u.rel.wrap = TRUE;
414
415     if ((n = obt_xml_find_node(node, "wrap")))
416         o->u.rel.wrap = obt_xml_node_bool(n);
417
418     if (input) {
419         o->interactive = TRUE;
420         *pre = i_pre_func;
421         *input = i_input_func;
422         *post = i_post_func;
423     }
424 }
425
426 static gpointer setup_go_next_func(xmlNodePtr node,
427                                    ObActionsIPreFunc *pre,
428                                    ObActionsIInputFunc *input,
429                                    ObActionsICancelFunc *cancel,
430                                    ObActionsIPostFunc *post)
431 {
432     Options *o = g_new0(Options, 1);
433     setup_rel(o, node, TRUE, OB_DIRECTION_EAST, pre, input, post);
434     return o;
435 }
436
437 static gpointer setup_send_next_func(xmlNodePtr node,
438                                      ObActionsIPreFunc *pre,
439                                      ObActionsIInputFunc *input,
440                                      ObActionsICancelFunc *cancel,
441                                      ObActionsIPostFunc *post)
442 {
443     Options *o = setup_follow(node);
444     setup_rel(o, node, TRUE, OB_DIRECTION_EAST,
445               pre, (o->follow ? input : NULL), post);
446     return o;
447 }
448
449 static gpointer setup_go_prev_func(xmlNodePtr node,
450                                    ObActionsIPreFunc *pre,
451                                    ObActionsIInputFunc *input,
452                                    ObActionsICancelFunc *cancel,
453                                    ObActionsIPostFunc *post)
454 {
455     Options *o = g_new0(Options, 1);
456     setup_rel(o, node, TRUE, OB_DIRECTION_WEST, pre, input, post);
457     return o;
458 }
459
460 static gpointer setup_send_prev_func(xmlNodePtr node,
461                                      ObActionsIPreFunc *pre,
462                                      ObActionsIInputFunc *input,
463                                      ObActionsICancelFunc *cancel,
464                                      ObActionsIPostFunc *post)
465 {
466     Options *o = setup_follow(node);
467     setup_rel(o, node, TRUE, OB_DIRECTION_WEST,
468               pre, (o->follow ? input : NULL), post);
469     return o;
470 }
471
472 static gpointer setup_go_left_func(xmlNodePtr node,
473                                    ObActionsIPreFunc *pre,
474                                    ObActionsIInputFunc *input,
475                                    ObActionsICancelFunc *cancel,
476                                    ObActionsIPostFunc *post)
477 {
478     Options *o = g_new0(Options, 1);
479     setup_rel(o, node, FALSE, OB_DIRECTION_WEST, pre, input, post);
480     return o;
481 }
482
483 static gpointer setup_send_left_func(xmlNodePtr node,
484                                      ObActionsIPreFunc *pre,
485                                      ObActionsIInputFunc *input,
486                                      ObActionsICancelFunc *cancel,
487                                      ObActionsIPostFunc *post)
488 {
489     Options *o = setup_follow(node);
490     setup_rel(o, node, FALSE, OB_DIRECTION_WEST,
491               pre, (o->follow ? input : NULL), post);
492     return o;
493 }
494
495 static gpointer setup_go_right_func(xmlNodePtr node,
496                                     ObActionsIPreFunc *pre,
497                                     ObActionsIInputFunc *input,
498                                     ObActionsICancelFunc *cancel,
499                                     ObActionsIPostFunc *post)
500 {
501     Options *o = g_new0(Options, 1);
502     setup_rel(o, node, FALSE, OB_DIRECTION_EAST, pre, input, post);
503     return o;
504 }
505
506 static gpointer setup_send_right_func(xmlNodePtr node,
507                                       ObActionsIPreFunc *pre,
508                                       ObActionsIInputFunc *input,
509                                       ObActionsICancelFunc *cancel,
510                                       ObActionsIPostFunc *post)
511 {
512     Options *o = setup_follow(node);
513     setup_rel(o, node, FALSE, OB_DIRECTION_EAST,
514               pre, (o->follow ? input : NULL), post);
515     return o;
516 }
517
518 static gpointer setup_go_up_func(xmlNodePtr node,
519                                  ObActionsIPreFunc *pre,
520                                  ObActionsIInputFunc *input,
521                                  ObActionsICancelFunc *cancel,
522                                  ObActionsIPostFunc *post)
523 {
524     Options *o = g_new0(Options, 1);
525     setup_rel(o, node, FALSE, OB_DIRECTION_NORTH, pre, input, post);
526     return o;
527 }
528
529 static gpointer setup_send_up_func(xmlNodePtr node,
530                                    ObActionsIPreFunc *pre,
531                                    ObActionsIInputFunc *input,
532                                    ObActionsICancelFunc *cancel,
533                                    ObActionsIPostFunc *post)
534 {
535     Options *o = setup_follow(node);
536     setup_rel(o, node, FALSE, OB_DIRECTION_NORTH,
537               pre, (o->follow ? input : NULL), post);
538     return o;
539 }
540
541 static gpointer setup_go_down_func(xmlNodePtr node,
542                                    ObActionsIPreFunc *pre,
543                                    ObActionsIInputFunc *input,
544                                    ObActionsICancelFunc *cancel,
545                                    ObActionsIPostFunc *post)
546 {
547     Options *o = g_new0(Options, 1);
548     setup_rel(o, node, FALSE, OB_DIRECTION_SOUTH, pre, input, post);
549     return o;
550 }
551
552 static gpointer setup_send_down_func(xmlNodePtr node,
553                                      ObActionsIPreFunc *pre,
554                                      ObActionsIInputFunc *input,
555                                      ObActionsICancelFunc *cancel,
556                                      ObActionsIPostFunc *post)
557 {
558     Options *o = setup_follow(node);
559     setup_rel(o, node, FALSE, OB_DIRECTION_SOUTH,
560               pre, (o->follow ? input : NULL), post);
561     return o;
562 }