]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/actions/desktop.c
Forgot to init restack sibling to NULL
[mikachu/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     CURRENT,
10     RELATIVE,
11     ABSOLUTE
12 } SwitchType;
13
14 typedef struct {
15     SwitchType type;
16     union {
17         struct {
18             guint desktop;
19         } abs;
20
21         struct {
22             gboolean linear;
23             gboolean wrap;
24             ObDirection dir;
25         } rel;
26     } u;
27     gboolean send;
28     gboolean follow;
29     gboolean interactive;
30 } Options;
31
32 static gpointer setup_go_func(xmlNodePtr node,
33                               ObActionsIPreFunc *pre,
34                               ObActionsIInputFunc *input,
35                               ObActionsICancelFunc *cancel,
36                               ObActionsIPostFunc *post);
37 static gpointer setup_send_func(xmlNodePtr node,
38                                 ObActionsIPreFunc *pre,
39                                 ObActionsIInputFunc *input,
40                                 ObActionsICancelFunc *cancel,
41                                 ObActionsIPostFunc *post);
42 static void free_func(gpointer o);
43 static gboolean run_func(ObActionsData *data, gpointer options);
44
45 static gboolean i_pre_func(guint state, gpointer options);
46 static gboolean i_input_func(guint initial_state,
47                              XEvent *e,
48                              ObtIC *ic,
49                              gpointer options,
50                              gboolean *used);
51 static void i_post_func(gpointer options);
52
53 /* 3.4-compatibility */
54 static gpointer setup_go_last_func(xmlNodePtr node);
55 static gpointer setup_send_last_func(xmlNodePtr node);
56 static gpointer setup_go_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_i("DesktopNext", setup_go_next_func, free_func, run_func);
128     actions_register_i("SendToDesktopNext", setup_send_next_func,
129                        free_func, run_func);
130     actions_register_i("DesktopPrevious", setup_go_prev_func,
131                        free_func, run_func);
132     actions_register_i("SendToDesktopPrevious", setup_send_prev_func,
133                        free_func, run_func);
134     actions_register_i("DesktopLeft", setup_go_left_func, free_func, run_func);
135     actions_register_i("SendToDesktopLeft", setup_send_left_func,
136                        free_func, run_func);
137     actions_register_i("DesktopRight", setup_go_right_func,
138                        free_func, run_func);
139     actions_register_i("SendToDesktopRight", setup_send_right_func,
140                        free_func, run_func);
141     actions_register_i("DesktopUp", setup_go_up_func, free_func, run_func);
142     actions_register_i("SendToDesktopUp", setup_send_up_func,
143                        free_func, run_func);
144     actions_register_i("DesktopDown", setup_go_down_func, free_func, run_func);
145     actions_register_i("SendToDesktopDown", setup_send_down_func,
146                        free_func, run_func);
147 }
148
149 static gpointer setup_func(xmlNodePtr node,
150                            ObActionsIPreFunc *pre,
151                            ObActionsIInputFunc *input,
152                            ObActionsICancelFunc *cancel,
153                            ObActionsIPostFunc *post)
154 {
155     xmlNodePtr n;
156     Options *o;
157
158     o = g_slice_new0(Options);
159     /* don't go anywhere if there are no options given */
160     o->type = ABSOLUTE;
161     o->u.abs.desktop = screen_desktop;
162     /* wrap by default - it's handy! */
163     o->u.rel.wrap = TRUE;
164
165     if ((n = obt_xml_find_node(node, "to"))) {
166         gchar *s = obt_xml_node_string(n);
167         if (!g_ascii_strcasecmp(s, "last"))
168             o->type = LAST;
169         else if (!g_ascii_strcasecmp(s, "current"))
170             o->type = CURRENT;
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     if ((n = obt_xml_find_node(node, "desktop"))) {
245         /* 3.4 compatibility */
246         o->u.abs.desktop = obt_xml_node_int(n) - 1;
247         o->type = ABSOLUTE;
248     }
249     o->send = TRUE;
250     o->follow = TRUE;
251
252     if ((n = obt_xml_find_node(node, "follow")))
253         o->follow = obt_xml_node_bool(n);
254
255     if (o->type == RELATIVE && o->follow) {
256         o->interactive = TRUE;
257         *pre = i_pre_func;
258         *input = i_input_func;
259         *post = i_post_func;
260     }
261
262     return o;
263 }
264
265 static void free_func(gpointer o)
266 {
267     g_slice_free(Options, o);
268 }
269
270 static gboolean run_func(ObActionsData *data, gpointer options)
271 {
272     Options *o = options;
273     guint d;
274
275
276
277     switch (o->type) {
278     case LAST:
279         d = screen_last_desktop;
280         break;
281     case CURRENT:
282         d = screen_desktop;
283         break;
284     case ABSOLUTE:
285         d = o->u.abs.desktop;
286         break;
287     case RELATIVE:
288         d = screen_find_desktop(screen_desktop,
289                                 o->u.rel.dir, o->u.rel.wrap, o->u.rel.linear);
290         break;
291     default:
292         g_assert_not_reached();
293     }
294
295     if (d < screen_num_desktops &&
296         (d != screen_desktop ||
297          (data->client && data->client->desktop != screen_desktop))) {
298         gboolean go = TRUE;
299
300         actions_client_move(data, TRUE);
301         if (o->send && data->client && client_normal(data->client)) {
302             client_set_desktop(data->client, d, o->follow, FALSE);
303             go = o->follow;
304         }
305
306         if (go) {
307             screen_set_desktop(d, TRUE);
308             if (data->client)
309                 client_bring_helper_windows(data->client);
310         }
311
312         actions_client_move(data, FALSE);
313     }
314
315     return o->interactive;
316 }
317
318 static gboolean i_input_func(guint initial_state,
319                              XEvent *e,
320                              ObtIC *ic,
321                              gpointer options,
322                              gboolean *used)
323 {
324     guint mods, initial_mods;
325
326     initial_mods = obt_keyboard_only_modmasks(initial_state);
327     mods = obt_keyboard_only_modmasks(e->xkey.state);
328     if (e->type == KeyRelease) {
329         /* remove from the state the mask of the modifier key being
330            released, if it is a modifier key being released that is */
331         mods &= ~obt_keyboard_keyevent_to_modmask(e);
332     }
333
334     if (e->type == KeyPress) {
335         KeySym sym = obt_keyboard_keypress_to_keysym(e);
336
337         /* Escape cancels no matter what */
338         if (sym == XK_Escape)
339             return FALSE;
340
341         /* There were no modifiers and they pressed enter */
342         else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_mods)
343             return FALSE;
344     }
345     /* They released the modifiers */
346     else if (e->type == KeyRelease && initial_mods && !(mods & initial_mods))
347     {
348         return FALSE;
349     }
350
351     return TRUE;
352 }
353
354 static gboolean i_pre_func(guint initial_state, gpointer options)
355 {
356     guint initial_mods = obt_keyboard_only_modmasks(initial_state);
357     if (!initial_mods) {
358         Options *o = options;
359         o->interactive = FALSE;
360         return FALSE;
361     }
362     else {
363         screen_show_desktop_popup(screen_desktop, TRUE);
364         return TRUE;
365     }
366 }
367
368 static void i_post_func(gpointer options)
369 {
370     screen_hide_desktop_popup();
371 }
372
373 /* 3.4-compatilibity */
374 static gpointer setup_follow(xmlNodePtr node)
375 {
376     xmlNodePtr n;
377     Options *o = g_slice_new0(Options);
378     o->send = TRUE;
379     o->follow = TRUE;
380     if ((n = obt_xml_find_node(node, "follow")))
381         o->follow = obt_xml_node_bool(n);
382     return o;
383 }
384
385 static gpointer setup_go_last_func(xmlNodePtr node)
386 {
387     Options *o = g_slice_new0(Options);
388     o->type = LAST;
389     return o;
390 }
391
392 static gpointer setup_send_last_func(xmlNodePtr node)
393 {
394     Options *o = setup_follow(node);
395     o->type = LAST;
396     return o;
397 }
398
399 static gpointer setup_go_abs_func(xmlNodePtr node)
400 {
401     xmlNodePtr n;
402     Options *o = g_slice_new0(Options);
403     o->type = ABSOLUTE;
404     if ((n = obt_xml_find_node(node, "desktop")))
405         o->u.abs.desktop = obt_xml_node_int(n) - 1;
406     else
407         o->u.abs.desktop = screen_desktop;
408     return o;
409 }
410
411 static void setup_rel(Options *o, xmlNodePtr node, gboolean lin,
412                       ObDirection dir,
413                       ObActionsIPreFunc *pre,
414                       ObActionsIInputFunc *input,
415                       ObActionsIPostFunc *post)
416 {
417     xmlNodePtr n;
418
419     o->type = RELATIVE;
420     o->u.rel.linear = lin;
421     o->u.rel.dir = dir;
422     o->u.rel.wrap = TRUE;
423
424     if ((n = obt_xml_find_node(node, "wrap")))
425         o->u.rel.wrap = obt_xml_node_bool(n);
426
427     if (input) {
428         o->interactive = TRUE;
429         *pre = i_pre_func;
430         *input = i_input_func;
431         *post = i_post_func;
432     }
433 }
434
435 static gpointer setup_go_next_func(xmlNodePtr node,
436                                    ObActionsIPreFunc *pre,
437                                    ObActionsIInputFunc *input,
438                                    ObActionsICancelFunc *cancel,
439                                    ObActionsIPostFunc *post)
440 {
441     Options *o = g_slice_new0(Options);
442     setup_rel(o, node, TRUE, OB_DIRECTION_EAST, pre, input, post);
443     return o;
444 }
445
446 static gpointer setup_send_next_func(xmlNodePtr node,
447                                      ObActionsIPreFunc *pre,
448                                      ObActionsIInputFunc *input,
449                                      ObActionsICancelFunc *cancel,
450                                      ObActionsIPostFunc *post)
451 {
452     Options *o = setup_follow(node);
453     setup_rel(o, node, TRUE, OB_DIRECTION_EAST,
454               pre, (o->follow ? input : NULL), post);
455     return o;
456 }
457
458 static gpointer setup_go_prev_func(xmlNodePtr node,
459                                    ObActionsIPreFunc *pre,
460                                    ObActionsIInputFunc *input,
461                                    ObActionsICancelFunc *cancel,
462                                    ObActionsIPostFunc *post)
463 {
464     Options *o = g_slice_new0(Options);
465     setup_rel(o, node, TRUE, OB_DIRECTION_WEST, pre, input, post);
466     return o;
467 }
468
469 static gpointer setup_send_prev_func(xmlNodePtr node,
470                                      ObActionsIPreFunc *pre,
471                                      ObActionsIInputFunc *input,
472                                      ObActionsICancelFunc *cancel,
473                                      ObActionsIPostFunc *post)
474 {
475     Options *o = setup_follow(node);
476     setup_rel(o, node, TRUE, OB_DIRECTION_WEST,
477               pre, (o->follow ? input : NULL), post);
478     return o;
479 }
480
481 static gpointer setup_go_left_func(xmlNodePtr node,
482                                    ObActionsIPreFunc *pre,
483                                    ObActionsIInputFunc *input,
484                                    ObActionsICancelFunc *cancel,
485                                    ObActionsIPostFunc *post)
486 {
487     Options *o = g_slice_new0(Options);
488     setup_rel(o, node, FALSE, OB_DIRECTION_WEST, pre, input, post);
489     return o;
490 }
491
492 static gpointer setup_send_left_func(xmlNodePtr node,
493                                      ObActionsIPreFunc *pre,
494                                      ObActionsIInputFunc *input,
495                                      ObActionsICancelFunc *cancel,
496                                      ObActionsIPostFunc *post)
497 {
498     Options *o = setup_follow(node);
499     setup_rel(o, node, FALSE, OB_DIRECTION_WEST,
500               pre, (o->follow ? input : NULL), post);
501     return o;
502 }
503
504 static gpointer setup_go_right_func(xmlNodePtr node,
505                                     ObActionsIPreFunc *pre,
506                                     ObActionsIInputFunc *input,
507                                     ObActionsICancelFunc *cancel,
508                                     ObActionsIPostFunc *post)
509 {
510     Options *o = g_slice_new0(Options);
511     setup_rel(o, node, FALSE, OB_DIRECTION_EAST, pre, input, post);
512     return o;
513 }
514
515 static gpointer setup_send_right_func(xmlNodePtr node,
516                                       ObActionsIPreFunc *pre,
517                                       ObActionsIInputFunc *input,
518                                       ObActionsICancelFunc *cancel,
519                                       ObActionsIPostFunc *post)
520 {
521     Options *o = setup_follow(node);
522     setup_rel(o, node, FALSE, OB_DIRECTION_EAST,
523               pre, (o->follow ? input : NULL), post);
524     return o;
525 }
526
527 static gpointer setup_go_up_func(xmlNodePtr node,
528                                  ObActionsIPreFunc *pre,
529                                  ObActionsIInputFunc *input,
530                                  ObActionsICancelFunc *cancel,
531                                  ObActionsIPostFunc *post)
532 {
533     Options *o = g_slice_new0(Options);
534     setup_rel(o, node, FALSE, OB_DIRECTION_NORTH, pre, input, post);
535     return o;
536 }
537
538 static gpointer setup_send_up_func(xmlNodePtr node,
539                                    ObActionsIPreFunc *pre,
540                                    ObActionsIInputFunc *input,
541                                    ObActionsICancelFunc *cancel,
542                                    ObActionsIPostFunc *post)
543 {
544     Options *o = setup_follow(node);
545     setup_rel(o, node, FALSE, OB_DIRECTION_NORTH,
546               pre, (o->follow ? input : NULL), post);
547     return o;
548 }
549
550 static gpointer setup_go_down_func(xmlNodePtr node,
551                                    ObActionsIPreFunc *pre,
552                                    ObActionsIInputFunc *input,
553                                    ObActionsICancelFunc *cancel,
554                                    ObActionsIPostFunc *post)
555 {
556     Options *o = g_slice_new0(Options);
557     setup_rel(o, node, FALSE, OB_DIRECTION_SOUTH, pre, input, post);
558     return o;
559 }
560
561 static gpointer setup_send_down_func(xmlNodePtr node,
562                                      ObActionsIPreFunc *pre,
563                                      ObActionsIInputFunc *input,
564                                      ObActionsICancelFunc *cancel,
565                                      ObActionsIPostFunc *post)
566 {
567     Options *o = setup_follow(node);
568     setup_rel(o, node, FALSE, OB_DIRECTION_SOUTH,
569               pre, (o->follow ? input : NULL), post);
570     return o;
571 }