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