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