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