]> Dogcows Code - chaz/openbox/blob - openbox/action.c
add the directionalcyclewindows action
[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
43
44 typedef struct
45 {
46 const gchar *name;
47 void (*func)(union ActionData *);
48 void (*setup)(ObAction **, ObUserAction uact);
49 } ActionString;
50
51 static ObAction *action_new(void (*func)(union ActionData *data))
52 {
53 ObAction *a = g_new0(ObAction, 1);
54 a->ref = 1;
55 a->func = func;
56
57 return a;
58 }
59
60 void action_ref(ObAction *a)
61 {
62 ++a->ref;
63 }
64
65 void action_unref(ObAction *a)
66 {
67 if (a == NULL) return;
68
69 if (--a->ref > 0) return;
70
71 /* deal with pointers */
72 if (a->func == action_execute || a->func == action_restart)
73 g_free(a->data.execute.path);
74 else if (a->func == action_debug)
75 g_free(a->data.debug.string);
76 else if (a->func == action_showmenu)
77 g_free(a->data.showmenu.name);
78
79 g_free(a);
80 }
81
82 ObAction* action_copy(const ObAction *src)
83 {
84 ObAction *a = action_new(src->func);
85
86 a->data = src->data;
87
88 /* deal with pointers */
89 if (a->func == action_execute || a->func == action_restart)
90 a->data.execute.path = g_strdup(a->data.execute.path);
91 else if (a->func == action_debug)
92 a->data.debug.string = g_strdup(a->data.debug.string);
93 else if (a->func == action_showmenu)
94 a->data.showmenu.name = g_strdup(a->data.showmenu.name);
95
96 return a;
97 }
98
99 void setup_action_send_to_desktop(ObAction **a, ObUserAction uact)
100 {
101 (*a)->data.sendto.any.client_action = OB_CLIENT_ACTION_ALWAYS;
102 (*a)->data.sendto.follow = TRUE;
103 }
104
105 void setup_action_send_to_desktop_prev(ObAction **a, ObUserAction uact)
106 {
107 (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
108 (*a)->data.sendtodir.inter.any.interactive = TRUE;
109 (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
110 (*a)->data.sendtodir.linear = TRUE;
111 (*a)->data.sendtodir.wrap = TRUE;
112 (*a)->data.sendtodir.follow = TRUE;
113 }
114
115 void setup_action_send_to_desktop_next(ObAction **a, ObUserAction uact)
116 {
117 (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
118 (*a)->data.sendtodir.inter.any.interactive = TRUE;
119 (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
120 (*a)->data.sendtodir.linear = TRUE;
121 (*a)->data.sendtodir.wrap = TRUE;
122 (*a)->data.sendtodir.follow = TRUE;
123 }
124
125 void setup_action_send_to_desktop_left(ObAction **a, ObUserAction uact)
126 {
127 (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
128 (*a)->data.sendtodir.inter.any.interactive = TRUE;
129 (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
130 (*a)->data.sendtodir.linear = FALSE;
131 (*a)->data.sendtodir.wrap = TRUE;
132 (*a)->data.sendtodir.follow = TRUE;
133 }
134
135 void setup_action_send_to_desktop_right(ObAction **a, ObUserAction uact)
136 {
137 (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
138 (*a)->data.sendtodir.inter.any.interactive = TRUE;
139 (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
140 (*a)->data.sendtodir.linear = FALSE;
141 (*a)->data.sendtodir.wrap = TRUE;
142 (*a)->data.sendtodir.follow = TRUE;
143 }
144
145 void setup_action_send_to_desktop_up(ObAction **a, ObUserAction uact)
146 {
147 (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
148 (*a)->data.sendtodir.inter.any.interactive = TRUE;
149 (*a)->data.sendtodir.dir = OB_DIRECTION_NORTH;
150 (*a)->data.sendtodir.linear = FALSE;
151 (*a)->data.sendtodir.wrap = TRUE;
152 (*a)->data.sendtodir.follow = TRUE;
153 }
154
155 void setup_action_send_to_desktop_down(ObAction **a, ObUserAction uact)
156 {
157 (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
158 (*a)->data.sendtodir.inter.any.interactive = TRUE;
159 (*a)->data.sendtodir.dir = OB_DIRECTION_SOUTH;
160 (*a)->data.sendtodir.linear = FALSE;
161 (*a)->data.sendtodir.wrap = TRUE;
162 (*a)->data.sendtodir.follow = TRUE;
163 }
164
165 void setup_action_desktop(ObAction **a, ObUserAction uact)
166 {
167 /*
168 (*a)->data.desktop.inter.any.interactive = FALSE;
169 */
170 }
171
172 void setup_action_desktop_prev(ObAction **a, ObUserAction uact)
173 {
174 (*a)->data.desktopdir.inter.any.interactive = TRUE;
175 (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
176 (*a)->data.desktopdir.linear = TRUE;
177 (*a)->data.desktopdir.wrap = TRUE;
178 }
179
180 void setup_action_desktop_next(ObAction **a, ObUserAction uact)
181 {
182 (*a)->data.desktopdir.inter.any.interactive = TRUE;
183 (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
184 (*a)->data.desktopdir.linear = TRUE;
185 (*a)->data.desktopdir.wrap = TRUE;
186 }
187
188 void setup_action_desktop_left(ObAction **a, ObUserAction uact)
189 {
190 (*a)->data.desktopdir.inter.any.interactive = TRUE;
191 (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
192 (*a)->data.desktopdir.linear = FALSE;
193 (*a)->data.desktopdir.wrap = TRUE;
194 }
195
196 void setup_action_desktop_right(ObAction **a, ObUserAction uact)
197 {
198 (*a)->data.desktopdir.inter.any.interactive = TRUE;
199 (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
200 (*a)->data.desktopdir.linear = FALSE;
201 (*a)->data.desktopdir.wrap = TRUE;
202 }
203
204 void setup_action_desktop_up(ObAction **a, ObUserAction uact)
205 {
206 (*a)->data.desktopdir.inter.any.interactive = TRUE;
207 (*a)->data.desktopdir.dir = OB_DIRECTION_NORTH;
208 (*a)->data.desktopdir.linear = FALSE;
209 (*a)->data.desktopdir.wrap = TRUE;
210 }
211
212 void setup_action_desktop_down(ObAction **a, ObUserAction uact)
213 {
214 (*a)->data.desktopdir.inter.any.interactive = TRUE;
215 (*a)->data.desktopdir.dir = OB_DIRECTION_SOUTH;
216 (*a)->data.desktopdir.linear = FALSE;
217 (*a)->data.desktopdir.wrap = TRUE;
218 }
219
220 void setup_action_movefromedge_north(ObAction **a, ObUserAction uact)
221 {
222 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
223 (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
224 (*a)->data.diraction.hang = TRUE;
225 }
226
227 void setup_action_movefromedge_south(ObAction **a, ObUserAction uact)
228 {
229 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
230 (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
231 (*a)->data.diraction.hang = TRUE;
232 }
233
234 void setup_action_movefromedge_east(ObAction **a, ObUserAction uact)
235 {
236 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
237 (*a)->data.diraction.direction = OB_DIRECTION_EAST;
238 (*a)->data.diraction.hang = TRUE;
239 }
240
241 void setup_action_movefromedge_west(ObAction **a, ObUserAction uact)
242 {
243 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
244 (*a)->data.diraction.direction = OB_DIRECTION_WEST;
245 (*a)->data.diraction.hang = TRUE;
246 }
247
248 void setup_action_movetoedge_north(ObAction **a, ObUserAction uact)
249 {
250 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
251 (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
252 (*a)->data.diraction.hang = FALSE;
253 }
254
255 void setup_action_movetoedge_south(ObAction **a, ObUserAction uact)
256 {
257 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
258 (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
259 (*a)->data.diraction.hang = FALSE;
260 }
261
262 void setup_action_movetoedge_east(ObAction **a, ObUserAction uact)
263 {
264 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
265 (*a)->data.diraction.direction = OB_DIRECTION_EAST;
266 (*a)->data.diraction.hang = FALSE;
267 }
268
269 void setup_action_movetoedge_west(ObAction **a, ObUserAction uact)
270 {
271 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
272 (*a)->data.diraction.direction = OB_DIRECTION_WEST;
273 (*a)->data.diraction.hang = FALSE;
274 }
275
276 void setup_action_growtoedge_north(ObAction **a, ObUserAction uact)
277 {
278 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
279 (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
280 }
281
282 void setup_action_growtoedge_south(ObAction **a, ObUserAction uact)
283 {
284 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
285 (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
286 }
287
288 void setup_action_growtoedge_east(ObAction **a, ObUserAction uact)
289 {
290 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
291 (*a)->data.diraction.direction = OB_DIRECTION_EAST;
292 }
293
294 void setup_action_growtoedge_west(ObAction **a, ObUserAction uact)
295 {
296 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
297 (*a)->data.diraction.direction = OB_DIRECTION_WEST;
298 }
299
300 void setup_action_top_layer(ObAction **a, ObUserAction uact)
301 {
302 (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
303 (*a)->data.layer.layer = 1;
304 }
305
306 void setup_action_normal_layer(ObAction **a, ObUserAction uact)
307 {
308 (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
309 (*a)->data.layer.layer = 0;
310 }
311
312 void setup_action_bottom_layer(ObAction **a, ObUserAction uact)
313 {
314 (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
315 (*a)->data.layer.layer = -1;
316 }
317
318 void setup_action_resize(ObAction **a, ObUserAction uact)
319 {
320 (*a)->data.moveresize.any.client_action = OB_CLIENT_ACTION_ALWAYS;
321 (*a)->data.moveresize.keyboard =
322 (uact == OB_USER_ACTION_NONE ||
323 uact == OB_USER_ACTION_KEYBOARD_KEY ||
324 uact == OB_USER_ACTION_MENU_SELECTION);
325 (*a)->data.moveresize.corner = 0;
326 }
327
328 void setup_action_addremove_desktop_current(ObAction **a, ObUserAction uact)
329 {
330 (*a)->data.addremovedesktop.current = TRUE;
331 }
332
333 void setup_action_addremove_desktop_last(ObAction **a, ObUserAction uact)
334 {
335 (*a)->data.addremovedesktop.current = FALSE;
336 }
337
338 void setup_client_action(ObAction **a, ObUserAction uact)
339 {
340 (*a)->data.any.client_action = OB_CLIENT_ACTION_ALWAYS;
341 }
342
343 ActionString actionstrings[] =
344 {
345 {
346 "shadelower",
347 action_shadelower,
348 setup_client_action
349 },
350 {
351 "unshaderaise",
352 action_unshaderaise,
353 setup_client_action
354 },
355 {
356 "resizerelativevert",
357 action_resize_relative_vert,
358 setup_client_action
359 },
360 {
361 "resizerelative",
362 action_resize_relative,
363 setup_client_action
364 },
365 {
366 "sendtodesktop",
367 action_send_to_desktop,
368 setup_action_send_to_desktop
369 },
370 {
371 "sendtodesktopnext",
372 action_send_to_desktop_dir,
373 setup_action_send_to_desktop_next
374 },
375 {
376 "sendtodesktopprevious",
377 action_send_to_desktop_dir,
378 setup_action_send_to_desktop_prev
379 },
380 {
381 "sendtodesktopright",
382 action_send_to_desktop_dir,
383 setup_action_send_to_desktop_right
384 },
385 {
386 "sendtodesktopleft",
387 action_send_to_desktop_dir,
388 setup_action_send_to_desktop_left
389 },
390 {
391 "sendtodesktopup",
392 action_send_to_desktop_dir,
393 setup_action_send_to_desktop_up
394 },
395 {
396 "sendtodesktopdown",
397 action_send_to_desktop_dir,
398 setup_action_send_to_desktop_down
399 },
400 {
401 "desktop",
402 action_desktop,
403 setup_action_desktop
404 },
405 {
406 "desktopnext",
407 action_desktop_dir,
408 setup_action_desktop_next
409 },
410 {
411 "desktopprevious",
412 action_desktop_dir,
413 setup_action_desktop_prev
414 },
415 {
416 "desktopright",
417 action_desktop_dir,
418 setup_action_desktop_right
419 },
420 {
421 "desktopleft",
422 action_desktop_dir,
423 setup_action_desktop_left
424 },
425 {
426 "desktopup",
427 action_desktop_dir,
428 setup_action_desktop_up
429 },
430 {
431 "desktopdown",
432 action_desktop_dir,
433 setup_action_desktop_down
434 },
435 {
436 "toggledecorations",
437 action_toggle_decorations,
438 setup_client_action
439 },
440 {
441 "resize",
442 action_resize,
443 setup_action_resize
444 },
445 {
446 "toggledockautohide",
447 action_toggle_dockautohide,
448 NULL
449 },
450 {
451 "desktoplast",
452 action_desktop_last,
453 NULL
454 },
455 {
456 "sendtotoplayer",
457 action_send_to_layer,
458 setup_action_top_layer
459 },
460 {
461 "togglealwaysontop",
462 action_toggle_layer,
463 setup_action_top_layer
464 },
465 {
466 "sendtonormallayer",
467 action_send_to_layer,
468 setup_action_normal_layer
469 },
470 {
471 "sendtobottomlayer",
472 action_send_to_layer,
473 setup_action_bottom_layer
474 },
475 {
476 "togglealwaysonbottom",
477 action_toggle_layer,
478 setup_action_bottom_layer
479 },
480 {
481 "movefromedgenorth",
482 action_movetoedge,
483 setup_action_movefromedge_north
484 },
485 {
486 "movefromedgesouth",
487 action_movetoedge,
488 setup_action_movefromedge_south
489 },
490 {
491 "movefromedgewest",
492 action_movetoedge,
493 setup_action_movefromedge_west
494 },
495 {
496 "movefromedgeeast",
497 action_movetoedge,
498 setup_action_movefromedge_east
499 },
500 {
501 "movetoedgenorth",
502 action_movetoedge,
503 setup_action_movetoedge_north
504 },
505 {
506 "movetoedgesouth",
507 action_movetoedge,
508 setup_action_movetoedge_south
509 },
510 {
511 "movetoedgewest",
512 action_movetoedge,
513 setup_action_movetoedge_west
514 },
515 {
516 "movetoedgeeast",
517 action_movetoedge,
518 setup_action_movetoedge_east
519 },
520 {
521 "growtoedgenorth",
522 action_growtoedge,
523 setup_action_growtoedge_north
524 },
525 {
526 "growtoedgesouth",
527 action_growtoedge,
528 setup_action_growtoedge_south
529 },
530 {
531 "growtoedgewest",
532 action_growtoedge,
533 setup_action_growtoedge_west
534 },
535 {
536 "growtoedgeeast",
537 action_growtoedge,
538 setup_action_growtoedge_east
539 },
540 {
541 "adddesktoplast",
542 action_add_desktop,
543 setup_action_addremove_desktop_last
544 },
545 {
546 "removedesktoplast",
547 action_remove_desktop,
548 setup_action_addremove_desktop_last
549 },
550 {
551 "adddesktopcurrent",
552 action_add_desktop,
553 setup_action_addremove_desktop_current
554 },
555 {
556 "removedesktopcurrent",
557 action_remove_desktop,
558 setup_action_addremove_desktop_current
559 },
560 {
561 NULL,
562 NULL,
563 NULL
564 }
565 };
566
567 /* only key bindings can be interactive. thus saith the xor.
568 because of how the mouse is grabbed, mouse events dont even get
569 read during interactive events, so no dice! >:) */
570 #define INTERACTIVE_LIMIT(a, uact) \
571 if (uact != OB_USER_ACTION_KEYBOARD_KEY) \
572 a->data.any.interactive = FALSE;
573
574 ObAction *action_from_string(const gchar *name, ObUserAction uact)
575 {
576 ObAction *a = NULL;
577 gboolean exist = FALSE;
578 gint i;
579
580 for (i = 0; actionstrings[i].name; i++)
581 if (!g_ascii_strcasecmp(name, actionstrings[i].name)) {
582 exist = TRUE;
583 a = action_new(actionstrings[i].func);
584 if (actionstrings[i].setup)
585 actionstrings[i].setup(&a, uact);
586 if (a)
587 INTERACTIVE_LIMIT(a, uact);
588 break;
589 }
590 if (!exist)
591 g_message(_("Invalid action '%s' requested. No such action exists."),
592 name);
593 if (!a)
594 g_message(_("Invalid use of action '%s'. Action will be ignored."),
595 name);
596 return a;
597 }
598
599 ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
600 ObUserAction uact)
601 {
602 gchar *actname;
603 ObAction *act = NULL;
604 xmlNodePtr n;
605
606 if (parse_attr_string("name", node, &actname)) {
607 if ((act = action_from_string(actname, uact))) {
608 } else if (act->func == action_resize_relative) {
609 if ((n = parse_find_node("left", node->xmlChildrenNode)))
610 act->data.relative.deltaxl = parse_int(doc, n);
611 if ((n = parse_find_node("up", node->xmlChildrenNode)))
612 act->data.relative.deltayu = parse_int(doc, n);
613 if ((n = parse_find_node("right", node->xmlChildrenNode)))
614 act->data.relative.deltax = parse_int(doc, n);
615 if ((n = parse_find_node("down", node->xmlChildrenNode)))
616 act->data.relative.deltay = parse_int(doc, n);
617 } else if (act->func == action_desktop) {
618 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
619 act->data.desktop.desk = parse_int(doc, n);
620 if (act->data.desktop.desk > 0) act->data.desktop.desk--;
621 /*
622 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
623 act->data.desktop.inter.any.interactive =
624 parse_bool(doc, n);
625 */
626 } else if (act->func == action_send_to_desktop) {
627 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
628 act->data.sendto.desk = parse_int(doc, n);
629 if (act->data.sendto.desk > 0) act->data.sendto.desk--;
630 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
631 act->data.sendto.follow = parse_bool(doc, n);
632 } else if (act->func == action_desktop_dir) {
633 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
634 act->data.desktopdir.wrap = parse_bool(doc, n);
635 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
636 act->data.desktopdir.inter.any.interactive =
637 parse_bool(doc, n);
638 } else if (act->func == action_send_to_desktop_dir) {
639 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
640 act->data.sendtodir.wrap = parse_bool(doc, n);
641 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
642 act->data.sendtodir.follow = parse_bool(doc, n);
643 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
644 act->data.sendtodir.inter.any.interactive =
645 parse_bool(doc, n);
646 } else if (act->func == action_resize) {
647 if ((n = parse_find_node("edge", node->xmlChildrenNode))) {
648 gchar *s = parse_string(doc, n);
649 if (!g_ascii_strcasecmp(s, "top"))
650 act->data.moveresize.corner =
651 prop_atoms.net_wm_moveresize_size_top;
652 else if (!g_ascii_strcasecmp(s, "bottom"))
653 act->data.moveresize.corner =
654 prop_atoms.net_wm_moveresize_size_bottom;
655 else if (!g_ascii_strcasecmp(s, "left"))
656 act->data.moveresize.corner =
657 prop_atoms.net_wm_moveresize_size_left;
658 else if (!g_ascii_strcasecmp(s, "right"))
659 act->data.moveresize.corner =
660 prop_atoms.net_wm_moveresize_size_right;
661 else if (!g_ascii_strcasecmp(s, "topleft"))
662 act->data.moveresize.corner =
663 prop_atoms.net_wm_moveresize_size_topleft;
664 else if (!g_ascii_strcasecmp(s, "topright"))
665 act->data.moveresize.corner =
666 prop_atoms.net_wm_moveresize_size_topright;
667 else if (!g_ascii_strcasecmp(s, "bottomleft"))
668 act->data.moveresize.corner =
669 prop_atoms.net_wm_moveresize_size_bottomleft;
670 else if (!g_ascii_strcasecmp(s, "bottomright"))
671 act->data.moveresize.corner =
672 prop_atoms.net_wm_moveresize_size_bottomright;
673 g_free(s);
674 }
675 INTERACTIVE_LIMIT(act, uact);
676 }
677 g_free(actname);
678 }
679 return act;
680 }
681
682 void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
683 guint state, guint button, gint x, gint y, Time time,
684 gboolean cancel, gboolean done)
685 {
686 GSList *it;
687 ObAction *a;
688
689 if (!acts)
690 return;
691
692 if (x < 0 && y < 0)
693 screen_pointer_pos(&x, &y);
694
695 for (it = acts; it; it = g_slist_next(it)) {
696 a = it->data;
697
698 if (!(a->data.any.client_action == OB_CLIENT_ACTION_ALWAYS && !c)) {
699 a->data.any.c = a->data.any.client_action ? c : NULL;
700 a->data.any.context = context;
701 a->data.any.x = x;
702 a->data.any.y = y;
703
704 a->data.any.button = button;
705
706 a->data.any.time = time;
707
708 if (a->data.any.interactive) {
709 a->data.inter.cancel = cancel;
710 a->data.inter.final = done;
711 if (!(cancel || done))
712 if (!keyboard_interactive_grab(state, a->data.any.c, a))
713 continue;
714 }
715
716 /* XXX UGLY HACK race with motion event starting a move and the
717 button release gettnig processed first. answer: don't queue
718 moveresize starts. UGLY HACK XXX
719
720 XXX ALSO don't queue showmenu events, because on button press
721 events we need to know if a mouse grab is going to take place,
722 and set the button to 0, so that later motion events don't think
723 that a drag is going on. since showmenu grabs the pointer..
724 */
725 if (a->data.any.interactive || a->func == action_move ||
726 a->func == action_resize || a->func == action_showmenu)
727 {
728 /* interactive actions are not queued */
729 a->func(&a->data);
730 } else if (a->func == action_focus ||
731 a->func == action_activate ||
732 a->func == action_showmenu)
733 {
734 /* XXX MORE UGLY HACK
735 actions from clicks on client windows are NOT queued.
736 this solves the mysterious click-and-drag-doesnt-work
737 problem. it was because the window gets focused and stuff
738 after the button event has already been passed through. i
739 dont really know why it should care but it does and it makes
740 a difference.
741
742 however this very bogus ! !
743 we want to send the button press to the window BEFORE
744 we do the action because the action might move the windows
745 (eg change desktops) and then the button press ends up on
746 the completely wrong window !
747 so, this is just for that bug, and it will only NOT queue it
748 if it is a focusing action that can be used with the mouse
749 pointer. ugh.
750
751 also with the menus, there is a race going on. if the
752 desktop wants to pop up a menu, and we do too, we send them
753 the button before we pop up the menu, so they pop up their
754 menu first. but not always. if we pop up our menu before
755 sending them the button press, then the result is
756 deterministic. yay.
757
758 XXX further more. focus actions are not queued at all,
759 because if you bind focus->showmenu, the menu will get
760 hidden to do the focusing
761 */
762 a->func(&a->data);
763 } else
764 ob_main_loop_queue_action(ob_main_loop, a);
765 }
766 }
767 }
768
769 void action_run_string(const gchar *name, struct _ObClient *c, Time time)
770 {
771 ObAction *a;
772 GSList *l;
773
774 a = action_from_string(name, OB_USER_ACTION_NONE);
775 g_assert(a);
776
777 l = g_slist_append(NULL, a);
778
779 action_run(l, c, 0, time);
780 }
781
782 void action_unshaderaise(union ActionData *data)
783 {
784 if (data->client.any.c->shaded)
785 action_unshade(data);
786 else
787 action_raise(data);
788 }
789
790 void action_shadelower(union ActionData *data)
791 {
792 if (data->client.any.c->shaded)
793 action_lower(data);
794 else
795 action_shade(data);
796 }
797
798 void action_resize_relative_horz(union ActionData *data)
799 {
800 ObClient *c = data->relative.any.c;
801 client_action_start(data);
802 client_resize(c,
803 c->area.width + data->relative.deltax * c->size_inc.width,
804 c->area.height);
805 client_action_end(data, FALSE);
806 }
807
808 void action_resize_relative_vert(union ActionData *data)
809 {
810 ObClient *c = data->relative.any.c;
811 if (!c->shaded) {
812 client_action_start(data);
813 client_resize(c, c->area.width, c->area.height +
814 data->relative.deltax * c->size_inc.height);
815 client_action_end(data, FALSE);
816 }
817 }
818
819 void action_resize_relative(union ActionData *data)
820 {
821 ObClient *c = data->relative.any.c;
822 gint x, y, ow, xoff, nw, oh, yoff, nh, lw, lh;
823
824 client_action_start(data);
825
826 x = c->area.x;
827 y = c->area.y;
828 ow = c->area.width;
829 xoff = -data->relative.deltaxl * c->size_inc.width;
830 nw = ow + data->relative.deltax * c->size_inc.width
831 + data->relative.deltaxl * c->size_inc.width;
832 oh = c->area.height;
833 yoff = -data->relative.deltayu * c->size_inc.height;
834 nh = oh + data->relative.deltay * c->size_inc.height
835 + data->relative.deltayu * c->size_inc.height;
836
837 g_print("deltax %d %d x %d ow %d xoff %d nw %d\n",
838 data->relative.deltax,
839 data->relative.deltaxl,
840 x, ow, xoff, nw);
841
842 client_try_configure(c, &x, &y, &nw, &nh, &lw, &lh, TRUE);
843 xoff = xoff == 0 ? 0 : (xoff < 0 ? MAX(xoff, ow-nw) : MIN(xoff, ow-nw));
844 yoff = yoff == 0 ? 0 : (yoff < 0 ? MAX(yoff, oh-nh) : MIN(yoff, oh-nh));
845 client_move_resize(c, x + xoff, y + yoff, nw, nh);
846 client_action_end(data, FALSE);
847 }
848
849 void action_send_to_desktop(union ActionData *data)
850 {
851 ObClient *c = data->sendto.any.c;
852
853 if (!client_normal(c)) return;
854
855 if (data->sendto.desk < screen_num_desktops ||
856 data->sendto.desk == DESKTOP_ALL) {
857 client_set_desktop(c, data->sendto.desk, data->sendto.follow, FALSE);
858 if (data->sendto.follow && data->sendto.desk != screen_desktop)
859 screen_set_desktop(data->sendto.desk, TRUE);
860 }
861 }
862
863 void action_desktop(union ActionData *data)
864 {
865 /* XXX add the interactive/dialog option back again once the dialog
866 has been made to not use grabs */
867 if (data->desktop.desk < screen_num_desktops ||
868 data->desktop.desk == DESKTOP_ALL)
869 {
870 screen_set_desktop(data->desktop.desk, TRUE);
871 if (data->inter.any.interactive)
872 screen_desktop_popup(data->desktop.desk, TRUE);
873 }
874 }
875
876 void action_desktop_dir(union ActionData *data)
877 {
878 guint d;
879
880 d = screen_cycle_desktop(data->desktopdir.dir,
881 data->desktopdir.wrap,
882 data->desktopdir.linear,
883 data->desktopdir.inter.any.interactive,
884 data->desktopdir.inter.final,
885 data->desktopdir.inter.cancel);
886 /* only move the desktop when the action is complete. if we switch
887 desktops during the interactive action, focus will move but with
888 NotifyWhileGrabbed and applications don't like that. */
889 if (!data->sendtodir.inter.any.interactive ||
890 (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
891 {
892 if (d != screen_desktop)
893 screen_set_desktop(d, TRUE);
894 }
895 }
896
897 void action_send_to_desktop_dir(union ActionData *data)
898 {
899 ObClient *c = data->sendtodir.inter.any.c;
900 guint d;
901
902 if (!client_normal(c)) return;
903
904 d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
905 data->sendtodir.linear,
906 data->sendtodir.inter.any.interactive,
907 data->sendtodir.inter.final,
908 data->sendtodir.inter.cancel);
909 /* only move the desktop when the action is complete. if we switch
910 desktops during the interactive action, focus will move but with
911 NotifyWhileGrabbed and applications don't like that. */
912 if (!data->sendtodir.inter.any.interactive ||
913 (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
914 {
915 client_set_desktop(c, d, data->sendtodir.follow, FALSE);
916 if (data->sendtodir.follow && d != screen_desktop)
917 screen_set_desktop(d, TRUE);
918 }
919 }
920
921 void action_desktop_last(union ActionData *data)
922 {
923 if (screen_last_desktop < screen_num_desktops)
924 screen_set_desktop(screen_last_desktop, TRUE);
925 }
926
927 void action_toggle_decorations(union ActionData *data)
928 {
929 ObClient *c = data->client.any.c;
930
931 client_action_start(data);
932 client_set_undecorated(c, !c->undecorated);
933 client_action_end(data, FALSE);
934 }
935
936 static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch,
937 gboolean shaded)
938 {
939 /* let's make x and y client relative instead of screen relative */
940 x = x - cx;
941 y = ch - (y - cy); /* y is inverted, 0 is at the bottom of the window */
942
943 #define X x*ch/cw
944 #define A -4*X + 7*ch/3
945 #define B 4*X -15*ch/9
946 #define C -X/4 + 2*ch/3
947 #define D X/4 + 5*ch/12
948 #define E X/4 + ch/3
949 #define F -X/4 + 7*ch/12
950 #define G 4*X - 4*ch/3
951 #define H -4*X + 8*ch/3
952 #define a (y > 5*ch/9)
953 #define b (x < 4*cw/9)
954 #define c (x > 5*cw/9)
955 #define d (y < 4*ch/9)
956
957 /*
958 Each of these defines (except X which is just there for fun), represents
959 the equation of a line. The lines they represent are shown in the diagram
960 below. Checking y against these lines, we are able to choose a region
961 of the window as shown.
962
963 +---------------------A-------|-------|-------B---------------------+
964 | |A B| |
965 | |A | | B| |
966 | | A B | |
967 | | A | | B | |
968 | | A B | |
969 | | A | | B | |
970 | northwest | A north B | northeast |
971 | | A | | B | |
972 | | A B | |
973 C---------------------+----A--+-------+--B----+---------------------D
974 |CCCCCCC | A B | DDDDDDD|
975 | CCCCCCCC | A | | B | DDDDDDDD |
976 | CCCCCCC A B DDDDDDD |
977 - - - - - - - - - - - +CCCCCCC+aaaaaaa+DDDDDDD+ - - - - - - - - - - - -
978 | | b c | | sh
979 | west | b move c | east | ad
980 | | b c | | ed
981 - - - - - - - - - - - +EEEEEEE+ddddddd+FFFFFFF+- - - - - - - - - - - -
982 | EEEEEEE G H FFFFFFF |
983 | EEEEEEEE | G | | H | FFFFFFFF |
984 |EEEEEEE | G H | FFFFFFF|
985 E---------------------+----G--+-------+--H----+---------------------F
986 | | G H | |
987 | | G | | H | |
988 | southwest | G south H | southeast |
989 | | G | | H | |
990 | | G H | |
991 | | G | | H | |
992 | | G H | |
993 | |G | | H| |
994 | |G H| |
995 +---------------------G-------|-------|-------H---------------------+
996 */
997
998 if (shaded) {
999 /* for shaded windows, you can only resize west/east and move */
1000 if (b)
1001 return prop_atoms.net_wm_moveresize_size_left;
1002 if (c)
1003 return prop_atoms.net_wm_moveresize_size_right;
1004 return prop_atoms.net_wm_moveresize_move;
1005 }
1006
1007 if (y < A && y >= C)
1008 return prop_atoms.net_wm_moveresize_size_topleft;
1009 else if (y >= A && y >= B && a)
1010 return prop_atoms.net_wm_moveresize_size_top;
1011 else if (y < B && y >= D)
1012 return prop_atoms.net_wm_moveresize_size_topright;
1013 else if (y < C && y >= E && b)
1014 return prop_atoms.net_wm_moveresize_size_left;
1015 else if (y < D && y >= F && c)
1016 return prop_atoms.net_wm_moveresize_size_right;
1017 else if (y < E && y >= G)
1018 return prop_atoms.net_wm_moveresize_size_bottomleft;
1019 else if (y < G && y < H && d)
1020 return prop_atoms.net_wm_moveresize_size_bottom;
1021 else if (y >= H && y < F)
1022 return prop_atoms.net_wm_moveresize_size_bottomright;
1023 else
1024 return prop_atoms.net_wm_moveresize_move;
1025
1026 #undef X
1027 #undef A
1028 #undef B
1029 #undef C
1030 #undef D
1031 #undef E
1032 #undef F
1033 #undef G
1034 #undef H
1035 #undef a
1036 #undef b
1037 #undef c
1038 #undef d
1039 }
1040
1041 void action_resize(union ActionData *data)
1042 {
1043 ObClient *c = data->moveresize.any.c;
1044 guint32 corner;
1045
1046 if (data->moveresize.keyboard)
1047 corner = prop_atoms.net_wm_moveresize_size_keyboard;
1048 else if (data->moveresize.corner)
1049 corner = data->moveresize.corner; /* it was specified in the binding */
1050 else
1051 corner = pick_corner(data->any.x, data->any.y,
1052 c->frame->area.x, c->frame->area.y,
1053 /* use the client size because the frame
1054 can be differently sized (shaded
1055 windows) and we want this based on the
1056 clients size */
1057 c->area.width + c->frame->size.left +
1058 c->frame->size.right,
1059 c->area.height + c->frame->size.top +
1060 c->frame->size.bottom, c->shaded);
1061
1062 moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1063 }
1064
1065 void action_directional_focus(union ActionData *data)
1066 {
1067 /* if using focus_delay, stop the timer now so that focus doesn't go moving
1068 on us */
1069 event_halt_focus_delay();
1070
1071 focus_directional_cycle(data->interdiraction.direction,
1072 data->interdiraction.dock_windows,
1073 data->interdiraction.desktop_windows,
1074 data->any.interactive,
1075 data->interdiraction.dialog,
1076 data->interdiraction.inter.final,
1077 data->interdiraction.inter.cancel);
1078 }
1079
1080 void action_movetoedge(union ActionData *data)
1081 {
1082 gint x, y;
1083 ObClient *c = data->diraction.any.c;
1084
1085 x = c->frame->area.x;
1086 y = c->frame->area.y;
1087
1088 switch(data->diraction.direction) {
1089 case OB_DIRECTION_NORTH:
1090 y = client_directional_edge_search(c, OB_DIRECTION_NORTH,
1091 data->diraction.hang)
1092 - (data->diraction.hang ? c->frame->area.height : 0);
1093 break;
1094 case OB_DIRECTION_WEST:
1095 x = client_directional_edge_search(c, OB_DIRECTION_WEST,
1096 data->diraction.hang)
1097 - (data->diraction.hang ? c->frame->area.width : 0);
1098 break;
1099 case OB_DIRECTION_SOUTH:
1100 y = client_directional_edge_search(c, OB_DIRECTION_SOUTH,
1101 data->diraction.hang)
1102 - (data->diraction.hang ? 0 : c->frame->area.height);
1103 break;
1104 case OB_DIRECTION_EAST:
1105 x = client_directional_edge_search(c, OB_DIRECTION_EAST,
1106 data->diraction.hang)
1107 - (data->diraction.hang ? 0 : c->frame->area.width);
1108 break;
1109 default:
1110 g_assert_not_reached();
1111 }
1112 frame_frame_gravity(c->frame, &x, &y, c->area.width, c->area.height);
1113 client_action_start(data);
1114 client_move(c, x, y);
1115 client_action_end(data, FALSE);
1116 }
1117
1118 void action_growtoedge(union ActionData *data)
1119 {
1120 gint x, y, width, height, dest;
1121 ObClient *c = data->diraction.any.c;
1122 Rect *a;
1123
1124 a = screen_area(c->desktop, SCREEN_AREA_ALL_MONITORS, &c->frame->area);
1125 x = c->frame->area.x;
1126 y = c->frame->area.y;
1127 /* get the unshaded frame's dimensions..if it is shaded */
1128 width = c->area.width + c->frame->size.left + c->frame->size.right;
1129 height = c->area.height + c->frame->size.top + c->frame->size.bottom;
1130
1131 switch(data->diraction.direction) {
1132 case OB_DIRECTION_NORTH:
1133 if (c->shaded) break; /* don't allow vertical resize if shaded */
1134
1135 dest = client_directional_edge_search(c, OB_DIRECTION_NORTH, FALSE);
1136 if (a->y == y)
1137 height = height / 2;
1138 else {
1139 height = c->frame->area.y + height - dest;
1140 y = dest;
1141 }
1142 break;
1143 case OB_DIRECTION_WEST:
1144 dest = client_directional_edge_search(c, OB_DIRECTION_WEST, FALSE);
1145 if (a->x == x)
1146 width = width / 2;
1147 else {
1148 width = c->frame->area.x + width - dest;
1149 x = dest;
1150 }
1151 break;
1152 case OB_DIRECTION_SOUTH:
1153 if (c->shaded) break; /* don't allow vertical resize if shaded */
1154
1155 dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH, FALSE);
1156 if (a->y + a->height == y + c->frame->area.height) {
1157 height = c->frame->area.height / 2;
1158 y = a->y + a->height - height;
1159 } else
1160 height = dest - c->frame->area.y;
1161 y += (height - c->frame->area.height) % c->size_inc.height;
1162 height -= (height - c->frame->area.height) % c->size_inc.height;
1163 break;
1164 case OB_DIRECTION_EAST:
1165 dest = client_directional_edge_search(c, OB_DIRECTION_EAST, FALSE);
1166 if (a->x + a->width == x + c->frame->area.width) {
1167 width = c->frame->area.width / 2;
1168 x = a->x + a->width - width;
1169 } else
1170 width = dest - c->frame->area.x;
1171 x += (width - c->frame->area.width) % c->size_inc.width;
1172 width -= (width - c->frame->area.width) % c->size_inc.width;
1173 break;
1174 default:
1175 g_assert_not_reached();
1176 }
1177 width -= c->frame->size.left + c->frame->size.right;
1178 height -= c->frame->size.top + c->frame->size.bottom;
1179 frame_frame_gravity(c->frame, &x, &y, width, height);
1180 client_action_start(data);
1181 client_move_resize(c, x, y, width, height);
1182 client_action_end(data, FALSE);
1183 g_free(a);
1184 }
1185
1186 void action_send_to_layer(union ActionData *data)
1187 {
1188 client_set_layer(data->layer.any.c, data->layer.layer);
1189 }
1190
1191 void action_toggle_layer(union ActionData *data)
1192 {
1193 ObClient *c = data->layer.any.c;
1194
1195 client_action_start(data);
1196 if (data->layer.layer < 0)
1197 client_set_layer(c, c->below ? 0 : -1);
1198 else if (data->layer.layer > 0)
1199 client_set_layer(c, c->above ? 0 : 1);
1200 client_action_end(data, config_focus_under_mouse);
1201 }
1202
1203 void action_toggle_dockautohide(union ActionData *data)
1204 {
1205 config_dock_hide = !config_dock_hide;
1206 dock_configure();
1207 }
1208
1209 void action_add_desktop(union ActionData *data)
1210 {
1211 client_action_start(data);
1212 screen_set_num_desktops(screen_num_desktops+1);
1213
1214 /* move all the clients over */
1215 if (data->addremovedesktop.current) {
1216 GList *it;
1217
1218 for (it = client_list; it; it = g_list_next(it)) {
1219 ObClient *c = it->data;
1220 if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop)
1221 client_set_desktop(c, c->desktop+1, FALSE, TRUE);
1222 }
1223 }
1224
1225 client_action_end(data, config_focus_under_mouse);
1226 }
1227
1228 void action_remove_desktop(union ActionData *data)
1229 {
1230 guint rmdesktop, movedesktop;
1231 GList *it, *stacking_copy;
1232
1233 if (screen_num_desktops < 2) return;
1234
1235 client_action_start(data);
1236
1237 /* what desktop are we removing and moving to? */
1238 if (data->addremovedesktop.current)
1239 rmdesktop = screen_desktop;
1240 else
1241 rmdesktop = screen_num_desktops - 1;
1242 if (rmdesktop < screen_num_desktops - 1)
1243 movedesktop = rmdesktop + 1;
1244 else
1245 movedesktop = rmdesktop;
1246
1247 /* make a copy of the list cuz we're changing it */
1248 stacking_copy = g_list_copy(stacking_list);
1249 for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
1250 if (WINDOW_IS_CLIENT(it->data)) {
1251 ObClient *c = it->data;
1252 guint d = c->desktop;
1253 if (d != DESKTOP_ALL && d >= movedesktop) {
1254 client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
1255 ob_debug("moving window %s\n", c->title);
1256 }
1257 /* raise all the windows that are on the current desktop which
1258 is being merged */
1259 if ((screen_desktop == rmdesktop - 1 ||
1260 screen_desktop == rmdesktop) &&
1261 (d == DESKTOP_ALL || d == screen_desktop))
1262 {
1263 stacking_raise(CLIENT_AS_WINDOW(c));
1264 ob_debug("raising window %s\n", c->title);
1265 }
1266 }
1267 }
1268
1269 /* act like we're changing desktops */
1270 if (screen_desktop < screen_num_desktops - 1) {
1271 gint d = screen_desktop;
1272 screen_desktop = screen_last_desktop;
1273 screen_set_desktop(d, TRUE);
1274 ob_debug("fake desktop change\n");
1275 }
1276
1277 screen_set_num_desktops(screen_num_desktops-1);
1278
1279 client_action_end(data, config_focus_under_mouse);
1280 }
This page took 0.091206 seconds and 4 git commands to generate.