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