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