i3 - improved tiling WM


Add configuration option for disabling mouse warping

Patch status: needinfo

Patch by Atte Peltomaki

Long description:

This patch fixes ticket #780 by adding 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/550/raw.patch | git am

b/docs/userguide

29
@@ -810,6 +810,22 @@ focus_follows_mouse <yes|no>
30
 focus_follows_mouse no
31
 ----------------------
32
 
33
+=== Focus follows mouse
34
+
35
+By default, mouse cursor is warped to center of the destination
36
+screen/workspace when switching to a different output (eg. monitor). This
37
+option allows disabling mouse cursor warping entirely.
38
+
39
+*Syntax*:
40
+---------------------------
41
+mouse_warping <output|none>
42
+---------------------------
43
+
44
+*Example*:
45
+------------------
46
+mouse_warping none
47
+------------------
48
+
49
 === Popups during fullscreen mode
50
 
51
 When you are in fullscreen mode, some applications still open popup windows

b/include/config.h

56
@@ -108,6 +108,11 @@ struct Config {
57
      * It is not planned to add any different focus models. */
58
     bool disable_focus_follows_mouse;
59
 
60
+    /** By default, mouse cursor is warped to center of the destination
61
+     * screen/workspace when switching focus to a different output.
62
+     * This option allows disabling mouse cursor warping entirely. */
63
+    bool disable_mouse_warping;
64
+
65
     /** Remove borders if they are adjacent to the screen edge.
66
      * This is useful if you are reaching scrollbar on the edge of the
67
      * screen or do not want to waste a single pixel of displayspace.

b/include/config_directives.h

72
@@ -46,6 +46,7 @@ CFGFUN(default_orientation, const char *orientation);
73
 CFGFUN(workspace_layout, const char *layout);
74
 CFGFUN(workspace_back_and_forth, const char *value);
75
 CFGFUN(focus_follows_mouse, const char *value);
76
+CFGFUN(mouse_warping, const char *value);
77
 CFGFUN(force_focus_wrapping, const char *value);
78
 CFGFUN(force_xinerama, const char *value);
79
 CFGFUN(fake_outputs, const char *outputs);

b/parser-specs/config.spec

84
@@ -32,6 +32,7 @@ state INITIAL:
85
   'for_window'                             -> FOR_WINDOW
86
   'assign'                                 -> ASSIGN
87
   'focus_follows_mouse'                    -> FOCUS_FOLLOWS_MOUSE
88
+  'mouse_warping'                          -> MOUSE_WARPING
89
   'force_focus_wrapping'                   -> FORCE_FOCUS_WRAPPING
90
   'force_xinerama', 'force-xinerama'       -> FORCE_XINERAMA
91
   'workspace_auto_back_and_forth'          -> WORKSPACE_BACK_AND_FORTH
92
@@ -172,6 +173,11 @@ state FOCUS_FOLLOWS_MOUSE:
93
   value = word
94
       -> call cfg_focus_follows_mouse($value)
95
 
96
+# mouse_warping bool
97
+state MOUSE_WARPING:
98
+  value = word
99
+      -> call cfg_mouse_warping($value)
100
+
101
 # force_focus_wrapping
102
 state FORCE_FOCUS_WRAPPING:
103
   value = word

b/src/config_directives.c

108
@@ -299,6 +299,13 @@ CFGFUN(focus_follows_mouse, const char *value) {
109
     config.disable_focus_follows_mouse = !eval_boolstr(value);
110
 }
111
 
112
+CFGFUN(mouse_warping, const char *value) {
113
+    if (strcmp(value, "none") == 0)
114
+        config.disable_mouse_warping = 1;
115
+    else if (strcmp(value, "output") == 0)
116
+        config.disable_mouse_warping = 0;
117
+}
118
+
119
 CFGFUN(force_xinerama, const char *value) {
120
     config.force_xinerama = eval_boolstr(value);
121
 }

b/src/x.c

126
@@ -1142,7 +1142,7 @@ void x_set_i3_atoms(void) {
127
  */
128
 void x_set_warp_to(Rect *rect)
129
 {
130
-    if (!config.disable_focus_follows_mouse)
131
+    if (!config.disable_focus_follows_mouse && !config.disable_mouse_warping)
132
         warp_to = rect;
133
 }
134
 

b/testcases/t/201-config-parser.t

139
@@ -310,6 +310,24 @@ is(parser_calls($config),
140
    'focus_follows_mouse ok');
141
 
142
 ################################################################################
143
+# mouse_warping
144
+################################################################################
145
+
146
+$config = <<'EOT';
147
+mouse_warping output
148
+mouse_warping none
149
+EOT
150
+
151
+$expected = <<'EOT';
152
+cfg_mouse_warping(output)
153
+cfg_mouse_warping(none)
154
+EOT
155
+
156
+is(parser_calls($config),
157
+   $expected,
158
+   'mouse_warping ok');
159
+
160
+################################################################################
161
 # force_display_urgency_hint
162
 ################################################################################
163
 
164
@@ -413,7 +431,7 @@ client.focused          #4c7899 #285577 #ffffff #2e9ef4
165
 EOT
166
 
167
 my $expected_all_tokens = <<'EOT';
168
-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'
169
+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'
170
 EOT
171
 
172
 my $expected_end = <<'EOT';

b/testcases/t/518-mouse-warping.t

178
@@ -0,0 +1,52 @@
179
+#!/perl
180
+# vim:ts=4:sw=4:expandtab
181
+#
182
+# Please read the following documents before working on tests:
183
+# • http://build.i3wm.org/docs/testsuite.html
184
+#   (or docs/testsuite)
185
+#
186
+# • http://build.i3wm.org/docs/lib-i3test.html
187
+#   (alternatively: perldoc ./testcases/lib/i3test.pm)
188
+#
189
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
190
+#   (unless you are already familiar with Perl)
191
+
192
+use i3test i3_autostart => 0;
193
+
194
+# Ensure the pointer is at (0, 0) so that we really start on the first
195
+# (the left) workspace.
196
+$x->root->warp_pointer(0, 0);
197
+
198
+my $config = <<EOT;
199
+# i3 config file (v4)
200
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
201
+fake-outputs 1024x768+0+0,1024x768+1024+0
202
+mouse_warping none
203
+EOT
204
+my $pid = launch_with_config($config);
205
+
206
+my $i3 = i3(get_socket_path());
207
+
208
+######################################################
209
+# Open one workspace with one window on both outputs #
210
+######################################################
211
+
212
+# Open window on workspace 1, left output
213
+is(focused_ws, '1', 'starting with focus on workspace 1');
214
+open_window;
215
+
216
+# Open window on workspace 2, right output
217
+cmd 'focus output right';
218
+is(focused_ws, '2', 'moved focus to workspace 2');
219
+open_window;
220
+
221
+# If mouse_warping is disabled, the pointer has not moved from
222
+# position (0, 0) when focus was switched to workspace 2
223
+$x->root->warp_pointer(0, 0);
224
+
225
+# Ensure focus is still on workspace 2
226
+is(focused_ws, '2', 'warped mouse cursor to (0, 0), focus still in workspace 2');
227
+
228
+# Exit gracefully
229
+exit_gracefully($pid);
230
+done_testing;