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