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