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