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