always ungrab keyboard before doing a noninteractive action (which will ungrab before...
[dana/openbox-history.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) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "debug.h"
21 #include "client.h"
22 #include "focus.h"
23 #include "moveresize.h"
24 #include "menu.h"
25 #include "prop.h"
26 #include "stacking.h"
27 #include "screen.h"
28 #include "action.h"
29 #include "openbox.h"
30 #include "grab.h"
31 #include "keyboard.h"
32 #include "event.h"
33 #include "dock.h"
34 #include "config.h"
35 #include "mainloop.h"
36 #include "startupnotify.h"
37 #include "gettext.h"
38
39 #include <glib.h>
40
41 inline void client_action_start(union ActionData *data)
42 {
43     if (config_focus_follow)
44         if (data->any.context != OB_FRAME_CONTEXT_CLIENT && !data->any.button)
45             grab_pointer(FALSE, FALSE, OB_CURSOR_NONE);
46 }
47
48 inline void client_action_end(union ActionData *data)
49 {
50     if (config_focus_follow)
51         if (data->any.context != OB_FRAME_CONTEXT_CLIENT) {
52             if (!data->any.button) {
53                 ungrab_pointer();
54             } else {
55                 ObClient *c;
56
57                 /* usually this is sorta redundant, but with a press action
58                    that moves windows our from under the cursor, the enter
59                    event will come as a GrabNotify which is ignored, so this
60                    makes a fake enter event
61                 */
62                 if ((c = client_under_pointer()))
63                     event_enter_client(c);
64             }
65         }
66 }
67
68 typedef struct
69 {
70     const gchar *name;
71     void (*func)(union ActionData *);
72     void (*setup)(ObAction **, ObUserAction uact);
73 } ActionString;
74
75 static ObAction *action_new(void (*func)(union ActionData *data))
76 {
77     ObAction *a = g_new0(ObAction, 1);
78     a->ref = 1;
79     a->func = func;
80
81     return a;
82 }
83
84 void action_ref(ObAction *a)
85 {
86     ++a->ref;
87 }
88
89 void action_unref(ObAction *a)
90 {
91     if (a == NULL) return;
92
93     if (--a->ref > 0) return;
94
95     /* deal with pointers */
96     if (a->func == action_execute || a->func == action_restart)
97         g_free(a->data.execute.path);
98     else if (a->func == action_debug)
99         g_free(a->data.debug.string);
100     else if (a->func == action_showmenu)
101         g_free(a->data.showmenu.name);
102
103     g_free(a);
104 }
105
106 ObAction* action_copy(const ObAction *src)
107 {
108     ObAction *a = action_new(src->func);
109
110     a->data = src->data;
111
112     /* deal with pointers */
113     if (a->func == action_execute || a->func == action_restart)
114         a->data.execute.path = g_strdup(a->data.execute.path);
115     else if (a->func == action_debug)
116         a->data.debug.string = g_strdup(a->data.debug.string);
117     else if (a->func == action_showmenu)
118         a->data.showmenu.name = g_strdup(a->data.showmenu.name);
119
120     return a;
121 }
122
123 void setup_action_directional_focus_north(ObAction **a, ObUserAction uact)
124 {
125     (*a)->data.interdiraction.inter.any.interactive = TRUE;
126     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTH;
127     (*a)->data.interdiraction.dialog = TRUE;
128     (*a)->data.interdiraction.dock_windows = FALSE;
129     (*a)->data.interdiraction.desktop_windows = FALSE;
130 }
131
132 void setup_action_directional_focus_east(ObAction **a, ObUserAction uact)
133 {
134     (*a)->data.interdiraction.inter.any.interactive = TRUE;
135     (*a)->data.interdiraction.direction = OB_DIRECTION_EAST;
136     (*a)->data.interdiraction.dialog = TRUE;
137     (*a)->data.interdiraction.dock_windows = FALSE;
138     (*a)->data.interdiraction.desktop_windows = FALSE;
139 }
140
141 void setup_action_directional_focus_south(ObAction **a, ObUserAction uact)
142 {
143     (*a)->data.interdiraction.inter.any.interactive = TRUE;
144     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTH;
145     (*a)->data.interdiraction.dialog = TRUE;
146     (*a)->data.interdiraction.dock_windows = FALSE;
147     (*a)->data.interdiraction.desktop_windows = FALSE;
148 }
149
150 void setup_action_directional_focus_west(ObAction **a, ObUserAction uact)
151 {
152     (*a)->data.interdiraction.inter.any.interactive = TRUE;
153     (*a)->data.interdiraction.direction = OB_DIRECTION_WEST;
154     (*a)->data.interdiraction.dialog = TRUE;
155     (*a)->data.interdiraction.dock_windows = FALSE;
156     (*a)->data.interdiraction.desktop_windows = FALSE;
157 }
158
159 void setup_action_directional_focus_northeast(ObAction **a, ObUserAction uact)
160 {
161     (*a)->data.interdiraction.inter.any.interactive = TRUE;
162     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHEAST;
163     (*a)->data.interdiraction.dialog = TRUE;
164     (*a)->data.interdiraction.dock_windows = FALSE;
165     (*a)->data.interdiraction.desktop_windows = FALSE;
166 }
167
168 void setup_action_directional_focus_southeast(ObAction **a, ObUserAction uact)
169 {
170     (*a)->data.interdiraction.inter.any.interactive = TRUE;
171     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHEAST;
172     (*a)->data.interdiraction.dialog = TRUE;
173     (*a)->data.interdiraction.dock_windows = FALSE;
174     (*a)->data.interdiraction.desktop_windows = FALSE;
175 }
176
177 void setup_action_directional_focus_southwest(ObAction **a, ObUserAction uact)
178 {
179     (*a)->data.interdiraction.inter.any.interactive = TRUE;
180     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHWEST;
181     (*a)->data.interdiraction.dialog = TRUE;
182     (*a)->data.interdiraction.dock_windows = FALSE;
183     (*a)->data.interdiraction.desktop_windows = FALSE;
184 }
185
186 void setup_action_directional_focus_northwest(ObAction **a, ObUserAction uact)
187 {
188     (*a)->data.interdiraction.inter.any.interactive = TRUE;
189     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHWEST;
190     (*a)->data.interdiraction.dialog = TRUE;
191     (*a)->data.interdiraction.dock_windows = FALSE;
192     (*a)->data.interdiraction.desktop_windows = FALSE;
193 }
194
195 void setup_action_send_to_desktop(ObAction **a, ObUserAction uact)
196 {
197     (*a)->data.sendto.any.client_action = OB_CLIENT_ACTION_ALWAYS;
198     (*a)->data.sendto.follow = TRUE;
199 }
200
201 void setup_action_send_to_desktop_prev(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_WEST;
206     (*a)->data.sendtodir.linear = TRUE;
207     (*a)->data.sendtodir.wrap = TRUE;
208     (*a)->data.sendtodir.follow = TRUE;
209 }
210
211 void setup_action_send_to_desktop_next(ObAction **a, ObUserAction uact)
212 {
213     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
214     (*a)->data.sendtodir.inter.any.interactive = TRUE;
215     (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
216     (*a)->data.sendtodir.linear = TRUE;
217     (*a)->data.sendtodir.wrap = TRUE;
218     (*a)->data.sendtodir.follow = TRUE;
219 }
220
221 void setup_action_send_to_desktop_left(ObAction **a, ObUserAction uact)
222 {
223     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
224     (*a)->data.sendtodir.inter.any.interactive = TRUE;
225     (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
226     (*a)->data.sendtodir.linear = FALSE;
227     (*a)->data.sendtodir.wrap = TRUE;
228     (*a)->data.sendtodir.follow = TRUE;
229 }
230
231 void setup_action_send_to_desktop_right(ObAction **a, ObUserAction uact)
232 {
233     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
234     (*a)->data.sendtodir.inter.any.interactive = TRUE;
235     (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
236     (*a)->data.sendtodir.linear = FALSE;
237     (*a)->data.sendtodir.wrap = TRUE;
238     (*a)->data.sendtodir.follow = TRUE;
239 }
240
241 void setup_action_send_to_desktop_up(ObAction **a, ObUserAction uact)
242 {
243     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
244     (*a)->data.sendtodir.inter.any.interactive = TRUE;
245     (*a)->data.sendtodir.dir = OB_DIRECTION_NORTH;
246     (*a)->data.sendtodir.linear = FALSE;
247     (*a)->data.sendtodir.wrap = TRUE;
248     (*a)->data.sendtodir.follow = TRUE;
249 }
250
251 void setup_action_send_to_desktop_down(ObAction **a, ObUserAction uact)
252 {
253     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
254     (*a)->data.sendtodir.inter.any.interactive = TRUE;
255     (*a)->data.sendtodir.dir = OB_DIRECTION_SOUTH;
256     (*a)->data.sendtodir.linear = FALSE;
257     (*a)->data.sendtodir.wrap = TRUE;
258     (*a)->data.sendtodir.follow = TRUE;
259 }
260
261 void setup_action_desktop(ObAction **a, ObUserAction uact)
262 {
263 /*
264     (*a)->data.desktop.inter.any.interactive = FALSE;
265 */
266 }
267
268 void setup_action_desktop_prev(ObAction **a, ObUserAction uact)
269 {
270     (*a)->data.desktopdir.inter.any.interactive = TRUE;
271     (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
272     (*a)->data.desktopdir.linear = TRUE;
273     (*a)->data.desktopdir.wrap = TRUE;
274 }
275
276 void setup_action_desktop_next(ObAction **a, ObUserAction uact)
277 {
278     (*a)->data.desktopdir.inter.any.interactive = TRUE;
279     (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
280     (*a)->data.desktopdir.linear = TRUE;
281     (*a)->data.desktopdir.wrap = TRUE;
282 }
283
284 void setup_action_desktop_left(ObAction **a, ObUserAction uact)
285 {
286     (*a)->data.desktopdir.inter.any.interactive = TRUE;
287     (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
288     (*a)->data.desktopdir.linear = FALSE;
289     (*a)->data.desktopdir.wrap = TRUE;
290 }
291
292 void setup_action_desktop_right(ObAction **a, ObUserAction uact)
293 {
294     (*a)->data.desktopdir.inter.any.interactive = TRUE;
295     (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
296     (*a)->data.desktopdir.linear = FALSE;
297     (*a)->data.desktopdir.wrap = TRUE;
298 }
299
300 void setup_action_desktop_up(ObAction **a, ObUserAction uact)
301 {
302     (*a)->data.desktopdir.inter.any.interactive = TRUE;
303     (*a)->data.desktopdir.dir = OB_DIRECTION_NORTH;
304     (*a)->data.desktopdir.linear = FALSE;
305     (*a)->data.desktopdir.wrap = TRUE;
306 }
307
308 void setup_action_desktop_down(ObAction **a, ObUserAction uact)
309 {
310     (*a)->data.desktopdir.inter.any.interactive = TRUE;
311     (*a)->data.desktopdir.dir = OB_DIRECTION_SOUTH;
312     (*a)->data.desktopdir.linear = FALSE;
313     (*a)->data.desktopdir.wrap = TRUE;
314 }
315
316 void setup_action_cycle_windows_next(ObAction **a, ObUserAction uact)
317 {
318     (*a)->data.cycle.inter.any.interactive = TRUE;
319     (*a)->data.cycle.linear = FALSE;
320     (*a)->data.cycle.forward = TRUE;
321     (*a)->data.cycle.dialog = TRUE;
322     (*a)->data.cycle.dock_windows = FALSE;
323     (*a)->data.cycle.desktop_windows = FALSE;
324     (*a)->data.cycle.all_desktops = FALSE;
325 }
326
327 void setup_action_cycle_windows_previous(ObAction **a, ObUserAction uact)
328 {
329     (*a)->data.cycle.inter.any.interactive = TRUE;
330     (*a)->data.cycle.linear = FALSE;
331     (*a)->data.cycle.forward = FALSE;
332     (*a)->data.cycle.dialog = TRUE;
333     (*a)->data.cycle.dock_windows = FALSE;
334     (*a)->data.cycle.desktop_windows = FALSE;
335     (*a)->data.cycle.all_desktops = FALSE;
336 }
337
338 void setup_action_movefromedge_north(ObAction **a, ObUserAction uact)
339 {
340     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
341     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
342     (*a)->data.diraction.hang = TRUE;
343 }
344
345 void setup_action_movefromedge_south(ObAction **a, ObUserAction uact)
346 {
347     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
348     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
349     (*a)->data.diraction.hang = TRUE;
350 }
351
352 void setup_action_movefromedge_east(ObAction **a, ObUserAction uact)
353 {
354     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
355     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
356     (*a)->data.diraction.hang = TRUE;
357 }
358
359 void setup_action_movefromedge_west(ObAction **a, ObUserAction uact)
360 {
361     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
362     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
363     (*a)->data.diraction.hang = TRUE;
364 }
365
366 void setup_action_movetoedge_north(ObAction **a, ObUserAction uact)
367 {
368     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
369     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
370     (*a)->data.diraction.hang = FALSE;
371 }
372
373 void setup_action_movetoedge_south(ObAction **a, ObUserAction uact)
374 {
375     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
376     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
377     (*a)->data.diraction.hang = FALSE;
378 }
379
380 void setup_action_movetoedge_east(ObAction **a, ObUserAction uact)
381 {
382     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
383     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
384     (*a)->data.diraction.hang = FALSE;
385 }
386
387 void setup_action_movetoedge_west(ObAction **a, ObUserAction uact)
388 {
389     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
390     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
391     (*a)->data.diraction.hang = FALSE;
392 }
393
394 void setup_action_growtoedge_north(ObAction **a, ObUserAction uact)
395 {
396     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
397     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
398 }
399
400 void setup_action_growtoedge_south(ObAction **a, ObUserAction uact)
401 {
402     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
403     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
404 }
405
406 void setup_action_growtoedge_east(ObAction **a, ObUserAction uact)
407 {
408     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
409     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
410 }
411
412 void setup_action_growtoedge_west(ObAction **a, ObUserAction uact)
413 {
414     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
415     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
416 }
417
418 void setup_action_top_layer(ObAction **a, ObUserAction uact)
419 {
420     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
421     (*a)->data.layer.layer = 1;
422 }
423
424 void setup_action_normal_layer(ObAction **a, ObUserAction uact)
425 {
426     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
427     (*a)->data.layer.layer = 0;
428 }
429
430 void setup_action_bottom_layer(ObAction **a, ObUserAction uact)
431 {
432     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
433     (*a)->data.layer.layer = -1;
434 }
435
436 void setup_action_move(ObAction **a, ObUserAction uact)
437 {
438     (*a)->data.moveresize.any.client_action = OB_CLIENT_ACTION_ALWAYS;
439     (*a)->data.moveresize.keyboard =
440         (uact == OB_USER_ACTION_NONE ||
441          uact == OB_USER_ACTION_KEYBOARD_KEY ||
442          uact == OB_USER_ACTION_MENU_SELECTION);
443     (*a)->data.moveresize.corner = 0;
444 }
445
446 void setup_action_resize(ObAction **a, ObUserAction uact)
447 {
448     (*a)->data.moveresize.any.client_action = OB_CLIENT_ACTION_ALWAYS;
449     (*a)->data.moveresize.keyboard =
450         (uact == OB_USER_ACTION_NONE ||
451          uact == OB_USER_ACTION_KEYBOARD_KEY ||
452          uact == OB_USER_ACTION_MENU_SELECTION);
453     (*a)->data.moveresize.corner = 0;
454 }
455
456 void setup_action_showmenu(ObAction **a, ObUserAction uact)
457 {
458     (*a)->data.showmenu.any.client_action = OB_CLIENT_ACTION_OPTIONAL;
459     /* you cannot call ShowMenu from inside a menu, cuz the menu code makes
460        assumptions that there is only one menu (and submenus) open at
461        a time! */
462     if (uact == OB_USER_ACTION_MENU_SELECTION) {
463         action_unref(*a);
464         *a = NULL;
465     }
466 }
467
468 void setup_action_focus(ObAction **a, ObUserAction uact)
469 {
470     (*a)->data.any.client_action = OB_CLIENT_ACTION_OPTIONAL;
471 }
472
473 void setup_client_action(ObAction **a, ObUserAction uact)
474 {
475     (*a)->data.any.client_action = OB_CLIENT_ACTION_ALWAYS;
476 }
477
478 ActionString actionstrings[] =
479 {
480     {
481         "debug", 
482         action_debug,
483         NULL
484     },
485     {
486         "execute", 
487         action_execute,
488         NULL
489     },
490     {
491         "directionalfocusnorth", 
492         action_directional_focus, 
493         setup_action_directional_focus_north
494     },
495     {
496         "directionalfocuseast", 
497         action_directional_focus, 
498         setup_action_directional_focus_east
499     },
500     {
501         "directionalfocussouth", 
502         action_directional_focus, 
503         setup_action_directional_focus_south
504     },
505     {
506         "directionalfocuswest",
507         action_directional_focus,
508         setup_action_directional_focus_west
509     },
510     {
511         "directionalfocusnortheast",
512         action_directional_focus,
513         setup_action_directional_focus_northeast
514     },
515     {
516         "directionalfocussoutheast",
517         action_directional_focus,
518         setup_action_directional_focus_southeast
519     },
520     {
521         "directionalfocussouthwest",
522         action_directional_focus,
523         setup_action_directional_focus_southwest
524     },
525     {
526         "directionalfocusnorthwest",
527         action_directional_focus,
528         setup_action_directional_focus_northwest
529     },
530     {
531         "activate",
532         action_activate,
533         setup_action_focus
534     },
535     {
536         "focus",
537         action_focus,
538         setup_action_focus
539     },
540     {
541         "unfocus",
542         action_unfocus,
543         setup_client_action
544     },
545     {
546         "iconify",
547         action_iconify,
548         setup_client_action
549     },
550     {
551         "focustobottom",
552         action_focus_order_to_bottom,
553         setup_client_action
554     },
555     {
556         "raiselower",
557         action_raiselower,
558         setup_client_action
559     },
560     {
561         "raise",
562         action_raise,
563         setup_client_action
564     },
565     {
566         "lower",
567         action_lower,
568         setup_client_action
569     },
570     {
571         "close",
572         action_close,
573         setup_client_action
574     },
575     {
576         "kill",
577         action_kill,
578         setup_client_action
579     },
580     {
581         "shadelower",
582         action_shadelower,
583         setup_client_action
584     },
585     {
586         "unshaderaise",
587         action_unshaderaise,
588         setup_client_action
589     },
590     {
591         "shade",
592         action_shade,
593         setup_client_action
594     },
595     {
596         "unshade",
597         action_unshade,
598         setup_client_action
599     },
600     {
601         "toggleshade",
602         action_toggle_shade,
603         setup_client_action
604     },
605     {
606         "toggleomnipresent",
607         action_toggle_omnipresent,
608         setup_client_action
609     },
610     {
611         "moverelativehorz",
612         action_move_relative_horz,
613         setup_client_action
614     },
615     {
616         "moverelativevert",
617         action_move_relative_vert,
618         setup_client_action
619     },
620     {
621         "movetocenter",
622         action_move_to_center,
623         setup_client_action
624     },
625     {
626         "resizerelativehorz",
627         action_resize_relative_horz,
628         setup_client_action
629     },
630     {
631         "resizerelativevert",
632         action_resize_relative_vert,
633         setup_client_action
634     },
635     {
636         "moverelative",
637         action_move_relative,
638         setup_client_action
639     },
640     {
641         "resizerelative",
642         action_resize_relative,
643         setup_client_action
644     },
645     {
646         "maximizefull",
647         action_maximize_full,
648         setup_client_action
649     },
650     {
651         "unmaximizefull",
652         action_unmaximize_full,
653         setup_client_action
654     },
655     {
656         "togglemaximizefull",
657         action_toggle_maximize_full,
658         setup_client_action
659     },
660     {
661         "maximizehorz",
662         action_maximize_horz,
663         setup_client_action
664     },
665     {
666         "unmaximizehorz",
667         action_unmaximize_horz,
668         setup_client_action
669     },
670     {
671         "togglemaximizehorz",
672         action_toggle_maximize_horz,
673         setup_client_action
674     },
675     {
676         "maximizevert",
677         action_maximize_vert,
678         setup_client_action
679     },
680     {
681         "unmaximizevert",
682         action_unmaximize_vert,
683         setup_client_action
684     },
685     {
686         "togglemaximizevert",
687         action_toggle_maximize_vert,
688         setup_client_action
689     },
690     {
691         "togglefullscreen",
692         action_toggle_fullscreen,
693         setup_client_action
694     },
695     {
696         "sendtodesktop",
697         action_send_to_desktop,
698         setup_action_send_to_desktop
699     },
700     {
701         "sendtodesktopnext",
702         action_send_to_desktop_dir,
703         setup_action_send_to_desktop_next
704     },
705     {
706         "sendtodesktopprevious",
707         action_send_to_desktop_dir,
708         setup_action_send_to_desktop_prev
709     },
710     {
711         "sendtodesktopright",
712         action_send_to_desktop_dir,
713         setup_action_send_to_desktop_right
714     },
715     {
716         "sendtodesktopleft",
717         action_send_to_desktop_dir,
718         setup_action_send_to_desktop_left
719     },
720     {
721         "sendtodesktopup",
722         action_send_to_desktop_dir,
723         setup_action_send_to_desktop_up
724     },
725     {
726         "sendtodesktopdown",
727         action_send_to_desktop_dir,
728         setup_action_send_to_desktop_down
729     },
730     {
731         "desktop",
732         action_desktop,
733         setup_action_desktop
734     },
735     {
736         "desktopnext",
737         action_desktop_dir,
738         setup_action_desktop_next
739     },
740     {
741         "desktopprevious",
742         action_desktop_dir,
743         setup_action_desktop_prev
744     },
745     {
746         "desktopright",
747         action_desktop_dir,
748         setup_action_desktop_right
749     },
750     {
751         "desktopleft",
752         action_desktop_dir,
753         setup_action_desktop_left
754     },
755     {
756         "desktopup",
757         action_desktop_dir,
758         setup_action_desktop_up
759     },
760     {
761         "desktopdown",
762         action_desktop_dir,
763         setup_action_desktop_down
764     },
765     {
766         "toggledecorations",
767         action_toggle_decorations,
768         setup_client_action
769     },
770     {
771         "move",
772         action_move,
773         setup_action_move
774     },
775     {
776         "resize",
777         action_resize,
778         setup_action_resize
779     },
780     {
781         "toggledockautohide",
782         action_toggle_dockautohide,
783         NULL
784     },
785     {
786         "toggleshowdesktop",
787         action_toggle_show_desktop,
788         NULL
789     },
790     {
791         "showdesktop",
792         action_show_desktop,
793         NULL
794     },
795     {
796         "unshowdesktop",
797         action_unshow_desktop,
798         NULL
799     },
800     {
801         "desktoplast",
802         action_desktop_last,
803         NULL
804     },
805     {
806         "reconfigure",
807         action_reconfigure,
808         NULL
809     },
810     {
811         "restart",
812         action_restart,
813         NULL
814     },
815     {
816         "exit",
817         action_exit,
818         NULL
819     },
820     {
821         "showmenu",
822         action_showmenu,
823         setup_action_showmenu
824     },
825     {
826         "sendtotoplayer",
827         action_send_to_layer,
828         setup_action_top_layer
829     },
830     {
831         "togglealwaysontop",
832         action_toggle_layer,
833         setup_action_top_layer
834     },
835     {
836         "sendtonormallayer",
837         action_send_to_layer,
838         setup_action_normal_layer
839     },
840     {
841         "sendtobottomlayer",
842         action_send_to_layer,
843         setup_action_bottom_layer
844     },
845     {
846         "togglealwaysonbottom",
847         action_toggle_layer,
848         setup_action_bottom_layer
849     },
850     {
851         "nextwindow",
852         action_cycle_windows,
853         setup_action_cycle_windows_next
854     },
855     {
856         "previouswindow",
857         action_cycle_windows,
858         setup_action_cycle_windows_previous
859     },
860     {
861         "movefromedgenorth",
862         action_movetoedge,
863         setup_action_movefromedge_north
864     },
865     {
866         "movefromedgesouth",
867         action_movetoedge,
868         setup_action_movefromedge_south
869     },
870     {
871         "movefromedgewest",
872         action_movetoedge,
873         setup_action_movefromedge_west
874     },
875     {
876         "movefromedgeeast",
877         action_movetoedge,
878         setup_action_movefromedge_east
879     },
880     {
881         "movetoedgenorth",
882         action_movetoedge,
883         setup_action_movetoedge_north
884     },
885     {
886         "movetoedgesouth",
887         action_movetoedge,
888         setup_action_movetoedge_south
889     },
890     {
891         "movetoedgewest",
892         action_movetoedge,
893         setup_action_movetoedge_west
894     },
895     {
896         "movetoedgeeast",
897         action_movetoedge,
898         setup_action_movetoedge_east
899     },
900     {
901         "growtoedgenorth",
902         action_growtoedge,
903         setup_action_growtoedge_north
904     },
905     {
906         "growtoedgesouth",
907         action_growtoedge,
908         setup_action_growtoedge_south
909     },
910     {
911         "growtoedgewest",
912         action_growtoedge,
913         setup_action_growtoedge_west
914     },
915     {
916         "growtoedgeeast",
917         action_growtoedge,
918         setup_action_growtoedge_east
919     },
920     {
921         "breakchroot",
922         action_break_chroot,
923         NULL
924     },
925     {
926         NULL,
927         NULL,
928         NULL
929     }
930 };
931
932 /* only key bindings can be interactive. thus saith the xor.
933    because of how the mouse is grabbed, mouse events dont even get
934    read during interactive events, so no dice! >:) */
935 #define INTERACTIVE_LIMIT(a, uact) \
936     if (uact != OB_USER_ACTION_KEYBOARD_KEY) \
937         a->data.any.interactive = FALSE;
938
939 ObAction *action_from_string(const gchar *name, ObUserAction uact)
940 {
941     ObAction *a = NULL;
942     gboolean exist = FALSE;
943     gint i;
944
945     for (i = 0; actionstrings[i].name; i++)
946         if (!g_ascii_strcasecmp(name, actionstrings[i].name)) {
947             exist = TRUE;
948             a = action_new(actionstrings[i].func);
949             if (actionstrings[i].setup)
950                 actionstrings[i].setup(&a, uact);
951             if (a)
952                 INTERACTIVE_LIMIT(a, uact);
953             break;
954         }
955     if (!exist)
956         g_message(_("Invalid action '%s' requested. No such action exists."),
957                   name);
958     if (!a)
959         g_message(_("Invalid use of action '%s'. Action will be ignored."),
960                   name);
961     return a;
962 }
963
964 ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
965                        ObUserAction uact)
966 {
967     gchar *actname;
968     ObAction *act = NULL;
969     xmlNodePtr n;
970
971     if (parse_attr_string("name", node, &actname)) {
972         if ((act = action_from_string(actname, uact))) {
973             if (act->func == action_execute || act->func == action_restart) {
974                 if ((n = parse_find_node("execute", node->xmlChildrenNode))) {
975                     gchar *s = parse_string(doc, n);
976                     act->data.execute.path = parse_expand_tilde(s);
977                     g_free(s);
978                 }
979                 if ((n = parse_find_node("startupnotify", node->xmlChildrenNode))) {
980                     xmlNodePtr m;
981                     if ((m = parse_find_node("enabled", n->xmlChildrenNode)))
982                         act->data.execute.startupnotify = parse_bool(doc, m);
983                     if ((m = parse_find_node("name", n->xmlChildrenNode)))
984                         act->data.execute.name = parse_string(doc, m);
985                     if ((m = parse_find_node("icon", n->xmlChildrenNode)))
986                         act->data.execute.icon_name = parse_string(doc, m);
987                 }
988             } else if (act->func == action_debug) {
989                 if ((n = parse_find_node("string", node->xmlChildrenNode)))
990                     act->data.debug.string = parse_string(doc, n);
991             } else if (act->func == action_showmenu) {
992                 if ((n = parse_find_node("menu", node->xmlChildrenNode)))
993                     act->data.showmenu.name = parse_string(doc, n);
994             } else if (act->func == action_move_relative_horz ||
995                        act->func == action_move_relative_vert ||
996                        act->func == action_resize_relative_horz ||
997                        act->func == action_resize_relative_vert) {
998                 if ((n = parse_find_node("delta", node->xmlChildrenNode)))
999                     act->data.relative.deltax = parse_int(doc, n);
1000             } else if (act->func == action_move_relative) {
1001                 if ((n = parse_find_node("x", node->xmlChildrenNode)))
1002                     act->data.relative.deltax = parse_int(doc, n);
1003                 if ((n = parse_find_node("y", node->xmlChildrenNode)))
1004                     act->data.relative.deltay = parse_int(doc, n);
1005             } else if (act->func == action_resize_relative) {
1006                 if ((n = parse_find_node("left", node->xmlChildrenNode)))
1007                     act->data.relative.deltaxl = parse_int(doc, n);
1008                 if ((n = parse_find_node("up", node->xmlChildrenNode)))
1009                     act->data.relative.deltayu = parse_int(doc, n);
1010                 if ((n = parse_find_node("right", node->xmlChildrenNode)))
1011                     act->data.relative.deltax = parse_int(doc, n);
1012                 if ((n = parse_find_node("down", node->xmlChildrenNode)))
1013                     act->data.relative.deltay = parse_int(doc, n);
1014             } else if (act->func == action_desktop) {
1015                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
1016                     act->data.desktop.desk = parse_int(doc, n);
1017                 if (act->data.desktop.desk > 0) act->data.desktop.desk--;
1018 /*
1019                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1020                     act->data.desktop.inter.any.interactive =
1021                         parse_bool(doc, n);
1022 */
1023            } else if (act->func == action_send_to_desktop) {
1024                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
1025                     act->data.sendto.desk = parse_int(doc, n);
1026                 if (act->data.sendto.desk > 0) act->data.sendto.desk--;
1027                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
1028                     act->data.sendto.follow = parse_bool(doc, n);
1029             } else if (act->func == action_desktop_dir) {
1030                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
1031                     act->data.desktopdir.wrap = parse_bool(doc, n); 
1032                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1033                     act->data.desktopdir.inter.any.interactive =
1034                         parse_bool(doc, n);
1035             } else if (act->func == action_send_to_desktop_dir) {
1036                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
1037                     act->data.sendtodir.wrap = parse_bool(doc, n);
1038                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
1039                     act->data.sendtodir.follow = parse_bool(doc, n);
1040                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1041                     act->data.sendtodir.inter.any.interactive =
1042                         parse_bool(doc, n);
1043             } else if (act->func == action_activate) {
1044                 if ((n = parse_find_node("here", node->xmlChildrenNode)))
1045                     act->data.activate.here = parse_bool(doc, n);
1046             } else if (act->func == action_cycle_windows) {
1047                 if ((n = parse_find_node("linear", node->xmlChildrenNode)))
1048                     act->data.cycle.linear = parse_bool(doc, n);
1049                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1050                     act->data.cycle.dialog = parse_bool(doc, n);
1051                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
1052                     act->data.cycle.dock_windows = parse_bool(doc, n);
1053                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
1054                     act->data.cycle.desktop_windows = parse_bool(doc, n);
1055                 if ((n = parse_find_node("allDesktops",
1056                                          node->xmlChildrenNode)))
1057                     act->data.cycle.all_desktops = parse_bool(doc, n);
1058             } else if (act->func == action_directional_focus) {
1059                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1060                     act->data.interdiraction.dialog = parse_bool(doc, n);
1061                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
1062                     act->data.interdiraction.dock_windows = parse_bool(doc, n);
1063                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
1064                     act->data.interdiraction.desktop_windows =
1065                         parse_bool(doc, n);
1066             } else if (act->func == action_resize) {
1067                 if ((n = parse_find_node("edge", node->xmlChildrenNode))) {
1068                     gchar *s = parse_string(doc, n);
1069                     if (!g_ascii_strcasecmp(s, "top"))
1070                         act->data.moveresize.corner =
1071                             prop_atoms.net_wm_moveresize_size_top;
1072                     else if (!g_ascii_strcasecmp(s, "bottom"))
1073                         act->data.moveresize.corner =
1074                             prop_atoms.net_wm_moveresize_size_bottom;
1075                     else if (!g_ascii_strcasecmp(s, "left"))
1076                         act->data.moveresize.corner =
1077                             prop_atoms.net_wm_moveresize_size_left;
1078                     else if (!g_ascii_strcasecmp(s, "right"))
1079                         act->data.moveresize.corner =
1080                             prop_atoms.net_wm_moveresize_size_right;
1081                     else if (!g_ascii_strcasecmp(s, "topleft"))
1082                         act->data.moveresize.corner =
1083                             prop_atoms.net_wm_moveresize_size_topleft;
1084                     else if (!g_ascii_strcasecmp(s, "topright"))
1085                         act->data.moveresize.corner =
1086                             prop_atoms.net_wm_moveresize_size_topright;
1087                     else if (!g_ascii_strcasecmp(s, "bottomleft"))
1088                         act->data.moveresize.corner =
1089                             prop_atoms.net_wm_moveresize_size_bottomleft;
1090                     else if (!g_ascii_strcasecmp(s, "bottomright"))
1091                         act->data.moveresize.corner =
1092                             prop_atoms.net_wm_moveresize_size_bottomright;
1093                     g_free(s);
1094                 }
1095             } else if (act->func == action_raise ||
1096                        act->func == action_lower ||
1097                        act->func == action_raiselower ||
1098                        act->func == action_shadelower ||
1099                        act->func == action_unshaderaise) {
1100             }
1101             INTERACTIVE_LIMIT(act, uact);
1102         }
1103         g_free(actname);
1104     }
1105     return act;
1106 }
1107
1108 void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
1109                      guint state, guint button, gint x, gint y, Time time,
1110                      gboolean cancel, gboolean done)
1111 {
1112     GSList *it;
1113     ObAction *a;
1114     gboolean ungrabbed = FALSE;
1115
1116     if (!acts)
1117         return;
1118
1119     if (x < 0 && y < 0)
1120         screen_pointer_pos(&x, &y);
1121
1122     for (it = acts; it; it = g_slist_next(it)) {
1123         a = it->data;
1124
1125         if (!(a->data.any.client_action == OB_CLIENT_ACTION_ALWAYS && !c)) {
1126             a->data.any.c = a->data.any.client_action ? c : NULL;
1127             a->data.any.context = context;
1128             a->data.any.x = x;
1129             a->data.any.y = y;
1130
1131             a->data.any.button = button;
1132
1133             a->data.any.time = time;
1134
1135             if (a->data.any.interactive) {
1136                 a->data.inter.cancel = cancel;
1137                 a->data.inter.final = done;
1138                 if (!(cancel || done))
1139                     if (!keyboard_interactive_grab(state, a->data.any.c, a))
1140                         continue;
1141             }
1142             else if (!ungrabbed) {
1143                 /* Ungrab the keyboard before running the action if it's not
1144                    interactive.
1145
1146                    If there is an interactive action going on, then cancel it
1147                    to release the keyboard. If not, then call
1148                    XUngrabKeyboard().
1149
1150                    We call XUngrabKeyboard() because a key press causes a
1151                    passive grab on the keyboard, and so if program we are
1152                    executing wants to grab the keyboard, it will fail if the
1153                    button is still held down (which is likely).
1154
1155                    Use the X function not out own, because we're not
1156                    considering a grab to be in place at all so our function
1157                    won't try ungrab anything.
1158                 */
1159                 if (keyboard_interactively_grabbed())
1160                     keyboard_interactive_cancel();
1161                 else
1162                     XUngrabKeyboard(ob_display, time);
1163
1164                 /* We don't the same with XUngrabPointer, even though it can
1165                    cause the same problem. But then Press bindings cause
1166                    Drag bindings to break.
1167                 XUngrabPointer(ob_display, time);*/
1168             }
1169
1170             /* XXX UGLY HACK race with motion event starting a move and the
1171                button release gettnig processed first. answer: don't queue
1172                moveresize starts. UGLY HACK XXX */
1173             if (a->data.any.interactive || a->func == action_move ||
1174                 a->func == action_resize)
1175             {
1176                 /* interactive actions are not queued */
1177                 a->func(&a->data);
1178             } else if (c &&
1179                        (context == OB_FRAME_CONTEXT_CLIENT ||
1180                         (c->type == OB_CLIENT_TYPE_DESKTOP &&
1181                          context == OB_FRAME_CONTEXT_DESKTOP)) &&
1182                        (a->func == action_focus ||
1183                         a->func == action_activate ||
1184                         a->func == action_showmenu))
1185             {
1186                 /* XXX MORE UGLY HACK
1187                    actions from clicks on client windows are NOT queued.
1188                    this solves the mysterious click-and-drag-doesnt-work
1189                    problem. it was because the window gets focused and stuff
1190                    after the button event has already been passed through. i
1191                    dont really know why it should care but it does and it makes
1192                    a difference.
1193
1194                    however this very bogus ! !
1195                    we want to send the button press to the window BEFORE
1196                    we do the action because the action might move the windows
1197                    (eg change desktops) and then the button press ends up on
1198                    the completely wrong window !
1199                    so, this is just for that bug, and it will only NOT queue it
1200                    if it is a focusing action that can be used with the mouse
1201                    pointer. ugh.
1202
1203                    also with the menus, there is a race going on. if the
1204                    desktop wants to pop up a menu, and we do to, we send them
1205                    the button before we pop up the menu, so they pop up their
1206                    menu first. but not always. if we pop up our menu before
1207                    sending them the button press, then the result is
1208                    deterministic. yay.
1209                 */
1210                 a->func(&a->data);
1211             } else
1212                 ob_main_loop_queue_action(ob_main_loop, a);
1213         }
1214     }
1215 }
1216
1217 void action_run_string(const gchar *name, struct _ObClient *c, Time time)
1218 {
1219     ObAction *a;
1220     GSList *l;
1221
1222     a = action_from_string(name, OB_USER_ACTION_NONE);
1223     g_assert(a);
1224
1225     l = g_slist_append(NULL, a);
1226
1227     action_run(l, c, 0, time);
1228 }
1229
1230 void action_debug(union ActionData *data)
1231 {
1232     if (data->debug.string)
1233         g_print("%s\n", data->debug.string);
1234 }
1235
1236 void action_execute(union ActionData *data)
1237 {
1238     GError *e = NULL;
1239     gchar *cmd, **argv = 0;
1240     if (data->execute.path) {
1241         cmd = g_filename_from_utf8(data->execute.path, -1, NULL, NULL, NULL);
1242         if (cmd) {
1243             if (!g_shell_parse_argv (cmd, NULL, &argv, &e)) {
1244                 g_message(_("Failed to execute '%s': %s"),
1245                           cmd, e->message);
1246                 g_error_free(e);
1247             } else if (data->execute.startupnotify) {
1248                 gchar *program;
1249                 
1250                 program = g_path_get_basename(argv[0]);
1251                 /* sets up the environment */
1252                 sn_setup_spawn_environment(program,
1253                                            data->execute.name,
1254                                            data->execute.icon_name,
1255                                            /* launch it on the current
1256                                               desktop */
1257                                            screen_desktop,
1258                                            data->execute.any.time);
1259                 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH |
1260                                    G_SPAWN_DO_NOT_REAP_CHILD,
1261                                    NULL, NULL, NULL, &e)) {
1262                     g_message(_("Failed to execute '%s': %s"),
1263                               cmd, e->message);
1264                     g_error_free(e);
1265                     sn_spawn_cancel();
1266                 }
1267                 unsetenv("DESKTOP_STARTUP_ID");
1268                 g_free(program);
1269                 g_strfreev(argv);
1270             } else {
1271                 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH |
1272                                    G_SPAWN_DO_NOT_REAP_CHILD,
1273                                    NULL, NULL, NULL, &e))
1274                 {
1275                     g_message(_("Failed to execute '%s': %s"),
1276                               cmd, e->message);
1277                     g_error_free(e);
1278                 }
1279                 g_strfreev(argv);
1280             }
1281             g_free(cmd);
1282         } else {
1283             g_message(_("Failed to convert the path '%s' from utf8"),
1284                       data->execute.path);
1285         }
1286     }
1287 }
1288
1289 void action_activate(union ActionData *data)
1290 {
1291     if (data->client.any.c) {
1292         if (!data->any.button || client_mouse_focusable(data->client.any.c) ||
1293             (data->any.context != OB_FRAME_CONTEXT_CLIENT &&
1294              data->any.context != OB_FRAME_CONTEXT_FRAME))
1295         {
1296             /* if using focus_delay, stop the timer now so that focus doesn't
1297                go moving on us */
1298             event_halt_focus_delay();
1299
1300             client_activate(data->activate.any.c, data->activate.here, TRUE);
1301         }
1302     } else {
1303         /* focus action on something other than a client, make keybindings
1304            work for this openbox instance, but don't focus any specific client
1305         */
1306         focus_nothing();
1307     }
1308 }
1309
1310 void action_focus(union ActionData *data)
1311 {
1312     if (data->client.any.c) {
1313         if (!data->any.button || client_mouse_focusable(data->client.any.c) ||
1314             (data->any.context != OB_FRAME_CONTEXT_CLIENT &&
1315              data->any.context != OB_FRAME_CONTEXT_FRAME))
1316         {
1317             /* if using focus_delay, stop the timer now so that focus doesn't
1318                go moving on us */
1319             event_halt_focus_delay();
1320
1321             client_focus(data->client.any.c);
1322         }
1323     } else {
1324         /* focus action on something other than a client, make keybindings
1325            work for this openbox instance, but don't focus any specific client
1326         */
1327         focus_nothing();
1328     }
1329 }
1330
1331 void action_unfocus (union ActionData *data)
1332 {
1333     if (data->client.any.c == focus_client)
1334         focus_fallback(TRUE);
1335 }
1336
1337 void action_iconify(union ActionData *data)
1338 {
1339     client_action_start(data);
1340     client_iconify(data->client.any.c, TRUE, TRUE, FALSE);
1341     client_action_end(data);
1342 }
1343
1344 void action_focus_order_to_bottom(union ActionData *data)
1345 {
1346     focus_order_to_bottom(data->client.any.c);
1347 }
1348
1349 void action_raiselower(union ActionData *data)
1350 {
1351     ObClient *c = data->client.any.c;
1352     GList *it;
1353     gboolean raise = FALSE;
1354
1355     for (it = stacking_list; it; it = g_list_next(it)) {
1356         if (WINDOW_IS_CLIENT(it->data)) {
1357             ObClient *cit = it->data;
1358
1359             if (cit == c) break;
1360             if (client_normal(cit) == client_normal(c) &&
1361                 cit->layer == c->layer &&
1362                 cit->frame->visible &&
1363                 !client_search_transient(c, cit))
1364             {
1365                 if (RECT_INTERSECTS_RECT(cit->frame->area, c->frame->area)) {
1366                     raise = TRUE;
1367                     break;
1368                 }
1369             }
1370         }
1371     }
1372
1373     if (raise)
1374         action_raise(data);
1375     else
1376         action_lower(data);
1377 }
1378
1379 void action_raise(union ActionData *data)
1380 {
1381     client_action_start(data);
1382     stacking_raise(CLIENT_AS_WINDOW(data->client.any.c));
1383     client_action_end(data);
1384 }
1385
1386 void action_unshaderaise(union ActionData *data)
1387 {
1388     if (data->client.any.c->shaded)
1389         action_unshade(data);
1390     else
1391         action_raise(data);
1392 }
1393
1394 void action_shadelower(union ActionData *data)
1395 {
1396     if (data->client.any.c->shaded)
1397         action_lower(data);
1398     else
1399         action_shade(data);
1400 }
1401
1402 void action_lower(union ActionData *data)
1403 {
1404     client_action_start(data);
1405     stacking_lower(CLIENT_AS_WINDOW(data->client.any.c));
1406     client_action_end(data);
1407 }
1408
1409 void action_close(union ActionData *data)
1410 {
1411     client_close(data->client.any.c);
1412 }
1413
1414 void action_kill(union ActionData *data)
1415 {
1416     client_kill(data->client.any.c);
1417 }
1418
1419 void action_shade(union ActionData *data)
1420 {
1421     client_action_start(data);
1422     client_shade(data->client.any.c, TRUE);
1423     client_action_end(data);
1424 }
1425
1426 void action_unshade(union ActionData *data)
1427 {
1428     client_action_start(data);
1429     client_shade(data->client.any.c, FALSE);
1430     client_action_end(data);
1431 }
1432
1433 void action_toggle_shade(union ActionData *data)
1434 {
1435     client_action_start(data);
1436     client_shade(data->client.any.c, !data->client.any.c->shaded);
1437     client_action_end(data);
1438 }
1439
1440 void action_toggle_omnipresent(union ActionData *data)
1441
1442     client_set_desktop(data->client.any.c,
1443                        data->client.any.c->desktop == DESKTOP_ALL ?
1444                        screen_desktop : DESKTOP_ALL, FALSE);
1445 }
1446
1447 void action_move_relative_horz(union ActionData *data)
1448 {
1449     ObClient *c = data->relative.any.c;
1450     client_action_start(data);
1451     client_move(c, c->area.x + data->relative.deltax, c->area.y);
1452     client_action_end(data);
1453 }
1454
1455 void action_move_relative_vert(union ActionData *data)
1456 {
1457     ObClient *c = data->relative.any.c;
1458     client_action_start(data);
1459     client_move(c, c->area.x, c->area.y + data->relative.deltax);
1460     client_action_end(data);
1461 }
1462
1463 void action_move_to_center(union ActionData *data)
1464 {
1465     ObClient *c = data->client.any.c;
1466     Rect *area;
1467     area = screen_area_monitor(c->desktop, 0);
1468     client_action_start(data);
1469     client_move(c, area->width / 2 - c->area.width / 2,
1470                 area->height / 2 - c->area.height / 2);
1471     client_action_end(data);
1472 }
1473
1474 void action_resize_relative_horz(union ActionData *data)
1475 {
1476     ObClient *c = data->relative.any.c;
1477     client_action_start(data);
1478     client_resize(c,
1479                   c->area.width + data->relative.deltax * c->size_inc.width,
1480                   c->area.height);
1481     client_action_end(data);
1482 }
1483
1484 void action_resize_relative_vert(union ActionData *data)
1485 {
1486     ObClient *c = data->relative.any.c;
1487     if (!c->shaded) {
1488         client_action_start(data);
1489         client_resize(c, c->area.width, c->area.height +
1490                       data->relative.deltax * c->size_inc.height);
1491         client_action_end(data);
1492     }
1493 }
1494
1495 void action_move_relative(union ActionData *data)
1496 {
1497     ObClient *c = data->relative.any.c;
1498     client_action_start(data);
1499     client_move(c, c->area.x + data->relative.deltax, c->area.y +
1500                 data->relative.deltay);
1501     client_action_end(data);
1502 }
1503
1504 void action_resize_relative(union ActionData *data)
1505 {
1506     ObClient *c = data->relative.any.c;
1507     gint x, y, ow, w, oh, h, lw, lh;
1508
1509     client_action_start(data);
1510
1511     x = c->area.x;
1512     y = c->area.y;
1513     ow = c->area.width;
1514     w = ow + data->relative.deltax * c->size_inc.width
1515         + data->relative.deltaxl * c->size_inc.width;
1516     oh = c->area.height;
1517     h = oh + data->relative.deltay * c->size_inc.height
1518         + data->relative.deltayu * c->size_inc.height;
1519     
1520     client_try_configure(c, &x, &y, &w, &h, &lw, &lh, TRUE);
1521     client_move_resize(c, x + (ow - w), y + (oh - h), w, h);
1522     client_action_end(data);
1523 }
1524
1525 void action_maximize_full(union ActionData *data)
1526 {
1527     client_action_start(data);
1528     client_maximize(data->client.any.c, TRUE, 0);
1529     client_action_end(data);
1530 }
1531
1532 void action_unmaximize_full(union ActionData *data)
1533 {
1534     client_action_start(data);
1535     client_maximize(data->client.any.c, FALSE, 0);
1536     client_action_end(data);
1537 }
1538
1539 void action_toggle_maximize_full(union ActionData *data)
1540 {
1541     client_action_start(data);
1542     client_maximize(data->client.any.c,
1543                     !(data->client.any.c->max_horz ||
1544                       data->client.any.c->max_vert),
1545                     0);
1546     client_action_end(data);
1547 }
1548
1549 void action_maximize_horz(union ActionData *data)
1550 {
1551     client_action_start(data);
1552     client_maximize(data->client.any.c, TRUE, 1);
1553     client_action_end(data);
1554 }
1555
1556 void action_unmaximize_horz(union ActionData *data)
1557 {
1558     client_action_start(data);
1559     client_maximize(data->client.any.c, FALSE, 1);
1560     client_action_end(data);
1561 }
1562
1563 void action_toggle_maximize_horz(union ActionData *data)
1564 {
1565     client_action_start(data);
1566     client_maximize(data->client.any.c,
1567                     !data->client.any.c->max_horz, 1);
1568     client_action_end(data);
1569 }
1570
1571 void action_maximize_vert(union ActionData *data)
1572 {
1573     client_action_start(data);
1574     client_maximize(data->client.any.c, TRUE, 2);
1575     client_action_end(data);
1576 }
1577
1578 void action_unmaximize_vert(union ActionData *data)
1579 {
1580     client_action_start(data);
1581     client_maximize(data->client.any.c, FALSE, 2);
1582     client_action_end(data);
1583 }
1584
1585 void action_toggle_maximize_vert(union ActionData *data)
1586 {
1587     client_action_start(data);
1588     client_maximize(data->client.any.c,
1589                     !data->client.any.c->max_vert, 2);
1590     client_action_end(data);
1591 }
1592
1593 void action_toggle_fullscreen(union ActionData *data)
1594 {
1595     client_action_start(data);
1596     client_fullscreen(data->client.any.c, !(data->client.any.c->fullscreen));
1597     client_action_end(data);
1598 }
1599
1600 void action_send_to_desktop(union ActionData *data)
1601 {
1602     ObClient *c = data->sendto.any.c;
1603
1604     if (!client_normal(c)) return;
1605
1606     if (data->sendto.desk < screen_num_desktops ||
1607         data->sendto.desk == DESKTOP_ALL) {
1608         client_set_desktop(c, data->sendto.desk, data->sendto.follow);
1609         if (data->sendto.follow)
1610             screen_set_desktop(data->sendto.desk, TRUE);
1611     }
1612 }
1613
1614 void action_desktop(union ActionData *data)
1615 {
1616     /* XXX add the interactive/dialog option back again once the dialog
1617        has been made to not use grabs */
1618     if (data->desktop.desk < screen_num_desktops ||
1619         data->desktop.desk == DESKTOP_ALL)
1620     {
1621         screen_set_desktop(data->desktop.desk, TRUE);
1622         if (data->inter.any.interactive)
1623             screen_desktop_popup(data->desktop.desk, TRUE);
1624     }
1625 }
1626
1627 void action_desktop_dir(union ActionData *data)
1628 {
1629     guint d;
1630
1631     d = screen_cycle_desktop(data->desktopdir.dir,
1632                              data->desktopdir.wrap,
1633                              data->desktopdir.linear,
1634                              data->desktopdir.inter.any.interactive,
1635                              data->desktopdir.inter.final,
1636                              data->desktopdir.inter.cancel);
1637     /* only move the desktop when the action is complete. if we switch
1638        desktops during the interactive action, focus will move but with
1639        NotifyWhileGrabbed and applications don't like that. */
1640     if (!data->sendtodir.inter.any.interactive ||
1641         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
1642     {
1643         if (d != screen_desktop) screen_set_desktop(d, TRUE);
1644     }
1645 }
1646
1647 void action_send_to_desktop_dir(union ActionData *data)
1648 {
1649     ObClient *c = data->sendtodir.inter.any.c;
1650     guint d;
1651
1652     if (!client_normal(c)) return;
1653
1654     d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
1655                              data->sendtodir.linear,
1656                              data->sendtodir.inter.any.interactive,
1657                              data->sendtodir.inter.final,
1658                              data->sendtodir.inter.cancel);
1659     /* only move the desktop when the action is complete. if we switch
1660        desktops during the interactive action, focus will move but with
1661        NotifyWhileGrabbed and applications don't like that. */
1662     if (!data->sendtodir.inter.any.interactive ||
1663         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
1664     {
1665         client_set_desktop(c, d, data->sendtodir.follow);
1666         if (data->sendtodir.follow && d != screen_desktop)
1667             screen_set_desktop(d, TRUE);
1668     }
1669 }
1670
1671 void action_desktop_last(union ActionData *data)
1672 {
1673     screen_set_desktop(screen_last_desktop, TRUE);
1674 }
1675
1676 void action_toggle_decorations(union ActionData *data)
1677 {
1678     ObClient *c = data->client.any.c;
1679
1680     client_action_start(data);
1681     client_set_undecorated(c, !c->undecorated);
1682     client_action_end(data);
1683 }
1684
1685 static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch,
1686                            gboolean shaded)
1687 {
1688     /* let's make x and y client relative instead of screen relative */
1689     x = x - cx;
1690     y = ch - (y - cy); /* y is inverted, 0 is at the bottom of the window */
1691
1692 #define X x*ch/cw
1693 #define A -4*X + 7*ch/3
1694 #define B  4*X -15*ch/9
1695 #define C -X/4 + 2*ch/3
1696 #define D  X/4 + 5*ch/12
1697 #define E  X/4 +   ch/3
1698 #define F -X/4 + 7*ch/12
1699 #define G  4*X - 4*ch/3
1700 #define H -4*X + 8*ch/3
1701 #define a (y > 5*ch/9)
1702 #define b (x < 4*cw/9)
1703 #define c (x > 5*cw/9)
1704 #define d (y < 4*ch/9)
1705
1706     /*
1707       Each of these defines (except X which is just there for fun), represents
1708       the equation of a line. The lines they represent are shown in the diagram
1709       below. Checking y against these lines, we are able to choose a region
1710       of the window as shown.
1711
1712       +---------------------A-------|-------|-------B---------------------+
1713       |                     |A                     B|                     |
1714       |                     |A      |       |      B|                     |
1715       |                     | A                   B |                     |
1716       |                     | A     |       |     B |                     |
1717       |                     |  A                 B  |                     |
1718       |                     |  A    |       |    B  |                     |
1719       |        northwest    |   A     north     B   |   northeast         |
1720       |                     |   A   |       |   B   |                     |
1721       |                     |    A             B    |                     |
1722       C---------------------+----A--+-------+--B----+---------------------D
1723       |CCCCCCC              |     A           B     |              DDDDDDD|
1724       |       CCCCCCCC      |     A |       | B     |      DDDDDDDD       |
1725       |               CCCCCCC      A         B      DDDDDDD               |
1726       - - - - - - - - - - - +CCCCCCC+aaaaaaa+DDDDDDD+ - - - - - - - - - - - -
1727       |                     |       b       c       |                     | sh
1728       |             west    |       b  move c       |   east              | ad
1729       |                     |       b       c       |                     | ed
1730       - - - - - - - - - - - +EEEEEEE+ddddddd+FFFFFFF+- - - - - - - - - - -  -
1731       |               EEEEEEE      G         H      FFFFFFF               |
1732       |       EEEEEEEE      |     G |       | H     |      FFFFFFFF       |
1733       |EEEEEEE              |     G           H     |              FFFFFFF|
1734       E---------------------+----G--+-------+--H----+---------------------F
1735       |                     |    G             H    |                     |
1736       |                     |   G   |       |   H   |                     |
1737       |        southwest    |   G     south     H   |   southeast         |
1738       |                     |  G    |       |    H  |                     |
1739       |                     |  G                 H  |                     |
1740       |                     | G     |       |     H |                     |
1741       |                     | G                   H |                     |
1742       |                     |G      |       |      H|                     |
1743       |                     |G                     H|                     |
1744       +---------------------G-------|-------|-------H---------------------+
1745     */
1746
1747     if (shaded) {
1748         /* for shaded windows, you can only resize west/east and move */
1749         if (b)
1750             return prop_atoms.net_wm_moveresize_size_left;
1751         if (c)
1752             return prop_atoms.net_wm_moveresize_size_right;
1753         return prop_atoms.net_wm_moveresize_move;
1754     }
1755
1756     if (y < A && y >= C)
1757         return prop_atoms.net_wm_moveresize_size_topleft;
1758     else if (y >= A && y >= B && a)
1759         return prop_atoms.net_wm_moveresize_size_top;
1760     else if (y < B && y >= D)
1761         return prop_atoms.net_wm_moveresize_size_topright;
1762     else if (y < C && y >= E && b)
1763         return prop_atoms.net_wm_moveresize_size_left;
1764     else if (y < D && y >= F && c)
1765         return prop_atoms.net_wm_moveresize_size_right;
1766     else if (y < E && y >= G)
1767         return prop_atoms.net_wm_moveresize_size_bottomleft;
1768     else if (y < G && y < H && d)
1769         return prop_atoms.net_wm_moveresize_size_bottom;
1770     else if (y >= H && y < F)
1771         return prop_atoms.net_wm_moveresize_size_bottomright;
1772     else
1773         return prop_atoms.net_wm_moveresize_move;
1774
1775 #undef X
1776 #undef A
1777 #undef B
1778 #undef C
1779 #undef D
1780 #undef E
1781 #undef F
1782 #undef G
1783 #undef H
1784 #undef a
1785 #undef b
1786 #undef c
1787 #undef d
1788 }
1789
1790 void action_move(union ActionData *data)
1791 {
1792     ObClient *c = data->moveresize.any.c;
1793     guint32 corner;
1794
1795     if (data->moveresize.keyboard)
1796         corner = prop_atoms.net_wm_moveresize_move_keyboard;
1797     else
1798         corner = prop_atoms.net_wm_moveresize_move;
1799
1800     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1801 }
1802
1803 void action_resize(union ActionData *data)
1804 {
1805     ObClient *c = data->moveresize.any.c;
1806     guint32 corner;
1807
1808     if (data->moveresize.keyboard)
1809         corner = prop_atoms.net_wm_moveresize_size_keyboard;
1810     else if (data->moveresize.corner)
1811         corner = data->moveresize.corner; /* it was specified in the binding */
1812     else
1813         corner = pick_corner(data->any.x, data->any.y,
1814                              c->frame->area.x, c->frame->area.y,
1815                              /* use the client size because the frame
1816                                 can be differently sized (shaded
1817                                 windows) and we want this based on the
1818                                 clients size */
1819                              c->area.width + c->frame->size.left +
1820                              c->frame->size.right,
1821                              c->area.height + c->frame->size.top +
1822                              c->frame->size.bottom, c->shaded);
1823
1824     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1825 }
1826
1827 void action_reconfigure(union ActionData *data)
1828 {
1829     ob_reconfigure();
1830 }
1831
1832 void action_restart(union ActionData *data)
1833 {
1834     ob_restart_other(data->execute.path);
1835 }
1836
1837 void action_exit(union ActionData *data)
1838 {
1839     ob_exit(0);
1840 }
1841
1842 void action_showmenu(union ActionData *data)
1843 {
1844     if (data->showmenu.name) {
1845         menu_show(data->showmenu.name, data->any.x, data->any.y,
1846                   data->any.button, data->showmenu.any.c);
1847     }
1848 }
1849
1850 void action_cycle_windows(union ActionData *data)
1851 {
1852     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1853        on us */
1854     event_halt_focus_delay();
1855
1856     focus_cycle(data->cycle.forward,
1857                 data->cycle.all_desktops,
1858                 data->cycle.dock_windows,
1859                 data->cycle.desktop_windows,
1860                 data->cycle.linear, data->any.interactive,
1861                 data->cycle.dialog,
1862                 data->cycle.inter.final, data->cycle.inter.cancel);
1863 }
1864
1865 void action_directional_focus(union ActionData *data)
1866 {
1867     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1868        on us */
1869     event_halt_focus_delay();
1870
1871     focus_directional_cycle(data->interdiraction.direction,
1872                             data->interdiraction.dock_windows,
1873                             data->interdiraction.desktop_windows,
1874                             data->any.interactive,
1875                             data->interdiraction.dialog,
1876                             data->interdiraction.inter.final,
1877                             data->interdiraction.inter.cancel);
1878 }
1879
1880 void action_movetoedge(union ActionData *data)
1881 {
1882     gint x, y;
1883     ObClient *c = data->diraction.any.c;
1884
1885     x = c->frame->area.x;
1886     y = c->frame->area.y;
1887     
1888     switch(data->diraction.direction) {
1889     case OB_DIRECTION_NORTH:
1890         y = client_directional_edge_search(c, OB_DIRECTION_NORTH,
1891                                            data->diraction.hang)
1892             - (data->diraction.hang ? c->frame->area.height : 0);
1893         break;
1894     case OB_DIRECTION_WEST:
1895         x = client_directional_edge_search(c, OB_DIRECTION_WEST,
1896                                            data->diraction.hang)
1897             - (data->diraction.hang ? c->frame->area.width : 0);
1898         break;
1899     case OB_DIRECTION_SOUTH:
1900         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH,
1901                                            data->diraction.hang)
1902             - (data->diraction.hang ? 0 : c->frame->area.height);
1903         break;
1904     case OB_DIRECTION_EAST:
1905         x = client_directional_edge_search(c, OB_DIRECTION_EAST,
1906                                            data->diraction.hang)
1907             - (data->diraction.hang ? 0 : c->frame->area.width);
1908         break;
1909     default:
1910         g_assert_not_reached();
1911     }
1912     frame_frame_gravity(c->frame, &x, &y, c->area.width, c->area.height);
1913     client_action_start(data);
1914     client_move(c, x, y);
1915     client_action_end(data);
1916 }
1917
1918 void action_growtoedge(union ActionData *data)
1919 {
1920     gint x, y, width, height, dest;
1921     ObClient *c = data->diraction.any.c;
1922     Rect *a;
1923
1924     a = screen_area(c->desktop);
1925     x = c->frame->area.x;
1926     y = c->frame->area.y;
1927     /* get the unshaded frame's dimensions..if it is shaded */
1928     width = c->area.width + c->frame->size.left + c->frame->size.right;
1929     height = c->area.height + c->frame->size.top + c->frame->size.bottom;
1930
1931     switch(data->diraction.direction) {
1932     case OB_DIRECTION_NORTH:
1933         if (c->shaded) break; /* don't allow vertical resize if shaded */
1934
1935         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH, FALSE);
1936         if (a->y == y)
1937             height = height / 2;
1938         else {
1939             height = c->frame->area.y + height - dest;
1940             y = dest;
1941         }
1942         break;
1943     case OB_DIRECTION_WEST:
1944         dest = client_directional_edge_search(c, OB_DIRECTION_WEST, FALSE);
1945         if (a->x == x)
1946             width = width / 2;
1947         else {
1948             width = c->frame->area.x + width - dest;
1949             x = dest;
1950         }
1951         break;
1952     case OB_DIRECTION_SOUTH:
1953         if (c->shaded) break; /* don't allow vertical resize if shaded */
1954
1955         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH, FALSE);
1956         if (a->y + a->height == y + c->frame->area.height) {
1957             height = c->frame->area.height / 2;
1958             y = a->y + a->height - height;
1959         } else
1960             height = dest - c->frame->area.y;
1961         y += (height - c->frame->area.height) % c->size_inc.height;
1962         height -= (height - c->frame->area.height) % c->size_inc.height;
1963         break;
1964     case OB_DIRECTION_EAST:
1965         dest = client_directional_edge_search(c, OB_DIRECTION_EAST, FALSE);
1966         if (a->x + a->width == x + c->frame->area.width) {
1967             width = c->frame->area.width / 2;
1968             x = a->x + a->width - width;
1969         } else
1970             width = dest - c->frame->area.x;
1971         x += (width - c->frame->area.width) % c->size_inc.width;
1972         width -= (width - c->frame->area.width) % c->size_inc.width;
1973         break;
1974     default:
1975         g_assert_not_reached();
1976     }
1977     width -= c->frame->size.left + c->frame->size.right;
1978     height -= c->frame->size.top + c->frame->size.bottom;
1979     frame_frame_gravity(c->frame, &x, &y, width, height);
1980     client_action_start(data);
1981     client_move_resize(c, x, y, width, height);
1982     client_action_end(data);
1983 }
1984
1985 void action_send_to_layer(union ActionData *data)
1986 {
1987     client_set_layer(data->layer.any.c, data->layer.layer);
1988 }
1989
1990 void action_toggle_layer(union ActionData *data)
1991 {
1992     ObClient *c = data->layer.any.c;
1993
1994     client_action_start(data);
1995     if (data->layer.layer < 0)
1996         client_set_layer(c, c->below ? 0 : -1);
1997     else if (data->layer.layer > 0)
1998         client_set_layer(c, c->above ? 0 : 1);
1999     client_action_end(data);
2000 }
2001
2002 void action_toggle_dockautohide(union ActionData *data)
2003 {
2004     config_dock_hide = !config_dock_hide;
2005     dock_configure();
2006 }
2007
2008 void action_toggle_show_desktop(union ActionData *data)
2009 {
2010     screen_show_desktop(!screen_showing_desktop, NULL);
2011 }
2012
2013 void action_show_desktop(union ActionData *data)
2014 {
2015     screen_show_desktop(TRUE, NULL);
2016 }
2017
2018 void action_unshow_desktop(union ActionData *data)
2019 {
2020     screen_show_desktop(FALSE, NULL);
2021 }
2022
2023 void action_break_chroot(union ActionData *data)
2024 {
2025     /* break out of one chroot */
2026     keyboard_reset_chains(1);
2027 }