make focuslast only apply to switching desktops, like 3.3.1
[dana/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 "focus_cycle.h"
24 #include "moveresize.h"
25 #include "menu.h"
26 #include "prop.h"
27 #include "stacking.h"
28 #include "screen.h"
29 #include "action.h"
30 #include "openbox.h"
31 #include "grab.h"
32 #include "keyboard.h"
33 #include "event.h"
34 #include "dock.h"
35 #include "config.h"
36 #include "mainloop.h"
37 #include "startupnotify.h"
38 #include "gettext.h"
39
40 #include <glib.h>
41
42 static void client_action_start(union ActionData *data)
43 {
44 }
45
46 static void client_action_end(union ActionData *data)
47 {
48     if (config_focus_follow)
49         if (data->any.context != OB_FRAME_CONTEXT_CLIENT) {
50             if (!data->any.button && data->any.c) {
51                 event_ignore_all_queued_enters();
52             } else {
53                 ObClient *c;
54
55                 /* usually this is sorta redundant, but with a press action
56                    that moves windows our from under the cursor, the enter
57                    event will come as a GrabNotify which is ignored, so this
58                    makes a fake enter event
59                 */
60                 if ((c = client_under_pointer()) && c != data->any.c)
61                     event_enter_client(c);
62             }
63         }
64 }
65
66 typedef struct
67 {
68     const gchar *name;
69     void (*func)(union ActionData *);
70     void (*setup)(ObAction **, ObUserAction uact);
71 } ActionString;
72
73 static ObAction *action_new(void (*func)(union ActionData *data))
74 {
75     ObAction *a = g_new0(ObAction, 1);
76     a->ref = 1;
77     a->func = func;
78
79     return a;
80 }
81
82 void action_ref(ObAction *a)
83 {
84     ++a->ref;
85 }
86
87 void action_unref(ObAction *a)
88 {
89     if (a == NULL) return;
90
91     if (--a->ref > 0) return;
92
93     /* deal with pointers */
94     if (a->func == action_execute || a->func == action_restart)
95         g_free(a->data.execute.path);
96     else if (a->func == action_debug)
97         g_free(a->data.debug.string);
98     else if (a->func == action_showmenu)
99         g_free(a->data.showmenu.name);
100
101     g_free(a);
102 }
103
104 ObAction* action_copy(const ObAction *src)
105 {
106     ObAction *a = action_new(src->func);
107
108     a->data = src->data;
109
110     /* deal with pointers */
111     if (a->func == action_execute || a->func == action_restart)
112         a->data.execute.path = g_strdup(a->data.execute.path);
113     else if (a->func == action_debug)
114         a->data.debug.string = g_strdup(a->data.debug.string);
115     else if (a->func == action_showmenu)
116         a->data.showmenu.name = g_strdup(a->data.showmenu.name);
117
118     return a;
119 }
120
121 void setup_action_directional_focus_north(ObAction **a, ObUserAction uact)
122 {
123     (*a)->data.interdiraction.inter.any.interactive = TRUE;
124     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTH;
125     (*a)->data.interdiraction.dialog = TRUE;
126     (*a)->data.interdiraction.dock_windows = FALSE;
127     (*a)->data.interdiraction.desktop_windows = FALSE;
128 }
129
130 void setup_action_directional_focus_east(ObAction **a, ObUserAction uact)
131 {
132     (*a)->data.interdiraction.inter.any.interactive = TRUE;
133     (*a)->data.interdiraction.direction = OB_DIRECTION_EAST;
134     (*a)->data.interdiraction.dialog = TRUE;
135     (*a)->data.interdiraction.dock_windows = FALSE;
136     (*a)->data.interdiraction.desktop_windows = FALSE;
137 }
138
139 void setup_action_directional_focus_south(ObAction **a, ObUserAction uact)
140 {
141     (*a)->data.interdiraction.inter.any.interactive = TRUE;
142     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTH;
143     (*a)->data.interdiraction.dialog = TRUE;
144     (*a)->data.interdiraction.dock_windows = FALSE;
145     (*a)->data.interdiraction.desktop_windows = FALSE;
146 }
147
148 void setup_action_directional_focus_west(ObAction **a, ObUserAction uact)
149 {
150     (*a)->data.interdiraction.inter.any.interactive = TRUE;
151     (*a)->data.interdiraction.direction = OB_DIRECTION_WEST;
152     (*a)->data.interdiraction.dialog = TRUE;
153     (*a)->data.interdiraction.dock_windows = FALSE;
154     (*a)->data.interdiraction.desktop_windows = FALSE;
155 }
156
157 void setup_action_directional_focus_northeast(ObAction **a, ObUserAction uact)
158 {
159     (*a)->data.interdiraction.inter.any.interactive = TRUE;
160     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHEAST;
161     (*a)->data.interdiraction.dialog = TRUE;
162     (*a)->data.interdiraction.dock_windows = FALSE;
163     (*a)->data.interdiraction.desktop_windows = FALSE;
164 }
165
166 void setup_action_directional_focus_southeast(ObAction **a, ObUserAction uact)
167 {
168     (*a)->data.interdiraction.inter.any.interactive = TRUE;
169     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHEAST;
170     (*a)->data.interdiraction.dialog = TRUE;
171     (*a)->data.interdiraction.dock_windows = FALSE;
172     (*a)->data.interdiraction.desktop_windows = FALSE;
173 }
174
175 void setup_action_directional_focus_southwest(ObAction **a, ObUserAction uact)
176 {
177     (*a)->data.interdiraction.inter.any.interactive = TRUE;
178     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHWEST;
179     (*a)->data.interdiraction.dialog = TRUE;
180     (*a)->data.interdiraction.dock_windows = FALSE;
181     (*a)->data.interdiraction.desktop_windows = FALSE;
182 }
183
184 void setup_action_directional_focus_northwest(ObAction **a, ObUserAction uact)
185 {
186     (*a)->data.interdiraction.inter.any.interactive = TRUE;
187     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHWEST;
188     (*a)->data.interdiraction.dialog = TRUE;
189     (*a)->data.interdiraction.dock_windows = FALSE;
190     (*a)->data.interdiraction.desktop_windows = FALSE;
191 }
192
193 void setup_action_send_to_desktop(ObAction **a, ObUserAction uact)
194 {
195     (*a)->data.sendto.any.client_action = OB_CLIENT_ACTION_ALWAYS;
196     (*a)->data.sendto.follow = TRUE;
197 }
198
199 void setup_action_send_to_desktop_prev(ObAction **a, ObUserAction uact)
200 {
201     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
202     (*a)->data.sendtodir.inter.any.interactive = TRUE;
203     (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
204     (*a)->data.sendtodir.linear = TRUE;
205     (*a)->data.sendtodir.wrap = TRUE;
206     (*a)->data.sendtodir.follow = TRUE;
207 }
208
209 void setup_action_send_to_desktop_next(ObAction **a, ObUserAction uact)
210 {
211     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
212     (*a)->data.sendtodir.inter.any.interactive = TRUE;
213     (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
214     (*a)->data.sendtodir.linear = TRUE;
215     (*a)->data.sendtodir.wrap = TRUE;
216     (*a)->data.sendtodir.follow = TRUE;
217 }
218
219 void setup_action_send_to_desktop_left(ObAction **a, ObUserAction uact)
220 {
221     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
222     (*a)->data.sendtodir.inter.any.interactive = TRUE;
223     (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
224     (*a)->data.sendtodir.linear = FALSE;
225     (*a)->data.sendtodir.wrap = TRUE;
226     (*a)->data.sendtodir.follow = TRUE;
227 }
228
229 void setup_action_send_to_desktop_right(ObAction **a, ObUserAction uact)
230 {
231     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
232     (*a)->data.sendtodir.inter.any.interactive = TRUE;
233     (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
234     (*a)->data.sendtodir.linear = FALSE;
235     (*a)->data.sendtodir.wrap = TRUE;
236     (*a)->data.sendtodir.follow = TRUE;
237 }
238
239 void setup_action_send_to_desktop_up(ObAction **a, ObUserAction uact)
240 {
241     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
242     (*a)->data.sendtodir.inter.any.interactive = TRUE;
243     (*a)->data.sendtodir.dir = OB_DIRECTION_NORTH;
244     (*a)->data.sendtodir.linear = FALSE;
245     (*a)->data.sendtodir.wrap = TRUE;
246     (*a)->data.sendtodir.follow = TRUE;
247 }
248
249 void setup_action_send_to_desktop_down(ObAction **a, ObUserAction uact)
250 {
251     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
252     (*a)->data.sendtodir.inter.any.interactive = TRUE;
253     (*a)->data.sendtodir.dir = OB_DIRECTION_SOUTH;
254     (*a)->data.sendtodir.linear = FALSE;
255     (*a)->data.sendtodir.wrap = TRUE;
256     (*a)->data.sendtodir.follow = TRUE;
257 }
258
259 void setup_action_desktop(ObAction **a, ObUserAction uact)
260 {
261 /*
262     (*a)->data.desktop.inter.any.interactive = FALSE;
263 */
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 /*
1017                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1018                     act->data.desktop.inter.any.interactive =
1019                         parse_bool(doc, n);
1020 */
1021            } else if (act->func == action_send_to_desktop) {
1022                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
1023                     act->data.sendto.desk = parse_int(doc, n);
1024                 if (act->data.sendto.desk > 0) act->data.sendto.desk--;
1025                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
1026                     act->data.sendto.follow = parse_bool(doc, n);
1027             } else if (act->func == action_desktop_dir) {
1028                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
1029                     act->data.desktopdir.wrap = parse_bool(doc, n); 
1030                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1031                     act->data.desktopdir.inter.any.interactive =
1032                         parse_bool(doc, n);
1033             } else if (act->func == action_send_to_desktop_dir) {
1034                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
1035                     act->data.sendtodir.wrap = parse_bool(doc, n);
1036                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
1037                     act->data.sendtodir.follow = parse_bool(doc, n);
1038                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1039                     act->data.sendtodir.inter.any.interactive =
1040                         parse_bool(doc, n);
1041             } else if (act->func == action_activate) {
1042                 if ((n = parse_find_node("here", node->xmlChildrenNode)))
1043                     act->data.activate.here = parse_bool(doc, n);
1044             } else if (act->func == action_cycle_windows) {
1045                 if ((n = parse_find_node("linear", node->xmlChildrenNode)))
1046                     act->data.cycle.linear = parse_bool(doc, n);
1047                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1048                     act->data.cycle.dialog = parse_bool(doc, n);
1049                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
1050                     act->data.cycle.dock_windows = parse_bool(doc, n);
1051                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
1052                     act->data.cycle.desktop_windows = parse_bool(doc, n);
1053                 if ((n = parse_find_node("allDesktops",
1054                                          node->xmlChildrenNode)))
1055                     act->data.cycle.all_desktops = parse_bool(doc, n);
1056             } else if (act->func == action_directional_focus) {
1057                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1058                     act->data.interdiraction.dialog = parse_bool(doc, n);
1059                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
1060                     act->data.interdiraction.dock_windows = parse_bool(doc, n);
1061                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
1062                     act->data.interdiraction.desktop_windows =
1063                         parse_bool(doc, n);
1064             } else if (act->func == action_resize) {
1065                 if ((n = parse_find_node("edge", node->xmlChildrenNode))) {
1066                     gchar *s = parse_string(doc, n);
1067                     if (!g_ascii_strcasecmp(s, "top"))
1068                         act->data.moveresize.corner =
1069                             prop_atoms.net_wm_moveresize_size_top;
1070                     else if (!g_ascii_strcasecmp(s, "bottom"))
1071                         act->data.moveresize.corner =
1072                             prop_atoms.net_wm_moveresize_size_bottom;
1073                     else if (!g_ascii_strcasecmp(s, "left"))
1074                         act->data.moveresize.corner =
1075                             prop_atoms.net_wm_moveresize_size_left;
1076                     else if (!g_ascii_strcasecmp(s, "right"))
1077                         act->data.moveresize.corner =
1078                             prop_atoms.net_wm_moveresize_size_right;
1079                     else if (!g_ascii_strcasecmp(s, "topleft"))
1080                         act->data.moveresize.corner =
1081                             prop_atoms.net_wm_moveresize_size_topleft;
1082                     else if (!g_ascii_strcasecmp(s, "topright"))
1083                         act->data.moveresize.corner =
1084                             prop_atoms.net_wm_moveresize_size_topright;
1085                     else if (!g_ascii_strcasecmp(s, "bottomleft"))
1086                         act->data.moveresize.corner =
1087                             prop_atoms.net_wm_moveresize_size_bottomleft;
1088                     else if (!g_ascii_strcasecmp(s, "bottomright"))
1089                         act->data.moveresize.corner =
1090                             prop_atoms.net_wm_moveresize_size_bottomright;
1091                     g_free(s);
1092                 }
1093             } else if (act->func == action_raise ||
1094                        act->func == action_lower ||
1095                        act->func == action_raiselower ||
1096                        act->func == action_shadelower ||
1097                        act->func == action_unshaderaise) {
1098             }
1099             INTERACTIVE_LIMIT(act, uact);
1100         }
1101         g_free(actname);
1102     }
1103     return act;
1104 }
1105
1106 void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
1107                      guint state, guint button, gint x, gint y, Time time,
1108                      gboolean cancel, gboolean done)
1109 {
1110     GSList *it;
1111     ObAction *a;
1112
1113     if (!acts)
1114         return;
1115
1116     if (x < 0 && y < 0)
1117         screen_pointer_pos(&x, &y);
1118
1119     for (it = acts; it; it = g_slist_next(it)) {
1120         a = it->data;
1121
1122         if (!(a->data.any.client_action == OB_CLIENT_ACTION_ALWAYS && !c)) {
1123             a->data.any.c = a->data.any.client_action ? c : NULL;
1124             a->data.any.context = context;
1125             a->data.any.x = x;
1126             a->data.any.y = y;
1127
1128             a->data.any.button = button;
1129
1130             a->data.any.time = time;
1131
1132             if (a->data.any.interactive) {
1133                 a->data.inter.cancel = cancel;
1134                 a->data.inter.final = done;
1135                 if (!(cancel || done))
1136                     if (!keyboard_interactive_grab(state, a->data.any.c, a))
1137                         continue;
1138             }
1139
1140             /* XXX UGLY HACK race with motion event starting a move and the
1141                button release gettnig processed first. answer: don't queue
1142                moveresize starts. UGLY HACK XXX
1143
1144                XXX ALSO don't queue showmenu events, because on button press
1145                events we need to know if a mouse grab is going to take place,
1146                and set the button to 0, so that later motion events don't think
1147                that a drag is going on. since showmenu grabs the pointer..
1148             */
1149             if (a->data.any.interactive || a->func == action_move ||
1150                 a->func == action_resize || a->func == action_showmenu)
1151             {
1152                 /* interactive actions are not queued */
1153                 a->func(&a->data);
1154             } else if (a->func == action_focus ||
1155                        a->func == action_activate ||
1156                        a->func == action_showmenu)
1157             {
1158                 /* XXX MORE UGLY HACK
1159                    actions from clicks on client windows are NOT queued.
1160                    this solves the mysterious click-and-drag-doesnt-work
1161                    problem. it was because the window gets focused and stuff
1162                    after the button event has already been passed through. i
1163                    dont really know why it should care but it does and it makes
1164                    a difference.
1165
1166                    however this very bogus ! !
1167                    we want to send the button press to the window BEFORE
1168                    we do the action because the action might move the windows
1169                    (eg change desktops) and then the button press ends up on
1170                    the completely wrong window !
1171                    so, this is just for that bug, and it will only NOT queue it
1172                    if it is a focusing action that can be used with the mouse
1173                    pointer. ugh.
1174
1175                    also with the menus, there is a race going on. if the
1176                    desktop wants to pop up a menu, and we do too, we send them
1177                    the button before we pop up the menu, so they pop up their
1178                    menu first. but not always. if we pop up our menu before
1179                    sending them the button press, then the result is
1180                    deterministic. yay.
1181
1182                    XXX further more. focus actions are not queued at all,
1183                    because if you bind focus->showmenu, the menu will get
1184                    hidden to do the focusing
1185                 */
1186                 a->func(&a->data);
1187             } else
1188                 ob_main_loop_queue_action(ob_main_loop, a);
1189         }
1190     }
1191 }
1192
1193 void action_run_string(const gchar *name, struct _ObClient *c, Time time)
1194 {
1195     ObAction *a;
1196     GSList *l;
1197
1198     a = action_from_string(name, OB_USER_ACTION_NONE);
1199     g_assert(a);
1200
1201     l = g_slist_append(NULL, a);
1202
1203     action_run(l, c, 0, time);
1204 }
1205
1206 void action_debug(union ActionData *data)
1207 {
1208     if (data->debug.string)
1209         g_print("%s\n", data->debug.string);
1210 }
1211
1212 void action_execute(union ActionData *data)
1213 {
1214     GError *e = NULL;
1215     gchar *cmd, **argv = 0;
1216     if (data->execute.path) {
1217         cmd = g_filename_from_utf8(data->execute.path, -1, NULL, NULL, NULL);
1218         if (cmd) {
1219             /* If there is a keyboard grab going on then we need to cancel
1220                it so the application can grab things */
1221             event_cancel_all_key_grabs();
1222
1223             if (!g_shell_parse_argv (cmd, NULL, &argv, &e)) {
1224                 g_message(_("Failed to execute '%s': %s"),
1225                           cmd, e->message);
1226                 g_error_free(e);
1227             } else if (data->execute.startupnotify) {
1228                 gchar *program;
1229                 
1230                 program = g_path_get_basename(argv[0]);
1231                 /* sets up the environment */
1232                 sn_setup_spawn_environment(program,
1233                                            data->execute.name,
1234                                            data->execute.icon_name,
1235                                            /* launch it on the current
1236                                               desktop */
1237                                            screen_desktop,
1238                                            data->execute.any.time);
1239                 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH |
1240                                    G_SPAWN_DO_NOT_REAP_CHILD,
1241                                    NULL, NULL, NULL, &e)) {
1242                     g_message(_("Failed to execute '%s': %s"),
1243                               cmd, e->message);
1244                     g_error_free(e);
1245                     sn_spawn_cancel();
1246                 }
1247                 unsetenv("DESKTOP_STARTUP_ID");
1248                 g_free(program);
1249                 g_strfreev(argv);
1250             } else {
1251                 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH |
1252                                    G_SPAWN_DO_NOT_REAP_CHILD,
1253                                    NULL, NULL, NULL, &e))
1254                 {
1255                     g_message(_("Failed to execute '%s': %s"),
1256                               cmd, e->message);
1257                     g_error_free(e);
1258                 }
1259                 g_strfreev(argv);
1260             }
1261             g_free(cmd);
1262         } else {
1263             g_message(_("Failed to convert the path '%s' from utf8"),
1264                       data->execute.path);
1265         }
1266     }
1267 }
1268
1269 void action_activate(union ActionData *data)
1270 {
1271     if (data->client.any.c) {
1272         if (!data->any.button || client_mouse_focusable(data->client.any.c) ||
1273             (data->any.context != OB_FRAME_CONTEXT_CLIENT &&
1274              data->any.context != OB_FRAME_CONTEXT_FRAME))
1275         {
1276             /* if using focus_delay, stop the timer now so that focus doesn't
1277                go moving on us */
1278             event_halt_focus_delay();
1279
1280             client_activate(data->activate.any.c, data->activate.here, TRUE);
1281         }
1282     } else {
1283         /* focus action on something other than a client, make keybindings
1284            work for this openbox instance, but don't focus any specific client
1285         */
1286         focus_nothing();
1287     }
1288 }
1289
1290 void action_focus(union ActionData *data)
1291 {
1292     if (data->client.any.c) {
1293         if (!data->any.button || client_mouse_focusable(data->client.any.c) ||
1294             (data->any.context != OB_FRAME_CONTEXT_CLIENT &&
1295              data->any.context != OB_FRAME_CONTEXT_FRAME))
1296         {
1297             /* if using focus_delay, stop the timer now so that focus doesn't
1298                go moving on us */
1299             event_halt_focus_delay();
1300
1301             client_focus(data->client.any.c);
1302         }
1303     } else {
1304         /* focus action on something other than a client, make keybindings
1305            work for this openbox instance, but don't focus any specific client
1306         */
1307         focus_nothing();
1308     }
1309 }
1310
1311 void action_unfocus (union ActionData *data)
1312 {
1313     if (data->client.any.c == focus_client)
1314         focus_fallback(FALSE, FALSE);
1315 }
1316
1317 void action_iconify(union ActionData *data)
1318 {
1319     client_action_start(data);
1320     client_iconify(data->client.any.c, TRUE, TRUE, FALSE);
1321     client_action_end(data);
1322 }
1323
1324 void action_focus_order_to_bottom(union ActionData *data)
1325 {
1326     focus_order_to_bottom(data->client.any.c);
1327 }
1328
1329 void action_raiselower(union ActionData *data)
1330 {
1331     ObClient *c = data->client.any.c;
1332     GList *it;
1333     gboolean raise = FALSE;
1334
1335     for (it = stacking_list; it; it = g_list_next(it)) {
1336         if (WINDOW_IS_CLIENT(it->data)) {
1337             ObClient *cit = it->data;
1338
1339             if (cit == c) break;
1340             if (client_normal(cit) == client_normal(c) &&
1341                 cit->layer == c->layer &&
1342                 cit->frame->visible &&
1343                 !client_search_transient(c, cit))
1344             {
1345                 if (RECT_INTERSECTS_RECT(cit->frame->area, c->frame->area)) {
1346                     raise = TRUE;
1347                     break;
1348                 }
1349             }
1350         }
1351     }
1352
1353     if (raise)
1354         action_raise(data);
1355     else
1356         action_lower(data);
1357 }
1358
1359 void action_raise(union ActionData *data)
1360 {
1361     client_action_start(data);
1362     stacking_raise(CLIENT_AS_WINDOW(data->client.any.c));
1363     client_action_end(data);
1364 }
1365
1366 void action_unshaderaise(union ActionData *data)
1367 {
1368     if (data->client.any.c->shaded)
1369         action_unshade(data);
1370     else
1371         action_raise(data);
1372 }
1373
1374 void action_shadelower(union ActionData *data)
1375 {
1376     if (data->client.any.c->shaded)
1377         action_lower(data);
1378     else
1379         action_shade(data);
1380 }
1381
1382 void action_lower(union ActionData *data)
1383 {
1384     client_action_start(data);
1385     stacking_lower(CLIENT_AS_WINDOW(data->client.any.c));
1386     client_action_end(data);
1387 }
1388
1389 void action_close(union ActionData *data)
1390 {
1391     client_close(data->client.any.c);
1392 }
1393
1394 void action_kill(union ActionData *data)
1395 {
1396     client_kill(data->client.any.c);
1397 }
1398
1399 void action_shade(union ActionData *data)
1400 {
1401     client_action_start(data);
1402     client_shade(data->client.any.c, TRUE);
1403     client_action_end(data);
1404 }
1405
1406 void action_unshade(union ActionData *data)
1407 {
1408     client_action_start(data);
1409     client_shade(data->client.any.c, FALSE);
1410     client_action_end(data);
1411 }
1412
1413 void action_toggle_shade(union ActionData *data)
1414 {
1415     client_action_start(data);
1416     client_shade(data->client.any.c, !data->client.any.c->shaded);
1417     client_action_end(data);
1418 }
1419
1420 void action_toggle_omnipresent(union ActionData *data)
1421
1422     client_set_desktop(data->client.any.c,
1423                        data->client.any.c->desktop == DESKTOP_ALL ?
1424                        screen_desktop : DESKTOP_ALL, FALSE);
1425 }
1426
1427 void action_move_relative_horz(union ActionData *data)
1428 {
1429     ObClient *c = data->relative.any.c;
1430     client_action_start(data);
1431     client_move(c, c->area.x + data->relative.deltax, c->area.y);
1432     client_action_end(data);
1433 }
1434
1435 void action_move_relative_vert(union ActionData *data)
1436 {
1437     ObClient *c = data->relative.any.c;
1438     client_action_start(data);
1439     client_move(c, c->area.x, c->area.y + data->relative.deltax);
1440     client_action_end(data);
1441 }
1442
1443 void action_move_to_center(union ActionData *data)
1444 {
1445     ObClient *c = data->client.any.c;
1446     Rect *area;
1447     area = screen_area_monitor(c->desktop, 0);
1448     client_action_start(data);
1449     client_move(c, area->width / 2 - c->area.width / 2,
1450                 area->height / 2 - c->area.height / 2);
1451     client_action_end(data);
1452 }
1453
1454 void action_resize_relative_horz(union ActionData *data)
1455 {
1456     ObClient *c = data->relative.any.c;
1457     client_action_start(data);
1458     client_resize(c,
1459                   c->area.width + data->relative.deltax * c->size_inc.width,
1460                   c->area.height);
1461     client_action_end(data);
1462 }
1463
1464 void action_resize_relative_vert(union ActionData *data)
1465 {
1466     ObClient *c = data->relative.any.c;
1467     if (!c->shaded) {
1468         client_action_start(data);
1469         client_resize(c, c->area.width, c->area.height +
1470                       data->relative.deltax * c->size_inc.height);
1471         client_action_end(data);
1472     }
1473 }
1474
1475 void action_move_relative(union ActionData *data)
1476 {
1477     ObClient *c = data->relative.any.c;
1478     client_action_start(data);
1479     client_move(c, c->area.x + data->relative.deltax, c->area.y +
1480                 data->relative.deltay);
1481     client_action_end(data);
1482 }
1483
1484 void action_resize_relative(union ActionData *data)
1485 {
1486     ObClient *c = data->relative.any.c;
1487     gint x, y, ow, w, oh, h, lw, lh;
1488
1489     client_action_start(data);
1490
1491     x = c->area.x;
1492     y = c->area.y;
1493     ow = c->area.width;
1494     w = ow + data->relative.deltax * c->size_inc.width
1495         + data->relative.deltaxl * c->size_inc.width;
1496     oh = c->area.height;
1497     h = oh + data->relative.deltay * c->size_inc.height
1498         + data->relative.deltayu * c->size_inc.height;
1499     
1500     client_try_configure(c, &x, &y, &w, &h, &lw, &lh, TRUE);
1501     client_move_resize(c, x + (ow - w), y + (oh - h), w, h);
1502     client_action_end(data);
1503 }
1504
1505 void action_maximize_full(union ActionData *data)
1506 {
1507     client_action_start(data);
1508     client_maximize(data->client.any.c, TRUE, 0);
1509     client_action_end(data);
1510 }
1511
1512 void action_unmaximize_full(union ActionData *data)
1513 {
1514     client_action_start(data);
1515     client_maximize(data->client.any.c, FALSE, 0);
1516     client_action_end(data);
1517 }
1518
1519 void action_toggle_maximize_full(union ActionData *data)
1520 {
1521     client_action_start(data);
1522     client_maximize(data->client.any.c,
1523                     !(data->client.any.c->max_horz ||
1524                       data->client.any.c->max_vert),
1525                     0);
1526     client_action_end(data);
1527 }
1528
1529 void action_maximize_horz(union ActionData *data)
1530 {
1531     client_action_start(data);
1532     client_maximize(data->client.any.c, TRUE, 1);
1533     client_action_end(data);
1534 }
1535
1536 void action_unmaximize_horz(union ActionData *data)
1537 {
1538     client_action_start(data);
1539     client_maximize(data->client.any.c, FALSE, 1);
1540     client_action_end(data);
1541 }
1542
1543 void action_toggle_maximize_horz(union ActionData *data)
1544 {
1545     client_action_start(data);
1546     client_maximize(data->client.any.c,
1547                     !data->client.any.c->max_horz, 1);
1548     client_action_end(data);
1549 }
1550
1551 void action_maximize_vert(union ActionData *data)
1552 {
1553     client_action_start(data);
1554     client_maximize(data->client.any.c, TRUE, 2);
1555     client_action_end(data);
1556 }
1557
1558 void action_unmaximize_vert(union ActionData *data)
1559 {
1560     client_action_start(data);
1561     client_maximize(data->client.any.c, FALSE, 2);
1562     client_action_end(data);
1563 }
1564
1565 void action_toggle_maximize_vert(union ActionData *data)
1566 {
1567     client_action_start(data);
1568     client_maximize(data->client.any.c,
1569                     !data->client.any.c->max_vert, 2);
1570     client_action_end(data);
1571 }
1572
1573 void action_toggle_fullscreen(union ActionData *data)
1574 {
1575     client_action_start(data);
1576     client_fullscreen(data->client.any.c, !(data->client.any.c->fullscreen));
1577     client_action_end(data);
1578 }
1579
1580 void action_send_to_desktop(union ActionData *data)
1581 {
1582     ObClient *c = data->sendto.any.c;
1583
1584     if (!client_normal(c)) return;
1585
1586     if (data->sendto.desk < screen_num_desktops ||
1587         data->sendto.desk == DESKTOP_ALL) {
1588         client_set_desktop(c, data->sendto.desk, data->sendto.follow);
1589         if (data->sendto.follow && data->sendto.desk != screen_desktop)
1590             screen_set_desktop(data->sendto.desk, TRUE);
1591     }
1592 }
1593
1594 void action_desktop(union ActionData *data)
1595 {
1596     /* XXX add the interactive/dialog option back again once the dialog
1597        has been made to not use grabs */
1598     if (data->desktop.desk < screen_num_desktops ||
1599         data->desktop.desk == DESKTOP_ALL)
1600     {
1601         screen_set_desktop(data->desktop.desk, TRUE);
1602         if (data->inter.any.interactive)
1603             screen_desktop_popup(data->desktop.desk, TRUE);
1604     }
1605 }
1606
1607 void action_desktop_dir(union ActionData *data)
1608 {
1609     guint d;
1610
1611     d = screen_cycle_desktop(data->desktopdir.dir,
1612                              data->desktopdir.wrap,
1613                              data->desktopdir.linear,
1614                              data->desktopdir.inter.any.interactive,
1615                              data->desktopdir.inter.final,
1616                              data->desktopdir.inter.cancel);
1617     /* only move the desktop when the action is complete. if we switch
1618        desktops during the interactive action, focus will move but with
1619        NotifyWhileGrabbed and applications don't like that. */
1620     if (!data->sendtodir.inter.any.interactive ||
1621         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
1622     {
1623         if (d != screen_desktop)
1624             screen_set_desktop(d, TRUE);
1625     }
1626 }
1627
1628 void action_send_to_desktop_dir(union ActionData *data)
1629 {
1630     ObClient *c = data->sendtodir.inter.any.c;
1631     guint d;
1632
1633     if (!client_normal(c)) return;
1634
1635     d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
1636                              data->sendtodir.linear,
1637                              data->sendtodir.inter.any.interactive,
1638                              data->sendtodir.inter.final,
1639                              data->sendtodir.inter.cancel);
1640     /* only move the desktop when the action is complete. if we switch
1641        desktops during the interactive action, focus will move but with
1642        NotifyWhileGrabbed and applications don't like that. */
1643     if (!data->sendtodir.inter.any.interactive ||
1644         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
1645     {
1646         client_set_desktop(c, d, data->sendtodir.follow);
1647         if (data->sendtodir.follow && d != screen_desktop)
1648             screen_set_desktop(d, TRUE);
1649     }
1650 }
1651
1652 void action_desktop_last(union ActionData *data)
1653 {
1654     screen_set_desktop(screen_last_desktop, TRUE);
1655 }
1656
1657 void action_toggle_decorations(union ActionData *data)
1658 {
1659     ObClient *c = data->client.any.c;
1660
1661     client_action_start(data);
1662     client_set_undecorated(c, !c->undecorated);
1663     client_action_end(data);
1664 }
1665
1666 static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch,
1667                            gboolean shaded)
1668 {
1669     /* let's make x and y client relative instead of screen relative */
1670     x = x - cx;
1671     y = ch - (y - cy); /* y is inverted, 0 is at the bottom of the window */
1672
1673 #define X x*ch/cw
1674 #define A -4*X + 7*ch/3
1675 #define B  4*X -15*ch/9
1676 #define C -X/4 + 2*ch/3
1677 #define D  X/4 + 5*ch/12
1678 #define E  X/4 +   ch/3
1679 #define F -X/4 + 7*ch/12
1680 #define G  4*X - 4*ch/3
1681 #define H -4*X + 8*ch/3
1682 #define a (y > 5*ch/9)
1683 #define b (x < 4*cw/9)
1684 #define c (x > 5*cw/9)
1685 #define d (y < 4*ch/9)
1686
1687     /*
1688       Each of these defines (except X which is just there for fun), represents
1689       the equation of a line. The lines they represent are shown in the diagram
1690       below. Checking y against these lines, we are able to choose a region
1691       of the window as shown.
1692
1693       +---------------------A-------|-------|-------B---------------------+
1694       |                     |A                     B|                     |
1695       |                     |A      |       |      B|                     |
1696       |                     | A                   B |                     |
1697       |                     | A     |       |     B |                     |
1698       |                     |  A                 B  |                     |
1699       |                     |  A    |       |    B  |                     |
1700       |        northwest    |   A     north     B   |   northeast         |
1701       |                     |   A   |       |   B   |                     |
1702       |                     |    A             B    |                     |
1703       C---------------------+----A--+-------+--B----+---------------------D
1704       |CCCCCCC              |     A           B     |              DDDDDDD|
1705       |       CCCCCCCC      |     A |       | B     |      DDDDDDDD       |
1706       |               CCCCCCC      A         B      DDDDDDD               |
1707       - - - - - - - - - - - +CCCCCCC+aaaaaaa+DDDDDDD+ - - - - - - - - - - - -
1708       |                     |       b       c       |                     | sh
1709       |             west    |       b  move c       |   east              | ad
1710       |                     |       b       c       |                     | ed
1711       - - - - - - - - - - - +EEEEEEE+ddddddd+FFFFFFF+- - - - - - - - - - -  -
1712       |               EEEEEEE      G         H      FFFFFFF               |
1713       |       EEEEEEEE      |     G |       | H     |      FFFFFFFF       |
1714       |EEEEEEE              |     G           H     |              FFFFFFF|
1715       E---------------------+----G--+-------+--H----+---------------------F
1716       |                     |    G             H    |                     |
1717       |                     |   G   |       |   H   |                     |
1718       |        southwest    |   G     south     H   |   southeast         |
1719       |                     |  G    |       |    H  |                     |
1720       |                     |  G                 H  |                     |
1721       |                     | G     |       |     H |                     |
1722       |                     | G                   H |                     |
1723       |                     |G      |       |      H|                     |
1724       |                     |G                     H|                     |
1725       +---------------------G-------|-------|-------H---------------------+
1726     */
1727
1728     if (shaded) {
1729         /* for shaded windows, you can only resize west/east and move */
1730         if (b)
1731             return prop_atoms.net_wm_moveresize_size_left;
1732         if (c)
1733             return prop_atoms.net_wm_moveresize_size_right;
1734         return prop_atoms.net_wm_moveresize_move;
1735     }
1736
1737     if (y < A && y >= C)
1738         return prop_atoms.net_wm_moveresize_size_topleft;
1739     else if (y >= A && y >= B && a)
1740         return prop_atoms.net_wm_moveresize_size_top;
1741     else if (y < B && y >= D)
1742         return prop_atoms.net_wm_moveresize_size_topright;
1743     else if (y < C && y >= E && b)
1744         return prop_atoms.net_wm_moveresize_size_left;
1745     else if (y < D && y >= F && c)
1746         return prop_atoms.net_wm_moveresize_size_right;
1747     else if (y < E && y >= G)
1748         return prop_atoms.net_wm_moveresize_size_bottomleft;
1749     else if (y < G && y < H && d)
1750         return prop_atoms.net_wm_moveresize_size_bottom;
1751     else if (y >= H && y < F)
1752         return prop_atoms.net_wm_moveresize_size_bottomright;
1753     else
1754         return prop_atoms.net_wm_moveresize_move;
1755
1756 #undef X
1757 #undef A
1758 #undef B
1759 #undef C
1760 #undef D
1761 #undef E
1762 #undef F
1763 #undef G
1764 #undef H
1765 #undef a
1766 #undef b
1767 #undef c
1768 #undef d
1769 }
1770
1771 void action_move(union ActionData *data)
1772 {
1773     ObClient *c = data->moveresize.any.c;
1774     guint32 corner;
1775
1776     if (data->moveresize.keyboard)
1777         corner = prop_atoms.net_wm_moveresize_move_keyboard;
1778     else
1779         corner = prop_atoms.net_wm_moveresize_move;
1780
1781     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1782 }
1783
1784 void action_resize(union ActionData *data)
1785 {
1786     ObClient *c = data->moveresize.any.c;
1787     guint32 corner;
1788
1789     if (data->moveresize.keyboard)
1790         corner = prop_atoms.net_wm_moveresize_size_keyboard;
1791     else if (data->moveresize.corner)
1792         corner = data->moveresize.corner; /* it was specified in the binding */
1793     else
1794         corner = pick_corner(data->any.x, data->any.y,
1795                              c->frame->area.x, c->frame->area.y,
1796                              /* use the client size because the frame
1797                                 can be differently sized (shaded
1798                                 windows) and we want this based on the
1799                                 clients size */
1800                              c->area.width + c->frame->size.left +
1801                              c->frame->size.right,
1802                              c->area.height + c->frame->size.top +
1803                              c->frame->size.bottom, c->shaded);
1804
1805     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1806 }
1807
1808 void action_reconfigure(union ActionData *data)
1809 {
1810     ob_reconfigure();
1811 }
1812
1813 void action_restart(union ActionData *data)
1814 {
1815     ob_restart_other(data->execute.path);
1816 }
1817
1818 void action_exit(union ActionData *data)
1819 {
1820     ob_exit(0);
1821 }
1822
1823 void action_showmenu(union ActionData *data)
1824 {
1825     if (data->showmenu.name) {
1826         menu_show(data->showmenu.name, data->any.x, data->any.y,
1827                   data->any.button, data->showmenu.any.c);
1828     }
1829 }
1830
1831 void action_cycle_windows(union ActionData *data)
1832 {
1833     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1834        on us */
1835     event_halt_focus_delay();
1836
1837     focus_cycle(data->cycle.forward,
1838                 data->cycle.all_desktops,
1839                 data->cycle.dock_windows,
1840                 data->cycle.desktop_windows,
1841                 data->cycle.linear, data->any.interactive,
1842                 data->cycle.dialog,
1843                 data->cycle.inter.final, data->cycle.inter.cancel);
1844 }
1845
1846 void action_directional_focus(union ActionData *data)
1847 {
1848     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1849        on us */
1850     event_halt_focus_delay();
1851
1852     focus_directional_cycle(data->interdiraction.direction,
1853                             data->interdiraction.dock_windows,
1854                             data->interdiraction.desktop_windows,
1855                             data->any.interactive,
1856                             data->interdiraction.dialog,
1857                             data->interdiraction.inter.final,
1858                             data->interdiraction.inter.cancel);
1859 }
1860
1861 void action_movetoedge(union ActionData *data)
1862 {
1863     gint x, y;
1864     ObClient *c = data->diraction.any.c;
1865
1866     x = c->frame->area.x;
1867     y = c->frame->area.y;
1868     
1869     switch(data->diraction.direction) {
1870     case OB_DIRECTION_NORTH:
1871         y = client_directional_edge_search(c, OB_DIRECTION_NORTH,
1872                                            data->diraction.hang)
1873             - (data->diraction.hang ? c->frame->area.height : 0);
1874         break;
1875     case OB_DIRECTION_WEST:
1876         x = client_directional_edge_search(c, OB_DIRECTION_WEST,
1877                                            data->diraction.hang)
1878             - (data->diraction.hang ? c->frame->area.width : 0);
1879         break;
1880     case OB_DIRECTION_SOUTH:
1881         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH,
1882                                            data->diraction.hang)
1883             - (data->diraction.hang ? 0 : c->frame->area.height);
1884         break;
1885     case OB_DIRECTION_EAST:
1886         x = client_directional_edge_search(c, OB_DIRECTION_EAST,
1887                                            data->diraction.hang)
1888             - (data->diraction.hang ? 0 : c->frame->area.width);
1889         break;
1890     default:
1891         g_assert_not_reached();
1892     }
1893     frame_frame_gravity(c->frame, &x, &y, c->area.width, c->area.height);
1894     client_action_start(data);
1895     client_move(c, x, y);
1896     client_action_end(data);
1897 }
1898
1899 void action_growtoedge(union ActionData *data)
1900 {
1901     gint x, y, width, height, dest;
1902     ObClient *c = data->diraction.any.c;
1903     Rect *a;
1904
1905     a = screen_area(c->desktop);
1906     x = c->frame->area.x;
1907     y = c->frame->area.y;
1908     /* get the unshaded frame's dimensions..if it is shaded */
1909     width = c->area.width + c->frame->size.left + c->frame->size.right;
1910     height = c->area.height + c->frame->size.top + c->frame->size.bottom;
1911
1912     switch(data->diraction.direction) {
1913     case OB_DIRECTION_NORTH:
1914         if (c->shaded) break; /* don't allow vertical resize if shaded */
1915
1916         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH, FALSE);
1917         if (a->y == y)
1918             height = height / 2;
1919         else {
1920             height = c->frame->area.y + height - dest;
1921             y = dest;
1922         }
1923         break;
1924     case OB_DIRECTION_WEST:
1925         dest = client_directional_edge_search(c, OB_DIRECTION_WEST, FALSE);
1926         if (a->x == x)
1927             width = width / 2;
1928         else {
1929             width = c->frame->area.x + width - dest;
1930             x = dest;
1931         }
1932         break;
1933     case OB_DIRECTION_SOUTH:
1934         if (c->shaded) break; /* don't allow vertical resize if shaded */
1935
1936         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH, FALSE);
1937         if (a->y + a->height == y + c->frame->area.height) {
1938             height = c->frame->area.height / 2;
1939             y = a->y + a->height - height;
1940         } else
1941             height = dest - c->frame->area.y;
1942         y += (height - c->frame->area.height) % c->size_inc.height;
1943         height -= (height - c->frame->area.height) % c->size_inc.height;
1944         break;
1945     case OB_DIRECTION_EAST:
1946         dest = client_directional_edge_search(c, OB_DIRECTION_EAST, FALSE);
1947         if (a->x + a->width == x + c->frame->area.width) {
1948             width = c->frame->area.width / 2;
1949             x = a->x + a->width - width;
1950         } else
1951             width = dest - c->frame->area.x;
1952         x += (width - c->frame->area.width) % c->size_inc.width;
1953         width -= (width - c->frame->area.width) % c->size_inc.width;
1954         break;
1955     default:
1956         g_assert_not_reached();
1957     }
1958     width -= c->frame->size.left + c->frame->size.right;
1959     height -= c->frame->size.top + c->frame->size.bottom;
1960     frame_frame_gravity(c->frame, &x, &y, width, height);
1961     client_action_start(data);
1962     client_move_resize(c, x, y, width, height);
1963     client_action_end(data);
1964 }
1965
1966 void action_send_to_layer(union ActionData *data)
1967 {
1968     client_set_layer(data->layer.any.c, data->layer.layer);
1969 }
1970
1971 void action_toggle_layer(union ActionData *data)
1972 {
1973     ObClient *c = data->layer.any.c;
1974
1975     client_action_start(data);
1976     if (data->layer.layer < 0)
1977         client_set_layer(c, c->below ? 0 : -1);
1978     else if (data->layer.layer > 0)
1979         client_set_layer(c, c->above ? 0 : 1);
1980     client_action_end(data);
1981 }
1982
1983 void action_toggle_dockautohide(union ActionData *data)
1984 {
1985     config_dock_hide = !config_dock_hide;
1986     dock_configure();
1987 }
1988
1989 void action_toggle_show_desktop(union ActionData *data)
1990 {
1991     screen_show_desktop(!screen_showing_desktop, NULL);
1992 }
1993
1994 void action_show_desktop(union ActionData *data)
1995 {
1996     screen_show_desktop(TRUE, NULL);
1997 }
1998
1999 void action_unshow_desktop(union ActionData *data)
2000 {
2001     screen_show_desktop(FALSE, NULL);
2002 }
2003
2004 void action_break_chroot(union ActionData *data)
2005 {
2006     /* break out of one chroot */
2007     keyboard_reset_chains(1);
2008 }