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