Add configuration option for disabling mouse warping
Patch status: merged
Patch by Atte Peltomaki
Long description:
This patch adds a new configuration option "mouse_warping [output|none]". When mouse warping is disabled, mouse cursor does not jump to middle of current screen when changing workspaces between multiple outputs. This introduces a "special" cursor state, where focus is in one window and cursor on another. Useful for eg. scrolling a web page with mouse wheel while typing into another window on keyboard.
To apply this patch, use:
curl http://cr.i3wm.org/patch/557/raw.patch | git am
b/docs/userguide
29 |
@@ -810,6 +810,26 @@ focus_follows_mouse <yes|no> |
30 |
focus_follows_mouse no |
31 |
---------------------- |
32 |
|
33 |
+=== Mouse warping |
34 |
+ |
35 |
+By default, when switching focus to a window on a different output (e.g. |
36 |
+focusing a window on workspace 3 on output VGA-1, coming from workspace 2 on |
37 |
+LVDS-1), the mouse cursor is warped to the center of that window. |
38 |
+ |
39 |
+With the +mouse_warping+ option, you can control when the mouse cursor should |
40 |
+be warped. +none+ disables warping entirely, whereas +output+ is the default |
41 |
+behavior described above. |
42 |
+ |
43 |
+*Syntax*: |
44 |
+--------------------------- |
45 |
+mouse_warping <output|none> |
46 |
+--------------------------- |
47 |
+ |
48 |
+*Example*: |
49 |
+------------------ |
50 |
+mouse_warping none |
51 |
+------------------ |
52 |
+ |
53 |
=== Popups during fullscreen mode |
54 |
|
55 |
When you are in fullscreen mode, some applications still open popup windows |
b/include/config.h
60 |
@@ -109,6 +109,16 @@ struct Config { |
61 |
* It is not planned to add any different focus models. */ |
62 |
bool disable_focus_follows_mouse; |
63 |
|
64 |
+ /** By default, when switching focus to a window on a different output |
65 |
+ * (e.g. focusing a window on workspace 3 on output VGA-1, coming from |
66 |
+ * workspace 2 on LVDS-1), the mouse cursor is warped to the center of |
67 |
+ * that window. |
68 |
+ * |
69 |
+ * With the mouse_warping option, you can control when the mouse cursor |
70 |
+ * should be warped. "none" disables warping entirely, whereas "output" |
71 |
+ * is the default behavior described above. */ |
72 |
+ warping_t mouse_warping; |
73 |
+ |
74 |
/** Remove borders if they are adjacent to the screen edge. |
75 |
* This is useful if you are reaching scrollbar on the edge of the |
76 |
* screen or do not want to waste a single pixel of displayspace. |
b/include/config_directives.h
81 |
@@ -46,6 +46,7 @@ CFGFUN(default_orientation, const char *orientation); |
82 |
CFGFUN(workspace_layout, const char *layout); |
83 |
CFGFUN(workspace_back_and_forth, const char *value); |
84 |
CFGFUN(focus_follows_mouse, const char *value); |
85 |
+CFGFUN(mouse_warping, const char *value); |
86 |
CFGFUN(force_focus_wrapping, const char *value); |
87 |
CFGFUN(force_xinerama, const char *value); |
88 |
CFGFUN(fake_outputs, const char *outputs); |
b/include/data.h
93 |
@@ -100,6 +100,14 @@ typedef enum { |
94 |
} input_type_t; |
95 |
|
96 |
/** |
97 |
+ * Mouse pointer warping modes. |
98 |
+ */ |
99 |
+typedef enum { |
100 |
+ POINTER_WARPING_NONE = 0, |
101 |
+ POINTER_WARPING_OUTPUT = 1 |
102 |
+} warping_t; |
103 |
+ |
104 |
+/** |
105 |
* Stores a rectangle, for example the size of a window, the child window etc. |
106 |
* It needs to be packed so that the compiler will not add any padding bytes. |
107 |
* (it is used in src/ewmh.c for example) |
b/parser-specs/config.spec
112 |
@@ -32,6 +32,7 @@ state INITIAL: |
113 |
'for_window' -> FOR_WINDOW |
114 |
'assign' -> ASSIGN |
115 |
'focus_follows_mouse' -> FOCUS_FOLLOWS_MOUSE |
116 |
+ 'mouse_warping' -> MOUSE_WARPING |
117 |
'force_focus_wrapping' -> FORCE_FOCUS_WRAPPING |
118 |
'force_xinerama', 'force-xinerama' -> FORCE_XINERAMA |
119 |
'workspace_auto_back_and_forth' -> WORKSPACE_BACK_AND_FORTH |
120 |
@@ -172,6 +173,11 @@ state FOCUS_FOLLOWS_MOUSE: |
121 |
value = word |
122 |
-> call cfg_focus_follows_mouse($value) |
123 |
|
124 |
+# mouse_warping warping_t |
125 |
+state MOUSE_WARPING: |
126 |
+ value = 'none', 'output' |
127 |
+ -> call cfg_mouse_warping($value) |
128 |
+ |
129 |
# force_focus_wrapping |
130 |
state FORCE_FOCUS_WRAPPING: |
131 |
value = word |
b/src/config_directives.c
136 |
@@ -298,6 +298,13 @@ CFGFUN(focus_follows_mouse, const char *value) { |
137 |
config.disable_focus_follows_mouse = !eval_boolstr(value); |
138 |
} |
139 |
|
140 |
+CFGFUN(mouse_warping, const char *value) { |
141 |
+ if (strcmp(value, "none") == 0) |
142 |
+ config.mouse_warping = POINTER_WARPING_NONE; |
143 |
+ else if (strcmp(value, "output") == 0) |
144 |
+ config.mouse_warping = POINTER_WARPING_OUTPUT; |
145 |
+} |
146 |
+ |
147 |
CFGFUN(force_xinerama, const char *value) { |
148 |
config.force_xinerama = eval_boolstr(value); |
149 |
} |
b/src/x.c
154 |
@@ -1142,7 +1142,7 @@ void x_set_i3_atoms(void) { |
155 |
*/ |
156 |
void x_set_warp_to(Rect *rect) |
157 |
{ |
158 |
- if (!config.disable_focus_follows_mouse) |
159 |
+ if (!config.disable_focus_follows_mouse && !config.mouse_warping == POINTER_WARPING_NONE) |
160 |
warp_to = rect; |
161 |
} |
162 |
|
b/testcases/t/201-config-parser.t
167 |
@@ -310,6 +310,24 @@ is(parser_calls($config), |
168 |
'focus_follows_mouse ok'); |
169 |
|
170 |
################################################################################ |
171 |
+# mouse_warping |
172 |
+################################################################################ |
173 |
+ |
174 |
+$config = <<'EOT'; |
175 |
+mouse_warping output |
176 |
+mouse_warping none |
177 |
+EOT |
178 |
+ |
179 |
+$expected = <<'EOT'; |
180 |
+cfg_mouse_warping(output) |
181 |
+cfg_mouse_warping(none) |
182 |
+EOT |
183 |
+ |
184 |
+is(parser_calls($config), |
185 |
+ $expected, |
186 |
+ 'mouse_warping ok'); |
187 |
+ |
188 |
+################################################################################ |
189 |
# force_display_urgency_hint |
190 |
################################################################################ |
191 |
|
192 |
@@ -413,7 +431,7 @@ client.focused #4c7899 #285577 #ffffff #2e9ef4 |
193 |
EOT |
194 |
|
195 |
my $expected_all_tokens = <<'EOT'; |
196 |
-ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'bindsym', 'bindcode', 'bind', 'bar', 'font', 'mode', 'floating_minimum_size', 'floating_maximum_size', 'floating_modifier', 'default_orientation', 'workspace_layout', 'new_window', 'new_float', 'hide_edge_borders', 'for_window', 'assign', 'focus_follows_mouse', 'force_focus_wrapping', 'force_xinerama', 'force-xinerama', 'workspace_auto_back_and_forth', 'fake_outputs', 'fake-outputs', 'force_display_urgency_hint', 'workspace', 'ipc_socket', 'ipc-socket', 'restart_state', 'popup_during_fullscreen', 'exec_always', 'exec', 'client.background', 'client.focused_inactive', 'client.focused', 'client.unfocused', 'client.urgent' |
197 |
+ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'bindsym', 'bindcode', 'bind', 'bar', 'font', 'mode', 'floating_minimum_size', 'floating_maximum_size', 'floating_modifier', 'default_orientation', 'workspace_layout', 'new_window', 'new_float', 'hide_edge_borders', 'for_window', 'assign', 'focus_follows_mouse', 'mouse_warping', 'force_focus_wrapping', 'force_xinerama', 'force-xinerama', 'workspace_auto_back_and_forth', 'fake_outputs', 'fake-outputs', 'force_display_urgency_hint', 'workspace', 'ipc_socket', 'ipc-socket', 'restart_state', 'popup_during_fullscreen', 'exec_always', 'exec', 'client.background', 'client.focused_inactive', 'client.focused', 'client.unfocused', 'client.urgent' |
198 |
EOT |
199 |
|
200 |
my $expected_end = <<'EOT'; |
b/testcases/t/519-mouse-warping.t
206 |
@@ -0,0 +1,52 @@ |
207 |
+#!perl |
208 |
+# vim:ts=4:sw=4:expandtab |
209 |
+# |
210 |
+# Please read the following documents before working on tests: |
211 |
+# • http://build.i3wm.org/docs/testsuite.html |
212 |
+# (or docs/testsuite) |
213 |
+# |
214 |
+# • http://build.i3wm.org/docs/lib-i3test.html |
215 |
+# (alternatively: perldoc ./testcases/lib/i3test.pm) |
216 |
+# |
217 |
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf |
218 |
+# (unless you are already familiar with Perl) |
219 |
+ |
220 |
+use i3test i3_autostart => 0; |
221 |
+ |
222 |
+# Ensure the pointer is at (0, 0) so that we really start on the first |
223 |
+# (the left) workspace. |
224 |
+$x->root->warp_pointer(0, 0); |
225 |
+ |
226 |
+my $config = <<EOT; |
227 |
+# i3 config file (v4) |
228 |
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 |
229 |
+fake-outputs 1024x768+0+0,1024x768+1024+0 |
230 |
+mouse_warping none |
231 |
+EOT |
232 |
+my $pid = launch_with_config($config); |
233 |
+ |
234 |
+my $i3 = i3(get_socket_path()); |
235 |
+ |
236 |
+###################################################### |
237 |
+# Open one workspace with one window on both outputs # |
238 |
+###################################################### |
239 |
+ |
240 |
+# Open window on workspace 1, left output |
241 |
+is(focused_ws, '1', 'starting with focus on workspace 1'); |
242 |
+open_window; |
243 |
+ |
244 |
+# Open window on workspace 2, right output |
245 |
+cmd 'focus output right'; |
246 |
+is(focused_ws, '2', 'moved focus to workspace 2'); |
247 |
+open_window; |
248 |
+ |
249 |
+# If mouse_warping is disabled, the pointer has not moved from |
250 |
+# position (0, 0) when focus was switched to workspace 2 |
251 |
+$x->root->warp_pointer(0, 0); |
252 |
+ |
253 |
+# Ensure focus is still on workspace 2 |
254 |
+is(focused_ws, '2', 'warped mouse cursor to (0, 0), focus still in workspace 2'); |
255 |
+ |
256 |
+# Exit gracefully |
257 |
+exit_gracefully($pid); |
258 |
+done_testing; |