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