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