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