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