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