Timo Aaltonen pushed to branch debian-unstable at X Strike Force / lib / libinput
Commits:
-
894d8fe0
by Kevin Kaland at 2019-08-11T20:02:18Z
-
a67b6523
by Ronan Pigott at 2019-08-13T22:05:35Z
-
ac007401
by Ronan Pigott at 2019-08-13T22:26:53Z
-
73d55cc6
by Matt Mayfield at 2019-08-16T04:48:52Z
-
7a12e460
by Peter Hutterer at 2019-08-26T01:38:05Z
-
b29f30ef
by Timo Aaltonen at 2019-09-07T21:09:02Z
-
5920aa7b
by Timo Aaltonen at 2019-09-07T21:09:43Z
-
7e267e20
by Timo Aaltonen at 2019-09-07T21:36:16Z
-
5028473c
by Timo Aaltonen at 2019-09-07T21:38:39Z
10 changed files:
- + completion/zsh/_libinput
- + completion/zsh/meson.build
- debian/changelog
- debian/libinput-tools.install
- meson.build
- meson_options.txt
- quirks/30-vendor-contour.quirks
- src/evdev-mt-touchpad-gestures.c
- test/test-touchpad-buttons.c
- tools/libinput-debug-events.man
Changes:
1 |
+#compdef libinput
|
|
2 |
+ |
|
3 |
+(( $+functions[_libinput_commands] )) || _libinput_commands()
|
|
4 |
+{
|
|
5 |
+ local -a commands
|
|
6 |
+ commands=(
|
|
7 |
+ "list-devices:List all devices recognized by libinput"
|
|
8 |
+ "debug-events:Print all events as seen by libinput"
|
|
9 |
+ "debug-gui:Show a GUI to visualize libinput's events"
|
|
10 |
+ "measure:Measure various properties of devices"
|
|
11 |
+ "record:Record the events from a device"
|
|
12 |
+ "replay:Replay the events from a device"
|
|
13 |
+ )
|
|
14 |
+ |
|
15 |
+ _describe -t commands 'command' commands
|
|
16 |
+}
|
|
17 |
+ |
|
18 |
+__all_seats()
|
|
19 |
+{
|
|
20 |
+ # Obviously only works with logind
|
|
21 |
+ local -a seats
|
|
22 |
+ seats=${(f)"$(loginctl --no-legend --no-pager list-seats 2>/dev/null)"}
|
|
23 |
+ if [[ -z $seats ]]; then
|
|
24 |
+ # Can always offer seat0, even if we can't enumerate the seats
|
|
25 |
+ compadd "$@" - seat0
|
|
26 |
+ else
|
|
27 |
+ compadd "$@" - $seats
|
|
28 |
+ fi
|
|
29 |
+}
|
|
30 |
+ |
|
31 |
+(( $+functions[_libinput_list-devices] )) || _libinput_list-devices()
|
|
32 |
+{
|
|
33 |
+ _arguments \
|
|
34 |
+ '--help[Show help and exit]' \
|
|
35 |
+ '--version[show version information and exit]'
|
|
36 |
+}
|
|
37 |
+ |
|
38 |
+(( $+functions[_libinput_debug-events] )) || _libinput_debug-events()
|
|
39 |
+{
|
|
40 |
+ _arguments \
|
|
41 |
+ '--help[Show debug-events help and exit]' \
|
|
42 |
+ '--quiet[Only print libinput messages and nothing from this tool]' \
|
|
43 |
+ '--verbose[Use verbose output]' \
|
|
44 |
+ '--show-keycodes[Make all keycodes visible]' \
|
|
45 |
+ '--grab[Exclusively grab all opened devices]' \
|
|
46 |
+ '--device=[Use the given device with the path backend]:device:_files -W /dev/input/ -P /dev/input/' \
|
|
47 |
+ '--udev=[Listen for notifications on the given seat]:seat:__all_seats' \
|
|
48 |
+ '--apply-to=[Apply configuration options where the device name matches the pattern]:pattern' \
|
|
49 |
+ '--disable-sendevents=[Disable send-events option for the devices matching the pattern]:pattern' \
|
|
50 |
+ '--set-click-method=[Set the desired click method]:click-method:(none clickfinger buttonareas)' \
|
|
51 |
+ '--set-scroll-method=[Set the desired scroll method]:scroll-method:(none twofinger edge button)' \
|
|
52 |
+ '--set-scroll-button=[Set the button to the given button code]' \
|
|
53 |
+ '--set-profile="" pointer acceleration profile]:accel-profile:(adaptive flat)' \
|
|
54 |
+ '--set-speed=[Set pointer acceleration speed (within range \[-1, 1\])]' \
|
|
55 |
+ '--set-tap-map=[Set button mapping for tapping]:tap-map:(( \
|
|
56 |
+ lrm\:2-fingers\ right-click\ /\ 3-fingers\ middle-click \
|
|
57 |
+ lmr\:2-fingers\ middle-click\ /\ 3-fingers\ right-click \
|
|
58 |
+ ))' \
|
|
59 |
+ + '(tap-to-click)' \
|
|
60 |
+ '--enable-tap[Enable tap-to-click]' \
|
|
61 |
+ '--disable-tap[Disable tap-to-click]' \
|
|
62 |
+ + '(drag)' \
|
|
63 |
+ '--enable-drag[Enable tap-and-drag]' \
|
|
64 |
+ '--disable-drag[Disable tap-and-drag]' \
|
|
65 |
+ + '(drag-lock)' \
|
|
66 |
+ '--enable-drag-lock[Enable drag-lock]' \
|
|
67 |
+ '--disable-drag-lock[Disable drag-lock]' \
|
|
68 |
+ + '(natural-scrolling)' \
|
|
69 |
+ '--enable-natural-scrolling[Enable natural scrolling]' \
|
|
70 |
+ '--disable-natural-scrolling[Disable natural scrolling]' \
|
|
71 |
+ + '(left-handed)' \
|
|
72 |
+ '--enable-left-handed[Enable left handed button configuration]' \
|
|
73 |
+ '--disable-left-handed[Disable left handed button configuration]' \
|
|
74 |
+ + '(middlebutton)' \
|
|
75 |
+ '--enable-middlebutton[Enable middle button emulation]' \
|
|
76 |
+ '--disable-middlebutton[Disable middle button emulation]' \
|
|
77 |
+ + '(dwt)' \
|
|
78 |
+ '--enable-dwt[Enable disable-while-typing]' \
|
|
79 |
+ '--disable-dwt[Disable enable-while-typing]'
|
|
80 |
+}
|
|
81 |
+ |
|
82 |
+(( $+functions[_libinput_debug-gui] )) || _libinput_debug-gui()
|
|
83 |
+{
|
|
84 |
+ _arguments \
|
|
85 |
+ '--help[Show debug-gui help and exit]' \
|
|
86 |
+ '--verbose[Use verbose output]' \
|
|
87 |
+ '--grab[Exclusively grab all opened devices]' \
|
|
88 |
+ '--device=[Use the given device with the path backend]:device:_files -W /dev/input/ -P /dev/input/' \
|
|
89 |
+ '--udev=[Listen for notifications on the given seat]:seat:_libinput_all_seats'
|
|
90 |
+}
|
|
91 |
+ |
|
92 |
+(( $+functions[_libinput_measure] )) || _libinput_measure()
|
|
93 |
+{
|
|
94 |
+ local curcontext=$curcontext state line ret=1
|
|
95 |
+ local features
|
|
96 |
+ features=(
|
|
97 |
+ "fuzz:Measure touch fuzz to avoid pointer jitter"
|
|
98 |
+ "touch-size:Measure touch size and orientation"
|
|
99 |
+ "touchpad-tap:Measure tap-to-click time"
|
|
100 |
+ "touchpad-pressure:Measure touch pressure"
|
|
101 |
+ )
|
|
102 |
+ |
|
103 |
+ _arguments -C \
|
|
104 |
+ '--help[Print help and exit]' \
|
|
105 |
+ ':feature:->feature' \
|
|
106 |
+ '*:: :->option-or-argument'
|
|
107 |
+ |
|
108 |
+ case $state in
|
|
109 |
+ (feature)
|
|
110 |
+ _describe -t features 'feature' features
|
|
111 |
+ ;;
|
|
112 |
+ (option-or-argument)
|
|
113 |
+ curcontext=${curcontext%:*:*}:libinput-measure-$words[1]:
|
|
114 |
+ if ! _call_function ret _libinput_measure_$words[1]; then
|
|
115 |
+ _message "unknown feature: $words[1]"
|
|
116 |
+ fi
|
|
117 |
+ ;;
|
|
118 |
+ esac
|
|
119 |
+ return ret
|
|
120 |
+}
|
|
121 |
+ |
|
122 |
+(( $+functions[_libinput_measure_fuzz] )) || _libinput_measure_fuzz()
|
|
123 |
+{
|
|
124 |
+ _arguments \
|
|
125 |
+ '--help[Show help message and exit]' \
|
|
126 |
+ ':device:_files -W /dev/input/ -P /dev/input/'
|
|
127 |
+}
|
|
128 |
+ |
|
129 |
+(( $+functions[_libinput_measure_touch-size] )) || _libinput_measure_touch-size()
|
|
130 |
+{
|
|
131 |
+ _arguments \
|
|
132 |
+ '--help[Show help message and exit]' \
|
|
133 |
+ '--touch-threshold=[Assume a touch pressure threshold of "down:up"]' \
|
|
134 |
+ '--palm-threshold=[Assume a palm threshold of N]' \
|
|
135 |
+ ':device:_files -W /dev/input/ -P /dev/input/'
|
|
136 |
+}
|
|
137 |
+ |
|
138 |
+(( $+functions[_libinput_measure_touchpad-pressure] )) || _libinput_measure_touchpad-pressure()
|
|
139 |
+{
|
|
140 |
+ _arguments \
|
|
141 |
+ '--help[Show help message and exit]' \
|
|
142 |
+ '--touch-threshold=[Assume a touch pressure threshold of "down:up"]' \
|
|
143 |
+ '--palm-threshold=[Assume a palm threshold of N]' \
|
|
144 |
+ ':device:_files -W /dev/input/ -P /dev/input/'
|
|
145 |
+}
|
|
146 |
+ |
|
147 |
+(( $+functions[_libinput_measure_touchpad-tap] )) || _libinput_measure_touchpad-tap()
|
|
148 |
+{
|
|
149 |
+ _arguments \
|
|
150 |
+ '--help[Show help message and exit]' \
|
|
151 |
+ '--format=dat[Specify the data format to be printed. The default is "summary"]'
|
|
152 |
+ ':device:_files -W /dev/input/ -P /dev/input/'
|
|
153 |
+}
|
|
154 |
+ |
|
155 |
+(( $+functions[_libinput_record] )) || _libinput_record()
|
|
156 |
+{
|
|
157 |
+ _arguments \
|
|
158 |
+ '--help[Show help message and exit]' \
|
|
159 |
+ '--all[Record all /dev/input/event* devices available on the system]' \
|
|
160 |
+ '--autorestart=[Terminate the current recording after s seconds of device inactivity]' \
|
|
161 |
+ {-o+,--output=}'[Speficy the output file to use]:file:_files -g "*.yml"' \
|
|
162 |
+ '--multiple[Record multiple devices at once]' \
|
|
163 |
+ '--show-keycodes[Show keycodes as-is in the recording]' \
|
|
164 |
+ '--with-libinput[Record libinput events alongside device events]' \
|
|
165 |
+ '*::device:_files -W /dev/input/ -P /dev/input/'
|
|
166 |
+}
|
|
167 |
+ |
|
168 |
+(( $+functions[_libinput_replay] )) || _libinput_replay()
|
|
169 |
+{
|
|
170 |
+ _arguments \
|
|
171 |
+ '--help[Show help message and exit]' \
|
|
172 |
+ ':recording:_files'
|
|
173 |
+}
|
|
174 |
+ |
|
175 |
+_libinput()
|
|
176 |
+{
|
|
177 |
+ local curcontext=$curcontext state line ret=1
|
|
178 |
+ |
|
179 |
+ _arguments -C \
|
|
180 |
+ {-h,--help}'[Show help message and exit]' \
|
|
181 |
+ '--version[Show version information and exit]' \
|
|
182 |
+ ':command:->command' \
|
|
183 |
+ '*:: :->option-or-argument' && return
|
|
184 |
+ |
|
185 |
+ case $state in
|
|
186 |
+ (command)
|
|
187 |
+ _libinput_commands && ret=0
|
|
188 |
+ ;;
|
|
189 |
+ (option-or-argument)
|
|
190 |
+ curcontext=${curcontext%:*:*}:libinput-$words[1]:
|
|
191 |
+ if ! _call_function ret _libinput_$words[1]; then
|
|
192 |
+ _message "unknown libinput command: $words[1]"
|
|
193 |
+ fi
|
|
194 |
+ ;;
|
|
195 |
+ esac
|
|
196 |
+ return ret
|
|
197 |
+}
|
|
198 |
+ |
|
199 |
+_libinput
|
1 |
+zshcompletiondir = get_option('zshcompletiondir')
|
|
2 |
+if zshcompletiondir == ''
|
|
3 |
+ zshcompletiondir = join_paths(get_option('datadir'), 'zsh', 'site-functions')
|
|
4 |
+endif
|
|
5 |
+ |
|
6 |
+if zshcompletiondir != 'no'
|
|
7 |
+ install_data(
|
|
8 |
+ '_libinput',
|
|
9 |
+ install_dir: zshcompletiondir,
|
|
10 |
+ install_mode: 'rw-r--r--',
|
|
11 |
+ )
|
|
12 |
+endif
|
1 |
+libinput (1.14.1-1) unstable; urgency=medium
|
|
2 |
+ |
|
3 |
+ * New upstream release.
|
|
4 |
+ * tools.install: Add zsh completions.
|
|
5 |
+ |
|
6 |
+ -- Timo Aaltonen <tjaalton@debian.org> Sun, 08 Sep 2019 00:37:12 +0300
|
|
7 |
+ |
|
1 | 8 |
libinput (1.14.0-1) unstable; urgency=medium
|
2 | 9 |
|
3 | 10 |
* New upstream release.
|
1 | 1 |
usr/bin/*
|
2 | 2 |
usr/libexec/libinput/*
|
3 | 3 |
usr/share/man/man1/*
|
4 |
+usr/share/zsh/site-functions/_libinput
|
1 | 1 |
project('libinput', 'c',
|
2 |
- version : '1.14.0',
|
|
2 |
+ version : '1.14.1',
|
|
3 | 3 |
license : 'MIT/Expat',
|
4 | 4 |
default_options : [ 'c_std=gnu99', 'warning_level=2' ],
|
5 | 5 |
meson_version : '>= 0.41.0')
|
... | ... | @@ -405,6 +405,10 @@ if get_option('documentation') |
405 | 405 |
subdir('doc/user')
|
406 | 406 |
endif
|
407 | 407 |
|
408 |
+############ shell completion #########
|
|
409 |
+ |
|
410 |
+subdir('completion/zsh')
|
|
411 |
+ |
|
408 | 412 |
############ tools ############
|
409 | 413 |
libinput_tool_path = dir_libexec
|
410 | 414 |
config_h.set_quoted('LIBINPUT_TOOL_PATH', libinput_tool_path)
|
... | ... | @@ -30,3 +30,7 @@ option('coverity', |
30 | 30 |
type: 'boolean',
|
31 | 31 |
value: false,
|
32 | 32 |
description: 'Enable coverity build fixes, see meson.build for details [default=false]')
|
33 |
+option('zshcompletiondir',
|
|
34 |
+ type: 'string',
|
|
35 |
+ value: '',
|
|
36 |
+ description: 'Directory for zsh completion scripts ["no" disables]')
|
... | ... | @@ -9,3 +9,9 @@ MatchVendor=0x0B33 |
9 | 9 |
MatchProduct=0x1000
|
10 | 10 |
MatchUdevType=mouse
|
11 | 11 |
ModelBouncingKeys=1
|
12 |
+ |
|
13 |
+[Contour Design RollerMouse Red v3]
|
|
14 |
+MatchVendor=0x0B33
|
|
15 |
+MatchProduct=0x1004
|
|
16 |
+MatchUdevType=mouse
|
|
17 |
+ModelBouncingKeys=1
|
... | ... | @@ -717,8 +717,13 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time) |
717 | 717 |
if (tp->gesture.finger_count == 0)
|
718 | 718 |
return;
|
719 | 719 |
|
720 |
- /* When tap-and-dragging, force 1fg mode. */
|
|
721 |
- if (tp_tap_dragging(tp)) {
|
|
720 |
+ /* When tap-and-dragging, force 1fg mode. On clickpads, if the
|
|
721 |
+ * physical button is down, don't allow gestures unless the button
|
|
722 |
+ * is held down by a *thumb*, specifically.
|
|
723 |
+ */
|
|
724 |
+ if (tp_tap_dragging(tp) ||
|
|
725 |
+ (tp->buttons.is_clickpad && tp->buttons.state &&
|
|
726 |
+ tp->thumb.state == THUMB_STATE_FINGER)) {
|
|
722 | 727 |
tp_gesture_cancel(tp, time);
|
723 | 728 |
tp->gesture.finger_count = 1;
|
724 | 729 |
tp->gesture.finger_count_pending = 0;
|
... | ... | @@ -958,6 +958,96 @@ START_TEST(touchpad_clickfinger_appletouch_3fg) |
958 | 958 |
}
|
959 | 959 |
END_TEST
|
960 | 960 |
|
961 |
+START_TEST(touchpad_clickfinger_click_drag)
|
|
962 |
+{
|
|
963 |
+ struct litest_device *dev = litest_current_device();
|
|
964 |
+ struct libinput *li = dev->libinput;
|
|
965 |
+ int nfingers = _i; /* ranged test */
|
|
966 |
+ unsigned int button;
|
|
967 |
+ int nslots = libevdev_get_num_slots(dev->evdev);
|
|
968 |
+ |
|
969 |
+ litest_enable_clickfinger(dev);
|
|
970 |
+ litest_drain_events(li);
|
|
971 |
+ |
|
972 |
+ litest_touch_down(dev, 0, 40, 50);
|
|
973 |
+ button = BTN_LEFT;
|
|
974 |
+ |
|
975 |
+ if (nfingers > 1) {
|
|
976 |
+ if (nslots > 1) {
|
|
977 |
+ litest_touch_down(dev, 1, 50, 50);
|
|
978 |
+ } else {
|
|
979 |
+ litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
|
|
980 |
+ }
|
|
981 |
+ button = BTN_RIGHT;
|
|
982 |
+ }
|
|
983 |
+ |
|
984 |
+ if (nfingers > 2) {
|
|
985 |
+ if (nslots > 2) {
|
|
986 |
+ litest_touch_down(dev, 2, 60, 50);
|
|
987 |
+ } else {
|
|
988 |
+ litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0);
|
|
989 |
+ litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 1);
|
|
990 |
+ }
|
|
991 |
+ button = BTN_MIDDLE;
|
|
992 |
+ }
|
|
993 |
+ |
|
994 |
+ litest_button_click(dev, BTN_LEFT, true);
|
|
995 |
+ |
|
996 |
+ libinput_dispatch(li);
|
|
997 |
+ litest_assert_button_event(li, button,
|
|
998 |
+ LIBINPUT_BUTTON_STATE_PRESSED);
|
|
999 |
+ |
|
1000 |
+ for (int i = 0; i < 20; i++) {
|
|
1001 |
+ litest_push_event_frame(dev);
|
|
1002 |
+ switch (nfingers) {
|
|
1003 |
+ case 3:
|
|
1004 |
+ if (nslots >= nfingers)
|
|
1005 |
+ litest_touch_move(dev, 2, 60, 50 + i);
|
|
1006 |
+ /* fallthrough */
|
|
1007 |
+ case 2:
|
|
1008 |
+ if (nslots >= nfingers)
|
|
1009 |
+ litest_touch_move(dev, 1, 50, 50 + i);
|
|
1010 |
+ /* fallthrough */
|
|
1011 |
+ case 1:
|
|
1012 |
+ litest_touch_move(dev, 0, 40, 50 + i);
|
|
1013 |
+ break;
|
|
1014 |
+ }
|
|
1015 |
+ litest_pop_event_frame(dev);
|
|
1016 |
+ libinput_dispatch(li);
|
|
1017 |
+ }
|
|
1018 |
+ |
|
1019 |
+ litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
|
|
1020 |
+ |
|
1021 |
+ litest_button_click(dev, BTN_LEFT, false);
|
|
1022 |
+ litest_assert_button_event(li, button,
|
|
1023 |
+ LIBINPUT_BUTTON_STATE_RELEASED);
|
|
1024 |
+ |
|
1025 |
+ if (nfingers > 3) {
|
|
1026 |
+ if (nslots > 3) {
|
|
1027 |
+ litest_touch_up(dev, 2);
|
|
1028 |
+ } else {
|
|
1029 |
+ litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
|
|
1030 |
+ litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 0);
|
|
1031 |
+ }
|
|
1032 |
+ }
|
|
1033 |
+ |
|
1034 |
+ if (nfingers > 2) {
|
|
1035 |
+ if (nslots > 2) {
|
|
1036 |
+ litest_touch_up(dev, 1);
|
|
1037 |
+ } else {
|
|
1038 |
+ litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0);
|
|
1039 |
+ }
|
|
1040 |
+ button = BTN_MIDDLE;
|
|
1041 |
+ }
|
|
1042 |
+ |
|
1043 |
+ litest_touch_up(dev, 0);
|
|
1044 |
+ |
|
1045 |
+ |
|
1046 |
+ libinput_dispatch(li);
|
|
1047 |
+ litest_assert_empty_queue(li);
|
|
1048 |
+}
|
|
1049 |
+END_TEST
|
|
1050 |
+ |
|
961 | 1051 |
START_TEST(touchpad_btn_left)
|
962 | 1052 |
{
|
963 | 1053 |
struct litest_device *dev = litest_current_device();
|
... | ... | @@ -2006,6 +2096,8 @@ END_TEST |
2006 | 2096 |
|
2007 | 2097 |
TEST_COLLECTION(touchpad_buttons)
|
2008 | 2098 |
{
|
2099 |
+ struct range finger_count = {1, 4};
|
|
2100 |
+ |
|
2009 | 2101 |
litest_add("touchpad:button", touchpad_button, LITEST_TOUCHPAD, LITEST_CLICKPAD);
|
2010 | 2102 |
|
2011 | 2103 |
litest_add("touchpad:clickfinger", touchpad_1fg_clickfinger, LITEST_CLICKPAD, LITEST_ANY);
|
... | ... | @@ -2036,6 +2128,8 @@ TEST_COLLECTION(touchpad_buttons) |
2036 | 2128 |
litest_add_for_device("touchpad:clickfinger", touchpad_clickfinger_appletouch_2fg, LITEST_APPLETOUCH);
|
2037 | 2129 |
litest_add_for_device("touchpad:clickfinger", touchpad_clickfinger_appletouch_3fg, LITEST_APPLETOUCH);
|
2038 | 2130 |
|
2131 |
+ litest_add_ranged("touchpad:clickfinger", touchpad_clickfinger_click_drag, LITEST_CLICKPAD, LITEST_ANY, &finger_count);
|
|
2132 |
+ |
|
2039 | 2133 |
litest_add("touchpad:click", touchpad_click_defaults_clickfinger, LITEST_APPLE_CLICKPAD, LITEST_ANY);
|
2040 | 2134 |
litest_add("touchpad:click", touchpad_click_defaults_btnarea, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD);
|
2041 | 2135 |
litest_add("touchpad:click", touchpad_click_defaults_none, LITEST_TOUCHPAD, LITEST_CLICKPAD);
|
... | ... | @@ -65,7 +65,7 @@ Enable or disable tap-to-click |
65 | 65 |
Enable or disable tap-and-drag
|
66 | 66 |
.TP 8
|
67 | 67 |
.B \-\-enable\-drag-lock|\-\-disable\-drag\-lock
|
68 |
-Enable or disable tap-and-drag
|
|
68 |
+Enable or disable drag-lock
|
|
69 | 69 |
.TP 8
|
70 | 70 |
.B \-\-enable\-natural\-scrolling|\-\-disable\-natural\-scrolling
|
71 | 71 |
Enable or disable natural scrolling
|