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