1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 action.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
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.
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.
17 See the COPYING file for a copy of the GNU General Public License.
23 #include "focus_cycle.h"
24 #include "moveresize.h"
37 #include "startupnotify.h"
47 void (*func
)(union ActionData
*);
48 void (*setup
)(ObAction
**, ObUserAction uact
);
51 static ObAction
*action_new(void (*func
)(union ActionData
*data
))
53 ObAction
*a
= g_new0(ObAction
, 1);
60 void action_ref(ObAction
*a
)
65 void action_unref(ObAction
*a
)
67 if (a
== NULL
) return;
69 if (--a
->ref
> 0) return;
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
);
82 ObAction
* action_copy(const ObAction
*src
)
84 ObAction
*a
= action_new(src
->func
);
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
);
99 void setup_action_send_to_desktop(ObAction
**a
, ObUserAction uact
)
101 (*a
)->data
.sendto
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
102 (*a
)->data
.sendto
.follow
= TRUE
;
105 void setup_action_send_to_desktop_prev(ObAction
**a
, ObUserAction uact
)
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
;
115 void setup_action_send_to_desktop_next(ObAction
**a
, ObUserAction uact
)
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
;
125 void setup_action_send_to_desktop_left(ObAction
**a
, ObUserAction uact
)
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
;
135 void setup_action_send_to_desktop_right(ObAction
**a
, ObUserAction uact
)
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
;
145 void setup_action_send_to_desktop_up(ObAction
**a
, ObUserAction uact
)
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
;
155 void setup_action_send_to_desktop_down(ObAction
**a
, ObUserAction uact
)
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
;
165 void setup_action_desktop(ObAction
**a
, ObUserAction uact
)
168 (*a)->data.desktop.inter.any.interactive = FALSE;
172 void setup_action_desktop_prev(ObAction
**a
, ObUserAction uact
)
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
;
180 void setup_action_desktop_next(ObAction
**a
, ObUserAction uact
)
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
;
188 void setup_action_desktop_left(ObAction
**a
, ObUserAction uact
)
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
;
196 void setup_action_desktop_right(ObAction
**a
, ObUserAction uact
)
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
;
204 void setup_action_desktop_up(ObAction
**a
, ObUserAction uact
)
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
;
212 void setup_action_desktop_down(ObAction
**a
, ObUserAction uact
)
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
;
220 void setup_action_movefromedge_north(ObAction
**a
, ObUserAction uact
)
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
;
227 void setup_action_movefromedge_south(ObAction
**a
, ObUserAction uact
)
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
;
234 void setup_action_movefromedge_east(ObAction
**a
, ObUserAction uact
)
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
;
241 void setup_action_movefromedge_west(ObAction
**a
, ObUserAction uact
)
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
;
248 void setup_action_movetoedge_north(ObAction
**a
, ObUserAction uact
)
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
;
255 void setup_action_movetoedge_south(ObAction
**a
, ObUserAction uact
)
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
;
262 void setup_action_movetoedge_east(ObAction
**a
, ObUserAction uact
)
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
;
269 void setup_action_movetoedge_west(ObAction
**a
, ObUserAction uact
)
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
;
276 void setup_action_growtoedge_north(ObAction
**a
, ObUserAction uact
)
278 (*a
)->data
.diraction
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
279 (*a
)->data
.diraction
.direction
= OB_DIRECTION_NORTH
;
282 void setup_action_growtoedge_south(ObAction
**a
, ObUserAction uact
)
284 (*a
)->data
.diraction
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
285 (*a
)->data
.diraction
.direction
= OB_DIRECTION_SOUTH
;
288 void setup_action_growtoedge_east(ObAction
**a
, ObUserAction uact
)
290 (*a
)->data
.diraction
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
291 (*a
)->data
.diraction
.direction
= OB_DIRECTION_EAST
;
294 void setup_action_growtoedge_west(ObAction
**a
, ObUserAction uact
)
296 (*a
)->data
.diraction
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
297 (*a
)->data
.diraction
.direction
= OB_DIRECTION_WEST
;
300 void setup_action_top_layer(ObAction
**a
, ObUserAction uact
)
302 (*a
)->data
.layer
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
303 (*a
)->data
.layer
.layer
= 1;
306 void setup_action_normal_layer(ObAction
**a
, ObUserAction uact
)
308 (*a
)->data
.layer
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
309 (*a
)->data
.layer
.layer
= 0;
312 void setup_action_bottom_layer(ObAction
**a
, ObUserAction uact
)
314 (*a
)->data
.layer
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
315 (*a
)->data
.layer
.layer
= -1;
318 void setup_action_resize(ObAction
**a
, ObUserAction uact
)
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;
328 void setup_action_addremove_desktop_current(ObAction
**a
, ObUserAction uact
)
330 (*a
)->data
.addremovedesktop
.current
= TRUE
;
333 void setup_action_addremove_desktop_last(ObAction
**a
, ObUserAction uact
)
335 (*a
)->data
.addremovedesktop
.current
= FALSE
;
338 void setup_client_action(ObAction
**a
, ObUserAction uact
)
340 (*a
)->data
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
343 ActionString actionstrings
[] =
356 "resizerelativevert",
357 action_resize_relative_vert
,
362 action_resize_relative
,
367 action_send_to_desktop
,
368 setup_action_send_to_desktop
372 action_send_to_desktop_dir
,
373 setup_action_send_to_desktop_next
376 "sendtodesktopprevious",
377 action_send_to_desktop_dir
,
378 setup_action_send_to_desktop_prev
381 "sendtodesktopright",
382 action_send_to_desktop_dir
,
383 setup_action_send_to_desktop_right
387 action_send_to_desktop_dir
,
388 setup_action_send_to_desktop_left
392 action_send_to_desktop_dir
,
393 setup_action_send_to_desktop_up
397 action_send_to_desktop_dir
,
398 setup_action_send_to_desktop_down
408 setup_action_desktop_next
413 setup_action_desktop_prev
418 setup_action_desktop_right
423 setup_action_desktop_left
428 setup_action_desktop_up
433 setup_action_desktop_down
437 action_toggle_decorations
,
446 "toggledockautohide",
447 action_toggle_dockautohide
,
457 action_send_to_layer
,
458 setup_action_top_layer
463 setup_action_top_layer
467 action_send_to_layer
,
468 setup_action_normal_layer
472 action_send_to_layer
,
473 setup_action_bottom_layer
476 "togglealwaysonbottom",
478 setup_action_bottom_layer
483 setup_action_movefromedge_north
488 setup_action_movefromedge_south
493 setup_action_movefromedge_west
498 setup_action_movefromedge_east
503 setup_action_movetoedge_north
508 setup_action_movetoedge_south
513 setup_action_movetoedge_west
518 setup_action_movetoedge_east
523 setup_action_growtoedge_north
528 setup_action_growtoedge_south
533 setup_action_growtoedge_west
538 setup_action_growtoedge_east
543 setup_action_addremove_desktop_last
547 action_remove_desktop
,
548 setup_action_addremove_desktop_last
553 setup_action_addremove_desktop_current
556 "removedesktopcurrent",
557 action_remove_desktop
,
558 setup_action_addremove_desktop_current
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;
574 ObAction
*action_from_string(const gchar
*name
, ObUserAction uact
)
577 gboolean exist
= FALSE
;
580 for (i
= 0; actionstrings
[i
].name
; i
++)
581 if (!g_ascii_strcasecmp(name
, actionstrings
[i
].name
)) {
583 a
= action_new(actionstrings
[i
].func
);
584 if (actionstrings
[i
].setup
)
585 actionstrings
[i
].setup(&a
, uact
);
587 INTERACTIVE_LIMIT(a
, uact
);
591 g_message(_("Invalid action '%s' requested. No such action exists."),
594 g_message(_("Invalid use of action '%s'. Action will be ignored."),
599 ObAction
*action_parse(ObParseInst
*i
, xmlDocPtr doc
, xmlNodePtr node
,
603 ObAction
*act
= NULL
;
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
--;
622 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
623 act->data.desktop.inter.any.interactive =
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
=
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
=
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
;
675 INTERACTIVE_LIMIT(act
, uact
);
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
)
693 screen_pointer_pos(&x
, &y
);
695 for (it
= acts
; it
; it
= g_slist_next(it
)) {
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
;
704 a
->data
.any
.button
= button
;
706 a
->data
.any
.time
= time
;
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
))
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
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..
725 if (a
->data
.any
.interactive
|| a
->func
== action_move
||
726 a
->func
== action_resize
|| a
->func
== action_showmenu
)
728 /* interactive actions are not queued */
730 } else if (a
->func
== action_focus
||
731 a
->func
== action_activate
||
732 a
->func
== action_showmenu
)
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
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
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
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
764 ob_main_loop_queue_action(ob_main_loop
, a
);
769 void action_run_string(const gchar
*name
, struct _ObClient
*c
, Time time
)
774 a
= action_from_string(name
, OB_USER_ACTION_NONE
);
777 l
= g_slist_append(NULL
, a
);
779 action_run(l
, c
, 0, time
);
782 void action_unshaderaise(union ActionData
*data
)
784 if (data
->client
.any
.c
->shaded
)
785 action_unshade(data
);
790 void action_shadelower(union ActionData
*data
)
792 if (data
->client
.any
.c
->shaded
)
798 void action_resize_relative_horz(union ActionData
*data
)
800 ObClient
*c
= data
->relative
.any
.c
;
801 client_action_start(data
);
803 c
->area
.width
+ data
->relative
.deltax
* c
->size_inc
.width
,
805 client_action_end(data
, FALSE
);
808 void action_resize_relative_vert(union ActionData
*data
)
810 ObClient
*c
= data
->relative
.any
.c
;
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
);
819 void action_resize_relative(union ActionData
*data
)
821 ObClient
*c
= data
->relative
.any
.c
;
822 gint x
, y
, ow
, xoff
, nw
, oh
, yoff
, nh
, lw
, lh
;
824 client_action_start(data
);
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
;
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
;
837 g_print("deltax %d %d x %d ow %d xoff %d nw %d\n",
838 data
->relative
.deltax
,
839 data
->relative
.deltaxl
,
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
);
849 void action_send_to_desktop(union ActionData
*data
)
851 ObClient
*c
= data
->sendto
.any
.c
;
853 if (!client_normal(c
)) return;
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
);
863 void action_desktop(union ActionData
*data
)
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
)
870 screen_set_desktop(data
->desktop
.desk
, TRUE
);
871 if (data
->inter
.any
.interactive
)
872 screen_desktop_popup(data
->desktop
.desk
, TRUE
);
876 void action_desktop_dir(union ActionData
*data
)
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
))
892 if (d
!= screen_desktop
)
893 screen_set_desktop(d
, TRUE
);
897 void action_send_to_desktop_dir(union ActionData
*data
)
899 ObClient
*c
= data
->sendtodir
.inter
.any
.c
;
902 if (!client_normal(c
)) return;
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
))
915 client_set_desktop(c
, d
, data
->sendtodir
.follow
, FALSE
);
916 if (data
->sendtodir
.follow
&& d
!= screen_desktop
)
917 screen_set_desktop(d
, TRUE
);
921 void action_desktop_last(union ActionData
*data
)
923 if (screen_last_desktop
< screen_num_desktops
)
924 screen_set_desktop(screen_last_desktop
, TRUE
);
927 void action_toggle_decorations(union ActionData
*data
)
929 ObClient
*c
= data
->client
.any
.c
;
931 client_action_start(data
);
932 client_set_undecorated(c
, !c
->undecorated
);
933 client_action_end(data
, FALSE
);
936 static guint32
pick_corner(gint x
, gint y
, gint cx
, gint cy
, gint cw
, gint ch
,
939 /* let's make x and y client relative instead of screen relative */
941 y
= ch
- (y
- cy
); /* y is inverted, 0 is at the bottom of the window */
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
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)
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.
963 +---------------------A-------|-------|-------B---------------------+
970 | northwest | A north B | northeast |
973 C---------------------+----A--+-------+--B----+---------------------D
974 |CCCCCCC | A B | DDDDDDD|
975 | CCCCCCCC | A | | B | DDDDDDDD |
976 | CCCCCCC A B DDDDDDD |
977 - - - - - - - - - - - +CCCCCCC+aaaaaaa+DDDDDDD+ - - - - - - - - - - - -
979 | west | b move c | east | ad
981 - - - - - - - - - - - +EEEEEEE+ddddddd+FFFFFFF+- - - - - - - - - - - -
982 | EEEEEEE G H FFFFFFF |
983 | EEEEEEEE | G | | H | FFFFFFFF |
984 |EEEEEEE | G H | FFFFFFF|
985 E---------------------+----G--+-------+--H----+---------------------F
988 | southwest | G south H | southeast |
995 +---------------------G-------|-------|-------H---------------------+
999 /* for shaded windows, you can only resize west/east and move */
1001 return prop_atoms
.net_wm_moveresize_size_left
;
1003 return prop_atoms
.net_wm_moveresize_size_right
;
1004 return prop_atoms
.net_wm_moveresize_move
;
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
;
1024 return prop_atoms
.net_wm_moveresize_move
;
1041 void action_resize(union ActionData
*data
)
1043 ObClient
*c
= data
->moveresize
.any
.c
;
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 */
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
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
);
1062 moveresize_start(c
, data
->any
.x
, data
->any
.y
, data
->any
.button
, corner
);
1065 void action_directional_focus(union ActionData
*data
)
1067 /* if using focus_delay, stop the timer now so that focus doesn't go moving
1069 event_halt_focus_delay();
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
);
1080 void action_movetoedge(union ActionData
*data
)
1083 ObClient
*c
= data
->diraction
.any
.c
;
1085 x
= c
->frame
->area
.x
;
1086 y
= c
->frame
->area
.y
;
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);
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);
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
);
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
);
1110 g_assert_not_reached();
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
);
1118 void action_growtoedge(union ActionData
*data
)
1120 gint x
, y
, width
, height
, dest
;
1121 ObClient
*c
= data
->diraction
.any
.c
;
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
;
1131 switch(data
->diraction
.direction
) {
1132 case OB_DIRECTION_NORTH
:
1133 if (c
->shaded
) break; /* don't allow vertical resize if shaded */
1135 dest
= client_directional_edge_search(c
, OB_DIRECTION_NORTH
, FALSE
);
1137 height
= height
/ 2;
1139 height
= c
->frame
->area
.y
+ height
- dest
;
1143 case OB_DIRECTION_WEST
:
1144 dest
= client_directional_edge_search(c
, OB_DIRECTION_WEST
, FALSE
);
1148 width
= c
->frame
->area
.x
+ width
- dest
;
1152 case OB_DIRECTION_SOUTH
:
1153 if (c
->shaded
) break; /* don't allow vertical resize if shaded */
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
;
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
;
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
;
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
;
1175 g_assert_not_reached();
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
);
1186 void action_send_to_layer(union ActionData
*data
)
1188 client_set_layer(data
->layer
.any
.c
, data
->layer
.layer
);
1191 void action_toggle_layer(union ActionData
*data
)
1193 ObClient
*c
= data
->layer
.any
.c
;
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
);
1203 void action_toggle_dockautohide(union ActionData
*data
)
1205 config_dock_hide
= !config_dock_hide
;
1209 void action_add_desktop(union ActionData
*data
)
1211 client_action_start(data
);
1212 screen_set_num_desktops(screen_num_desktops
+1);
1214 /* move all the clients over */
1215 if (data
->addremovedesktop
.current
) {
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
);
1225 client_action_end(data
, config_focus_under_mouse
);
1228 void action_remove_desktop(union ActionData
*data
)
1230 guint rmdesktop
, movedesktop
;
1231 GList
*it
, *stacking_copy
;
1233 if (screen_num_desktops
< 2) return;
1235 client_action_start(data
);
1237 /* what desktop are we removing and moving to? */
1238 if (data
->addremovedesktop
.current
)
1239 rmdesktop
= screen_desktop
;
1241 rmdesktop
= screen_num_desktops
- 1;
1242 if (rmdesktop
< screen_num_desktops
- 1)
1243 movedesktop
= rmdesktop
+ 1;
1245 movedesktop
= rmdesktop
;
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
);
1257 /* raise all the windows that are on the current desktop which
1259 if ((screen_desktop
== rmdesktop
- 1 ||
1260 screen_desktop
== rmdesktop
) &&
1261 (d
== DESKTOP_ALL
|| d
== screen_desktop
))
1263 stacking_raise(CLIENT_AS_WINDOW(c
));
1264 ob_debug("raising window %s\n", c
->title
);
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");
1277 screen_set_num_desktops(screen_num_desktops
-1);
1279 client_action_end(data
, config_focus_under_mouse
);