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