const
[mikachu/openbox.git] / openbox / action.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    action.c for the Openbox window manager
4    Copyright (c) 2003        Ben Jansens
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "debug.h"
20 #include "client.h"
21 #include "focus.h"
22 #include "moveresize.h"
23 #include "menu.h"
24 #include "prop.h"
25 #include "stacking.h"
26 #include "screen.h"
27 #include "action.h"
28 #include "openbox.h"
29 #include "grab.h"
30 #include "keyboard.h"
31
32 #include <glib.h>
33
34 typedef struct ActionString {
35     char *name;
36     void (*func)(union ActionData *);
37     void (*setup)(ObAction **, ObUserAction uact);
38 } ActionString;
39
40 static ObAction *action_new(void (*func)(union ActionData *data),
41                             ObUserAction uact)
42 {
43     ObAction *a = g_new0(ObAction, 1);
44     a->func = func;
45
46     return a;
47 }
48
49 void action_free(ObAction *a)
50 {
51     if (a == NULL) return;
52
53     /* deal with pointers */
54     if (a->func == action_execute || a->func == action_restart)
55         g_free(a->data.execute.path);
56     else if (a->func == action_showmenu)
57         g_free(a->data.showmenu.name);
58
59     g_free(a);
60 }
61
62 void setup_action_directional_focus_north(ObAction **a, ObUserAction uact)
63
64     (*a)->data.interdiraction.inter.any.interactive = TRUE;
65     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTH;
66 }
67
68 void setup_action_directional_focus_east(ObAction **a, ObUserAction uact)
69 {
70     (*a)->data.interdiraction.inter.any.interactive = TRUE;
71     (*a)->data.interdiraction.direction = OB_DIRECTION_EAST;
72 }
73
74 void setup_action_directional_focus_south(ObAction **a, ObUserAction uact)
75 {
76     (*a)->data.interdiraction.inter.any.interactive = TRUE;
77     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTH;
78 }
79
80 void setup_action_directional_focus_west(ObAction **a, ObUserAction uact)
81 {
82     (*a)->data.interdiraction.inter.any.interactive = TRUE;
83     (*a)->data.interdiraction.direction = OB_DIRECTION_WEST;
84 }
85
86 void setup_action_directional_focus_northeast(ObAction **a, ObUserAction uact)
87 {
88     (*a)->data.interdiraction.inter.any.interactive = TRUE;
89     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHEAST;
90 }
91
92 void setup_action_directional_focus_southeast(ObAction **a, ObUserAction uact)
93 {
94     (*a)->data.interdiraction.inter.any.interactive = TRUE;
95     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHEAST;
96 }
97
98 void setup_action_directional_focus_southwest(ObAction **a, ObUserAction uact)
99 {
100     (*a)->data.interdiraction.inter.any.interactive = TRUE;
101     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHWEST;
102 }
103
104 void setup_action_directional_focus_northwest(ObAction **a, ObUserAction uact)
105 {
106     (*a)->data.interdiraction.inter.any.interactive = TRUE;
107     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHWEST;
108 }
109
110 void setup_action_send_to_desktop(ObAction **a, ObUserAction uact)
111 {
112     (*a)->data.sendto.follow = TRUE;
113 }
114
115 void setup_action_send_to_desktop_prev(ObAction **a, ObUserAction uact)
116 {
117     (*a)->data.sendtodir.inter.any.interactive = TRUE;
118     (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
119     (*a)->data.sendtodir.linear = TRUE;
120     (*a)->data.sendtodir.wrap = TRUE;
121     (*a)->data.sendtodir.follow = TRUE;
122 }
123
124 void setup_action_send_to_desktop_next(ObAction **a, ObUserAction uact)
125 {
126     (*a)->data.sendtodir.inter.any.interactive = TRUE;
127     (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
128     (*a)->data.sendtodir.linear = TRUE;
129     (*a)->data.sendtodir.wrap = TRUE;
130     (*a)->data.sendtodir.follow = TRUE;
131 }
132
133 void setup_action_send_to_desktop_left(ObAction **a, ObUserAction uact)
134 {
135     (*a)->data.sendtodir.inter.any.interactive = TRUE;
136     (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
137     (*a)->data.sendtodir.linear = FALSE;
138     (*a)->data.sendtodir.wrap = TRUE;
139     (*a)->data.sendtodir.follow = TRUE;
140 }
141
142 void setup_action_send_to_desktop_right(ObAction **a, ObUserAction uact)
143 {
144     (*a)->data.sendtodir.inter.any.interactive = TRUE;
145     (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
146     (*a)->data.sendtodir.linear = FALSE;
147     (*a)->data.sendtodir.wrap = TRUE;
148     (*a)->data.sendtodir.follow = TRUE;
149 }
150
151 void setup_action_send_to_desktop_up(ObAction **a, ObUserAction uact)
152 {
153     (*a)->data.sendtodir.inter.any.interactive = TRUE;
154     (*a)->data.sendtodir.dir = OB_DIRECTION_NORTH;
155     (*a)->data.sendtodir.linear = FALSE;
156     (*a)->data.sendtodir.wrap = TRUE;
157     (*a)->data.sendtodir.follow = TRUE;
158 }
159
160 void setup_action_send_to_desktop_down(ObAction **a, ObUserAction uact)
161 {
162     (*a)->data.sendtodir.inter.any.interactive = TRUE;
163     (*a)->data.sendtodir.dir = OB_DIRECTION_SOUTH;
164     (*a)->data.sendtodir.linear = FALSE;
165     (*a)->data.sendtodir.wrap = TRUE;
166     (*a)->data.sendtodir.follow = TRUE;
167 }
168
169 void setup_action_desktop_prev(ObAction **a, ObUserAction uact)
170 {
171     (*a)->data.desktopdir.inter.any.interactive = TRUE;
172     (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
173     (*a)->data.desktopdir.linear = TRUE;
174     (*a)->data.desktopdir.wrap = TRUE;
175 }
176
177 void setup_action_desktop_next(ObAction **a, ObUserAction uact)
178 {
179     (*a)->data.desktopdir.inter.any.interactive = TRUE;
180     (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
181     (*a)->data.desktopdir.linear = TRUE;
182     (*a)->data.desktopdir.wrap = TRUE;
183 }
184
185 void setup_action_desktop_left(ObAction **a, ObUserAction uact)
186 {
187     (*a)->data.desktopdir.inter.any.interactive = TRUE;
188     (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
189     (*a)->data.desktopdir.linear = FALSE;
190     (*a)->data.desktopdir.wrap = TRUE;
191 }
192
193 void setup_action_desktop_right(ObAction **a, ObUserAction uact)
194 {
195     (*a)->data.desktopdir.inter.any.interactive = TRUE;
196     (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
197     (*a)->data.desktopdir.linear = FALSE;
198     (*a)->data.desktopdir.wrap = TRUE;
199 }
200
201 void setup_action_desktop_up(ObAction **a, ObUserAction uact)
202 {
203     (*a)->data.desktopdir.inter.any.interactive = TRUE;
204     (*a)->data.desktopdir.dir = OB_DIRECTION_NORTH;
205     (*a)->data.desktopdir.linear = FALSE;
206     (*a)->data.desktopdir.wrap = TRUE;
207 }
208
209 void setup_action_desktop_down(ObAction **a, ObUserAction uact)
210 {
211     (*a)->data.desktopdir.inter.any.interactive = TRUE;
212     (*a)->data.desktopdir.dir = OB_DIRECTION_SOUTH;
213     (*a)->data.desktopdir.linear = FALSE;
214     (*a)->data.desktopdir.wrap = TRUE;
215 }
216
217 void setup_action_cycle_windows_next(ObAction **a, ObUserAction uact)
218 {
219     (*a)->data.cycle.inter.any.interactive = TRUE;
220     (*a)->data.cycle.linear = FALSE;
221     (*a)->data.cycle.forward = TRUE;
222 }
223
224 void setup_action_cycle_windows_previous(ObAction **a, ObUserAction uact)
225 {
226     (*a)->data.cycle.inter.any.interactive = TRUE;
227     (*a)->data.cycle.linear = FALSE;
228     (*a)->data.cycle.forward = FALSE;
229 }
230
231 void setup_action_movetoedge_north(ObAction **a, ObUserAction uact)
232 {
233     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
234 }
235
236 void setup_action_movetoedge_south(ObAction **a, ObUserAction uact)
237 {
238     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
239 }
240
241 void setup_action_movetoedge_east(ObAction **a, ObUserAction uact)
242 {
243     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
244 }
245
246 void setup_action_movetoedge_west(ObAction **a, ObUserAction uact)
247 {
248     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
249 }
250
251 void setup_action_growtoedge_north(ObAction **a, ObUserAction uact)
252 {
253     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
254 }
255
256 void setup_action_growtoedge_south(ObAction **a, ObUserAction uact)
257 {
258     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
259 }
260
261 void setup_action_growtoedge_east(ObAction **a, ObUserAction uact)
262 {
263     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
264 }
265
266 void setup_action_growtoedge_west(ObAction **a, ObUserAction uact)
267 {
268     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
269 }
270
271 void setup_action_top_layer(ObAction **a, ObUserAction uact)
272 {
273     (*a)->data.layer.layer = 1;
274 }
275
276 void setup_action_normal_layer(ObAction **a, ObUserAction uact)
277 {
278     (*a)->data.layer.layer = 0;
279 }
280
281 void setup_action_bottom_layer(ObAction **a, ObUserAction uact)
282 {
283     (*a)->data.layer.layer = -1;
284 }
285
286 void setup_action_move(ObAction **a, ObUserAction uact)
287 {
288     (*a)->data.moveresize.move = TRUE;
289     (*a)->data.moveresize.keyboard =
290         (uact == OB_USER_ACTION_KEYBOARD_KEY ||
291          uact == OB_USER_ACTION_MENU_SELECTION);
292 }
293
294 void setup_action_resize(ObAction **a, ObUserAction uact)
295 {
296     (*a)->data.moveresize.move = FALSE;
297     (*a)->data.moveresize.keyboard =
298         (uact == OB_USER_ACTION_KEYBOARD_KEY ||
299          uact == OB_USER_ACTION_MENU_SELECTION);
300 }
301
302 void setup_action_showmenu(ObAction **a, ObUserAction uact)
303 {
304     /* you cannot call ShowMenu from inside a menu, cuz the menu code makes
305        assumptions that there is only one menu (and submenus) open at
306        a time! */
307     if (uact == OB_USER_ACTION_MENU_SELECTION) {
308         action_free(*a);
309         a = NULL;
310     }
311 }
312
313 ActionString actionstrings[] =
314 {
315     {
316         "execute", 
317         action_execute, 
318         NULL
319     },
320     {
321         "directionalfocusnorth", 
322         action_directional_focus, 
323         setup_action_directional_focus_north
324     },
325     {
326         "directionalfocuseast", 
327         action_directional_focus, 
328         setup_action_directional_focus_east
329     },
330     {
331         "directionalfocussouth", 
332         action_directional_focus, 
333         setup_action_directional_focus_south
334     },
335     {
336         "directionalfocuswest",
337         action_directional_focus,
338         setup_action_directional_focus_west
339     },
340     {
341         "directionalfocusnortheast",
342         action_directional_focus,
343         setup_action_directional_focus_northeast
344     },
345     {
346         "directionalfocussoutheast",
347         action_directional_focus,
348         setup_action_directional_focus_southeast
349     },
350     {
351         "directionalfocussouthwest",
352         action_directional_focus,
353         setup_action_directional_focus_southwest
354     },
355     {
356         "directionalfocusnorthwest",
357         action_directional_focus,
358         setup_action_directional_focus_northwest
359     },
360     {
361         "activate",
362         action_activate,
363         NULL
364     },
365     {
366         "focus",
367         action_focus,
368         NULL
369     },
370     {
371         "unfocus",
372         action_unfocus,
373         NULL
374     },
375     {
376         "iconify",
377         action_iconify,
378         NULL
379     },
380     {
381         "raiselower",
382         action_raiselower,
383         NULL
384     },
385     {
386         "raise",
387         action_raise,
388         NULL
389     },
390     {
391         "lower",
392         action_lower,
393         NULL
394     },
395     {
396         "close",
397         action_close,
398         NULL
399     },
400     {
401         "kill",
402         action_kill,
403         NULL
404     },
405     {
406         "shadelower",
407         action_shadelower,
408         NULL
409     },
410     {
411         "unshaderaise",
412         action_unshaderaise,
413         NULL
414     },
415     {
416         "shade",
417         action_shade,
418         NULL
419     },
420     {
421         "unshade",
422         action_unshade,
423         NULL
424     },
425     {
426         "toggleshade",
427         action_toggle_shade,
428         NULL
429     },
430     {
431         "toggleomnipresent",
432         action_toggle_omnipresent,
433         NULL
434     },
435     {
436         "moverelativehorz",
437         action_move_relative_horz,
438         NULL
439     },
440     {
441         "moverelativevert",
442         action_move_relative_vert,
443         NULL
444     },
445     {
446         "resizerelativehorz",
447         action_resize_relative_horz,
448         NULL
449     },
450     {
451         "resizerelativevert",
452         action_resize_relative_vert,
453         NULL
454     },
455     {
456         "maximizefull",
457         action_maximize_full,
458         NULL
459     },
460     {
461         "unmaximizefull",
462         action_unmaximize_full,
463         NULL
464     },
465     {
466         "togglemaximizefull",
467         action_toggle_maximize_full,
468         NULL
469     },
470     {
471         "maximizehorz",
472         action_maximize_horz,
473         NULL
474     },
475     {
476         "unmaximizehorz",
477         action_unmaximize_horz,
478         NULL
479     },
480     {
481         "togglemaximizehorz",
482         action_toggle_maximize_horz,
483         NULL
484     },
485     {
486         "maximizevert",
487         action_maximize_vert,
488         NULL
489     },
490     {
491         "unmaximizevert",
492         action_unmaximize_vert,
493         NULL
494     },
495     {
496         "togglemaximizevert",
497         action_toggle_maximize_vert,
498         NULL
499     },
500     {
501         "sendtodesktop",
502         action_send_to_desktop,
503         setup_action_send_to_desktop
504     },
505     {
506         "sendtodesktopnext",
507         action_send_to_desktop_dir,
508         setup_action_send_to_desktop_next
509     },
510     {
511         "sendtodesktopprevious",
512         action_send_to_desktop_dir,
513         setup_action_send_to_desktop_prev
514     },
515     {
516         "sendtodesktopright",
517         action_send_to_desktop_dir,
518         setup_action_send_to_desktop_right
519     },
520     {
521         "sendtodesktopleft",
522         action_send_to_desktop_dir,
523         setup_action_send_to_desktop_left
524     },
525     {
526         "sendtodesktopup",
527         action_send_to_desktop_dir,
528         setup_action_send_to_desktop_up
529     },
530     {
531         "sendtodesktopdown",
532         action_send_to_desktop_dir,
533         setup_action_send_to_desktop_down
534     },
535     {
536         "desktop",
537         action_desktop,
538         NULL
539     },
540     {
541         "desktopnext",
542         action_desktop_dir,
543         setup_action_desktop_next
544     },
545     {
546         "desktopprevious",
547         action_desktop_dir,
548         setup_action_desktop_prev
549     },
550     {
551         "desktopright",
552         action_desktop_dir,
553         setup_action_desktop_right
554     },
555     {
556         "desktopleft",
557         action_desktop_dir,
558         setup_action_desktop_left
559     },
560     {
561         "desktopup",
562         action_desktop_dir,
563         setup_action_desktop_up
564     },
565     {
566         "desktopdown",
567         action_desktop_dir,
568         setup_action_desktop_down
569     },
570     {
571         "toggledecorations",
572         action_toggle_decorations,
573         NULL
574     },
575     {
576         "move",
577         action_moveresize,
578         setup_action_move
579     },
580     {
581         "resize",
582         action_moveresize,
583         setup_action_resize
584     },
585     {
586         "toggleshowdesktop",
587         action_toggle_show_desktop,
588         NULL
589     },
590     {
591         "showdesktop",
592         action_show_desktop,
593         NULL
594     },
595     {
596         "unshowdesktop",
597         action_unshow_desktop,
598         NULL
599     },
600     {
601         "desktoplast",
602         action_desktop_last,
603         NULL
604     },
605     {
606         "reconfigure",
607         action_reconfigure,
608         NULL
609     },
610     {
611         "restart",
612         action_restart,
613         NULL
614     },
615     {
616         "exit",
617         action_exit,
618         NULL
619     },
620     {
621         "showmenu",
622         action_showmenu,
623         setup_action_showmenu
624     },
625     {
626         "sendtotoplayer",
627         action_send_to_layer,
628         setup_action_top_layer
629     },
630     {
631         "togglealwaysontop",
632         action_toggle_layer,
633         setup_action_top_layer
634     },
635     {
636         "sendtonormallayer",
637         action_send_to_layer,
638         setup_action_normal_layer
639     },
640     {
641         "sendtobottomlayer",
642         action_send_to_layer,
643         setup_action_bottom_layer
644     },
645     {
646         "togglealwaysonbottom",
647         action_toggle_layer,
648         setup_action_bottom_layer
649     },
650     {
651         "nextwindow",
652         action_cycle_windows,
653         setup_action_cycle_windows_next
654     },
655     {
656         "previouswindow",
657         action_cycle_windows,
658         setup_action_cycle_windows_previous
659     },
660     {
661         "movetoedgenorth",
662         action_movetoedge,
663         setup_action_movetoedge_north
664     },
665     {
666         "movetoedgesouth",
667         action_movetoedge,
668         setup_action_movetoedge_south
669     },
670     {
671         "movetoedgewest",
672         action_movetoedge,
673         setup_action_movetoedge_west
674     },
675     {
676         "movetoedgeeast",
677         action_movetoedge,
678         setup_action_movetoedge_east
679     },
680     {
681         "growtoedgenorth",
682         action_growtoedge,
683         setup_action_growtoedge_north
684     },
685     {
686         "growtoedgesouth",
687         action_growtoedge,
688         setup_action_growtoedge_south
689     },
690     {
691         "growtoedgewest",
692         action_growtoedge,
693         setup_action_growtoedge_west
694     },
695     {
696         "growtoedgeeast",
697         action_growtoedge,
698         setup_action_growtoedge_east
699     },
700     {
701         NULL,
702         NULL,
703         NULL
704     }
705 };
706
707 ObAction *action_from_string(const gchar *name, ObUserAction uact)
708 {
709     ObAction *a = NULL;
710     gboolean exist = FALSE;
711     int i;
712
713     for (i = 0; actionstrings[i].name; i++)
714         if (!g_ascii_strcasecmp(name, actionstrings[i].name)) {
715             exist = TRUE;
716             a = action_new(actionstrings[i].func, uact);
717             if (actionstrings[i].setup)
718                 actionstrings[i].setup(&a, uact);
719             /* only key bindings can be interactive. thus saith the xor. */
720             if (uact != OB_USER_ACTION_KEYBOARD_KEY)
721                 a->data.any.interactive = FALSE;
722             break;
723         }
724     if (!exist)
725         g_warning("Invalid action '%s' requested. No such action exists.",
726                   name);
727     if (!a)
728         g_warning("Invalid use of action '%s'. Action will be ignored.", name);
729     return a;
730 }
731
732 ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
733                        ObUserAction uact)
734 {
735     char *actname;
736     ObAction *act = NULL;
737     xmlNodePtr n;
738
739     if (parse_attr_string("name", node, &actname)) {
740         if ((act = action_from_string(actname, uact))) {
741             if (act->func == action_execute || act->func == action_restart) {
742                 if ((n = parse_find_node("execute", node->xmlChildrenNode))) {
743                     gchar *s = parse_string(doc, n);
744                     act->data.execute.path = parse_expand_tilde(s);
745                     g_free(s);
746                 }
747             } else if (act->func == action_showmenu) {
748                 if ((n = parse_find_node("menu", node->xmlChildrenNode)))
749                     act->data.showmenu.name = parse_string(doc, n);
750             } else if (act->func == action_move_relative_horz ||
751                        act->func == action_move_relative_vert ||
752                        act->func == action_resize_relative_horz ||
753                        act->func == action_resize_relative_vert) {
754                 if ((n = parse_find_node("delta", node->xmlChildrenNode)))
755                     act->data.relative.delta = parse_int(doc, n);
756             } else if (act->func == action_desktop) {
757                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
758                     act->data.desktop.desk = parse_int(doc, n);
759                 if (act->data.desktop.desk > 0) act->data.desktop.desk--;
760            } else if (act->func == action_send_to_desktop) {
761                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
762                     act->data.sendto.desk = parse_int(doc, n);
763                 if (act->data.sendto.desk > 0) act->data.sendto.desk--;
764                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
765                     act->data.sendto.follow = parse_bool(doc, n);
766             } else if (act->func == action_desktop_dir) {
767                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
768                     act->data.desktopdir.wrap = parse_bool(doc, n); 
769             } else if (act->func == action_send_to_desktop_dir) {
770                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
771                     act->data.sendtodir.wrap = parse_bool(doc, n);
772                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
773                     act->data.sendtodir.follow = parse_bool(doc, n);
774             } else if (act->func == action_activate) {
775                 if ((n = parse_find_node("here", node->xmlChildrenNode)))
776                     act->data.activate.here = parse_bool(doc, n);
777             } else if (act->func == action_cycle_windows) {
778                 if ((n = parse_find_node("linear", node->xmlChildrenNode)))
779                     act->data.cycle.linear = parse_bool(doc, n);
780             }
781         }
782         g_free(actname);
783     }
784     return act;
785 }
786
787 void action_run_full(ObAction *a, struct _ObClient *c,
788                      guint state, guint button, gint x, gint y,
789                      gboolean cancel, gboolean done)
790 {
791     if (x < 0 && y < 0)
792         screen_pointer_pos(&x, &y);
793
794     a->data.any.c = c;
795     a->data.any.x = x;
796     a->data.any.y = y;
797
798     a->data.any.button = button;
799
800     if (a->data.any.interactive) {
801         a->data.inter.cancel = cancel;
802         a->data.inter.final = done;
803         if (!(cancel || done))
804             keyboard_interactive_grab(state, c, a);
805     }
806
807     a->func(&a->data);
808 }
809
810 void action_execute(union ActionData *data)
811 {
812     GError *e = NULL;
813     char *cmd;
814     if (data->execute.path) {
815         cmd = g_filename_from_utf8(data->execute.path, -1, NULL, NULL, NULL);
816         if (cmd) {
817             if (!g_spawn_command_line_async(cmd, &e)) {
818                 g_warning("failed to execute '%s': %s",
819                           cmd, e->message);
820             }
821             g_free(cmd);
822         } else {
823             g_warning("failed to convert '%s' from utf8", data->execute.path);
824         }
825     }
826 }
827
828 void action_activate(union ActionData *data)
829 {
830     if (data->activate.any.c)
831         client_activate(data->activate.any.c, data->activate.here);
832 }
833
834 void action_focus(union ActionData *data)
835 {
836     if (data->client.any.c)
837         client_focus(data->client.any.c);
838 }
839
840 void action_unfocus (union ActionData *data)
841 {
842     if (data->client.any.c)
843         client_unfocus(data->client.any.c);
844 }
845
846 void action_iconify(union ActionData *data)
847 {
848     if (data->client.any.c)
849         client_iconify(data->client.any.c, TRUE, TRUE);
850 }
851
852 void action_raiselower(union ActionData *data)
853 {
854     ObClient *c = data->client.any.c;
855     GList *it;
856     gboolean raise = FALSE;
857
858     if (!c) return;
859
860     for (it = stacking_list; it; it = g_list_next(it)) {
861         ObClient *cit = it->data;
862
863         if (cit == c) break;
864         if (client_normal(cit) == client_normal(c) &&
865             cit->layer == c->layer &&
866             cit->frame->visible)
867         {
868             if (RECT_INTERSECTS_RECT(cit->frame->area, c->frame->area)) {
869                 raise = TRUE;
870                 break;
871             }
872         }
873     }
874
875     if (raise)
876         stacking_raise(CLIENT_AS_WINDOW(c));
877     else
878         stacking_lower(CLIENT_AS_WINDOW(c));
879 }
880
881 void action_raise(union ActionData *data)
882 {
883     if (data->client.any.c)
884         stacking_raise(CLIENT_AS_WINDOW(data->client.any.c));
885 }
886
887 void action_unshaderaise(union ActionData *data)
888 {
889     if (data->client.any.c) {
890         if (data->client.any.c->shaded) {
891             grab_pointer(TRUE, OB_CURSOR_NONE);
892             client_shade(data->client.any.c, FALSE);
893             grab_pointer(FALSE, OB_CURSOR_NONE);
894         } else
895             stacking_raise(CLIENT_AS_WINDOW(data->client.any.c));
896     }
897 }
898
899 void action_shadelower(union ActionData *data)
900 {
901     if (data->client.any.c) {
902         if (data->client.any.c->shaded)
903             stacking_lower(CLIENT_AS_WINDOW(data->client.any.c));
904         else {
905             grab_pointer(TRUE, OB_CURSOR_NONE);
906             client_shade(data->client.any.c, TRUE);
907             grab_pointer(FALSE, OB_CURSOR_NONE);
908         }
909     }
910 }
911
912 void action_lower(union ActionData *data)
913 {
914     if (data->client.any.c)
915         stacking_lower(CLIENT_AS_WINDOW(data->client.any.c));
916 }
917
918 void action_close(union ActionData *data)
919 {
920     if (data->client.any.c)
921         client_close(data->client.any.c);
922 }
923
924 void action_kill(union ActionData *data)
925 {
926     if (data->client.any.c)
927         client_kill(data->client.any.c);
928 }
929
930 void action_shade(union ActionData *data)
931 {
932     if (data->client.any.c) { 
933         grab_pointer(TRUE, OB_CURSOR_NONE);
934         client_shade(data->client.any.c, TRUE);
935         grab_pointer(FALSE, OB_CURSOR_NONE);
936     }
937 }
938
939 void action_unshade(union ActionData *data)
940 {
941     if (data->client.any.c) {
942         grab_pointer(TRUE, OB_CURSOR_NONE);
943         client_shade(data->client.any.c, FALSE);
944         grab_pointer(FALSE, OB_CURSOR_NONE);
945     }
946 }
947
948 void action_toggle_shade(union ActionData *data)
949 {
950     if (data->client.any.c) {
951         grab_pointer(TRUE, OB_CURSOR_NONE);
952         client_shade(data->client.any.c, !data->client.any.c->shaded);
953         grab_pointer(FALSE, OB_CURSOR_NONE);
954     }
955 }
956
957 void action_toggle_omnipresent(union ActionData *data)
958
959     if (data->client.any.c)
960         client_set_desktop(data->client.any.c,
961                            data->client.any.c->desktop == DESKTOP_ALL ?
962                            screen_desktop : DESKTOP_ALL, FALSE);
963 }
964
965 void action_move_relative_horz(union ActionData *data)
966 {
967     ObClient *c = data->relative.any.c;
968     if (c) {
969         grab_pointer(TRUE, OB_CURSOR_NONE);
970         client_move(c, c->area.x + data->relative.delta, c->area.y);
971         grab_pointer(FALSE, OB_CURSOR_NONE);
972     }
973 }
974
975 void action_move_relative_vert(union ActionData *data)
976 {
977     ObClient *c = data->relative.any.c;
978     if (c) {
979         grab_pointer(TRUE, OB_CURSOR_NONE);
980         client_move(c, c->area.x, c->area.y + data->relative.delta);
981         grab_pointer(FALSE, OB_CURSOR_NONE);
982     }
983 }
984
985 void action_resize_relative_horz(union ActionData *data)
986 {
987     ObClient *c = data->relative.any.c;
988     if (c) {
989         grab_pointer(TRUE, OB_CURSOR_NONE);
990         client_resize(c,
991                       c->area.width + data->relative.delta * c->size_inc.width,
992                       c->area.height);
993         grab_pointer(FALSE, OB_CURSOR_NONE);
994     }
995 }
996
997 void action_resize_relative_vert(union ActionData *data)
998 {
999     ObClient *c = data->relative.any.c;
1000     if (c && !c->shaded) {
1001         grab_pointer(TRUE, OB_CURSOR_NONE);
1002         client_resize(c, c->area.width, c->area.height +
1003                       data->relative.delta * c->size_inc.height);
1004         grab_pointer(FALSE, OB_CURSOR_NONE);
1005     }
1006 }
1007
1008 void action_maximize_full(union ActionData *data)
1009 {
1010     if (data->client.any.c)
1011         client_maximize(data->client.any.c, TRUE, 0, TRUE);
1012 }
1013
1014 void action_unmaximize_full(union ActionData *data)
1015 {
1016     if (data->client.any.c)
1017         client_maximize(data->client.any.c, FALSE, 0, TRUE);
1018 }
1019
1020 void action_toggle_maximize_full(union ActionData *data)
1021 {
1022     if (data->client.any.c)
1023         client_maximize(data->client.any.c,
1024                         !(data->client.any.c->max_horz ||
1025                           data->client.any.c->max_vert),
1026                         0, TRUE);
1027 }
1028
1029 void action_maximize_horz(union ActionData *data)
1030 {
1031     if (data->client.any.c)
1032         client_maximize(data->client.any.c, TRUE, 1, TRUE);
1033 }
1034
1035 void action_unmaximize_horz(union ActionData *data)
1036 {
1037     if (data->client.any.c)
1038         client_maximize(data->client.any.c, FALSE, 1, TRUE);
1039 }
1040
1041 void action_toggle_maximize_horz(union ActionData *data)
1042 {
1043     if (data->client.any.c)
1044         client_maximize(data->client.any.c,
1045                         !data->client.any.c->max_horz, 1, TRUE);
1046 }
1047
1048 void action_maximize_vert(union ActionData *data)
1049 {
1050     if (data->client.any.c)
1051         client_maximize(data->client.any.c, TRUE, 2, TRUE);
1052 }
1053
1054 void action_unmaximize_vert(union ActionData *data)
1055 {
1056     if (data->client.any.c)
1057         client_maximize(data->client.any.c, FALSE, 2, TRUE);
1058 }
1059
1060 void action_toggle_maximize_vert(union ActionData *data)
1061 {
1062     if (data->client.any.c)
1063         client_maximize(data->client.any.c, !data->client.any.c->max_vert, 2, TRUE);
1064 }
1065
1066 void action_send_to_desktop(union ActionData *data)
1067 {
1068     ObClient *c = data->sendto.any.c;
1069
1070     if (!c || !client_normal(c)) return;
1071
1072     if (data->sendto.desk < screen_num_desktops ||
1073         data->sendto.desk == DESKTOP_ALL) {
1074         client_set_desktop(c, data->sendto.desk, data->sendto.follow);
1075         if (data->sendto.follow)
1076             screen_set_desktop(data->sendto.desk);
1077     }
1078 }
1079
1080 void action_desktop(union ActionData *data)
1081 {
1082     if (data->desktop.desk < screen_num_desktops ||
1083         data->desktop.desk == DESKTOP_ALL)
1084         screen_set_desktop(data->desktop.desk);
1085 }
1086
1087 void action_desktop_dir(union ActionData *data)
1088 {
1089     guint d;
1090
1091     d = screen_cycle_desktop(data->desktopdir.dir,
1092                              data->desktopdir.wrap,
1093                              data->sendtodir.linear,
1094                              data->desktopdir.inter.any.interactive,
1095                              data->desktopdir.inter.final,
1096                              data->desktopdir.inter.cancel);
1097     screen_set_desktop(d);
1098 }
1099
1100 void action_send_to_desktop_dir(union ActionData *data)
1101 {
1102     ObClient *c = data->sendtodir.inter.any.c;
1103     guint d;
1104
1105     if (!c || !client_normal(c)) return;
1106
1107     d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
1108                              data->sendtodir.linear,
1109                              data->sendtodir.inter.any.interactive,
1110                              data->sendtodir.inter.final,
1111                              data->sendtodir.inter.cancel);
1112     client_set_desktop(c, d, data->sendtodir.follow);
1113     if (data->sendtodir.follow)
1114         screen_set_desktop(d);
1115 }
1116
1117 void action_desktop_last(union ActionData *data)
1118 {
1119     screen_set_desktop(screen_last_desktop);
1120 }
1121
1122 void action_toggle_decorations(union ActionData *data)
1123 {
1124     ObClient *c = data->client.any.c;
1125
1126     if (!c) return;
1127
1128     c->decorate = !c->decorate;
1129     client_setup_decor_and_functions(c);
1130 }
1131
1132 static guint32 pick_corner(int x, int y, int cx, int cy, int cw, int ch)
1133 {
1134     if (x - cx > cw / 2) {
1135         if (y - cy > ch / 2)
1136             return prop_atoms.net_wm_moveresize_size_bottomright;
1137         else
1138             return prop_atoms.net_wm_moveresize_size_topright;
1139     } else {
1140         if (y - cy > ch / 2)
1141             return prop_atoms.net_wm_moveresize_size_bottomleft;
1142         else
1143             return prop_atoms.net_wm_moveresize_size_topleft;
1144     }
1145 }
1146
1147 void action_moveresize(union ActionData *data)
1148 {
1149     ObClient *c = data->moveresize.any.c;
1150     guint32 corner;
1151
1152     if (!c || !client_normal(c)) return;
1153
1154     if (data->moveresize.keyboard) {
1155         corner = (data->moveresize.move ?
1156                   prop_atoms.net_wm_moveresize_move_keyboard :
1157                   prop_atoms.net_wm_moveresize_size_keyboard);
1158     } else {
1159         corner = (data->moveresize.move ?
1160                   prop_atoms.net_wm_moveresize_move :
1161                   pick_corner(data->any.x, data->any.y,
1162                               c->frame->area.x, c->frame->area.y,
1163                               /* use the client size because the frame
1164                                  can be differently sized (shaded
1165                                  windows) and we want this based on the
1166                                  clients size */
1167                               c->area.width + c->frame->size.left +
1168                               c->frame->size.right,
1169                               c->area.height + c->frame->size.top +
1170                               c->frame->size.bottom));
1171     }
1172
1173     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1174 }
1175
1176 void action_reconfigure(union ActionData *data)
1177 {
1178     ob_reconfigure();
1179 }
1180
1181 void action_restart(union ActionData *data)
1182 {
1183     ob_restart_other(data->execute.path);
1184 }
1185
1186 void action_exit(union ActionData *data)
1187 {
1188     ob_exit(0);
1189 }
1190
1191 void action_showmenu(union ActionData *data)
1192 {
1193     if (data->showmenu.name) {
1194         menu_show(data->showmenu.name, data->any.x, data->any.y,
1195                   data->showmenu.any.c);
1196     }
1197 }
1198
1199 void action_cycle_windows(union ActionData *data)
1200 {
1201     focus_cycle(data->cycle.forward, data->cycle.linear,
1202                 data->cycle.inter.any.interactive,
1203                 data->cycle.inter.final, data->cycle.inter.cancel);
1204 }
1205
1206 void action_directional_focus(union ActionData *data)
1207 {
1208     focus_directional_cycle(data->interdiraction.direction,
1209                             data->interdiraction.inter.any.interactive,
1210                             data->interdiraction.inter.final,
1211                             data->interdiraction.inter.cancel);
1212 }
1213
1214 void action_movetoedge(union ActionData *data)
1215 {
1216     int x, y;
1217     ObClient *c = data->diraction.any.c;
1218
1219     if (!c)
1220         return;
1221     x = c->frame->area.x;
1222     y = c->frame->area.y;
1223     
1224     switch(data->diraction.direction) {
1225     case OB_DIRECTION_NORTH:
1226         y = client_directional_edge_search(c, OB_DIRECTION_NORTH);
1227         break;
1228     case OB_DIRECTION_WEST:
1229         x = client_directional_edge_search(c, OB_DIRECTION_WEST);
1230         break;
1231     case OB_DIRECTION_SOUTH:
1232         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH) -
1233             c->frame->area.height;
1234         break;
1235     case OB_DIRECTION_EAST:
1236         x = client_directional_edge_search(c, OB_DIRECTION_EAST) -
1237             c->frame->area.width;
1238         break;
1239     default:
1240         g_assert_not_reached();
1241     }
1242     frame_frame_gravity(c->frame, &x, &y);
1243     grab_pointer(TRUE, OB_CURSOR_NONE);
1244     client_move(c, x, y);
1245     grab_pointer(FALSE, OB_CURSOR_NONE);
1246
1247 }
1248
1249 void action_growtoedge(union ActionData *data)
1250 {
1251     int x, y, width, height, dest;
1252     ObClient *c = data->diraction.any.c;
1253     Rect *a;
1254
1255     if (!c)
1256         return;
1257     
1258     a = screen_area(c->desktop);
1259     x = c->frame->area.x;
1260     y = c->frame->area.y;
1261     width = c->frame->area.width;
1262     height = c->frame->area.height;
1263
1264     switch(data->diraction.direction) {
1265     case OB_DIRECTION_NORTH:
1266         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH);
1267         if (a->y == y)
1268             height = c->frame->area.height / 2;
1269         else {
1270             height = c->frame->area.y + c->frame->area.height - dest;
1271             y = dest;
1272         }
1273         break;
1274     case OB_DIRECTION_WEST:
1275         dest = client_directional_edge_search(c, OB_DIRECTION_WEST);
1276         if (a->x == x)
1277             width = c->frame->area.width / 2;
1278         else {
1279             width = c->frame->area.x + c->frame->area.width - dest;
1280             x = dest;
1281         }
1282         break;
1283     case OB_DIRECTION_SOUTH:
1284         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH);
1285         if (a->y + a->height == y + c->frame->area.height) {
1286             height = c->frame->area.height / 2;
1287             y = a->y + a->height - height;
1288         } else
1289             height = dest - c->frame->area.y;
1290         y += (height - c->frame->area.height) % c->size_inc.height;
1291         height -= (height - c->frame->area.height) % c->size_inc.height;
1292         break;
1293     case OB_DIRECTION_EAST:
1294         dest = client_directional_edge_search(c, OB_DIRECTION_EAST);
1295         if (a->x + a->width == x + c->frame->area.width) {
1296             width = c->frame->area.width / 2;
1297             x = a->x + a->width - width;
1298         } else
1299             width = dest - c->frame->area.x;
1300         x += (width - c->frame->area.width) % c->size_inc.width;
1301         width -= (width - c->frame->area.width) % c->size_inc.width;
1302         break;
1303     default:
1304         g_assert_not_reached();
1305     }
1306     frame_frame_gravity(c->frame, &x, &y);
1307     width -= c->frame->size.left + c->frame->size.right;
1308     height -= c->frame->size.top + c->frame->size.bottom;
1309     grab_pointer(TRUE, OB_CURSOR_NONE);
1310     client_move_resize(c, x, y, width, height);
1311     grab_pointer(FALSE, OB_CURSOR_NONE);
1312 }
1313
1314 void action_send_to_layer(union ActionData *data)
1315 {
1316     if (data->layer.any.c)
1317         client_set_layer(data->layer.any.c, data->layer.layer);
1318 }
1319
1320 void action_toggle_layer(union ActionData *data)
1321 {
1322     ObClient *c = data->layer.any.c;
1323
1324     if (c) {
1325         if (data->layer.layer < 0)
1326             client_set_layer(c, c->below ? 0 : -1);
1327         else if (data->layer.layer > 0)
1328             client_set_layer(c, c->above ? 0 : 1);
1329     }
1330 }
1331
1332 void action_toggle_show_desktop(union ActionData *data)
1333 {
1334     screen_show_desktop(!screen_showing_desktop);
1335 }
1336
1337 void action_show_desktop(union ActionData *data)
1338 {
1339     screen_show_desktop(TRUE);
1340 }
1341
1342 void action_unshow_desktop(union ActionData *data)
1343 {
1344     screen_show_desktop(FALSE);
1345 }