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