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