Bug Summary

File:dev/wscons/wstpad.c
Warning:line 1277, column 2
The result of the left shift is undefined because the left operand is negative

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name wstpad.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=all -relaxed-aliasing -ffp-contract=on -fno-rounding-math -mconstructor-aliases -ffreestanding -mcmodel=kernel -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -target-feature -sse2 -target-feature -sse -target-feature -3dnow -target-feature -mmx -target-feature +save-args -target-feature +retpoline-external-thunk -disable-red-zone -no-implicit-float -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/sys/arch/amd64/compile/GENERIC.MP/obj -nostdsysteminc -nobuiltininc -resource-dir /usr/local/llvm16/lib/clang/16 -I /usr/src/sys -I /usr/src/sys/arch/amd64/compile/GENERIC.MP/obj -I /usr/src/sys/arch -I /usr/src/sys/dev/pci/drm/include -I /usr/src/sys/dev/pci/drm/include/uapi -I /usr/src/sys/dev/pci/drm/amd/include/asic_reg -I /usr/src/sys/dev/pci/drm/amd/include -I /usr/src/sys/dev/pci/drm/amd/amdgpu -I /usr/src/sys/dev/pci/drm/amd/display -I /usr/src/sys/dev/pci/drm/amd/display/include -I /usr/src/sys/dev/pci/drm/amd/display/dc -I /usr/src/sys/dev/pci/drm/amd/display/amdgpu_dm -I /usr/src/sys/dev/pci/drm/amd/pm/inc -I /usr/src/sys/dev/pci/drm/amd/pm/legacy-dpm -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/inc -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/smu11 -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/smu12 -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/smu13 -I /usr/src/sys/dev/pci/drm/amd/pm/powerplay/inc -I /usr/src/sys/dev/pci/drm/amd/pm/powerplay/hwmgr -I /usr/src/sys/dev/pci/drm/amd/pm/powerplay/smumgr -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/inc -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/inc/pmfw_if -I /usr/src/sys/dev/pci/drm/amd/display/dc/inc -I /usr/src/sys/dev/pci/drm/amd/display/dc/inc/hw -I /usr/src/sys/dev/pci/drm/amd/display/dc/clk_mgr -I /usr/src/sys/dev/pci/drm/amd/display/modules/inc -I /usr/src/sys/dev/pci/drm/amd/display/modules/hdcp -I /usr/src/sys/dev/pci/drm/amd/display/dmub/inc -I /usr/src/sys/dev/pci/drm/i915 -D DDB -D DIAGNOSTIC -D KTRACE -D ACCOUNTING -D KMEMSTATS -D PTRACE -D POOL_DEBUG -D CRYPTO -D SYSVMSG -D SYSVSEM -D SYSVSHM -D UVM_SWAP_ENCRYPT -D FFS -D FFS2 -D FFS_SOFTUPDATES -D UFS_DIRHASH -D QUOTA -D EXT2FS -D MFS -D NFSCLIENT -D NFSSERVER -D CD9660 -D UDF -D MSDOSFS -D FIFO -D FUSE -D SOCKET_SPLICE -D TCP_ECN -D TCP_SIGNATURE -D INET6 -D IPSEC -D PPP_BSDCOMP -D PPP_DEFLATE -D PIPEX -D MROUTING -D MPLS -D BOOT_CONFIG -D USER_PCICONF -D APERTURE -D MTRR -D NTFS -D SUSPEND -D HIBERNATE -D PCIVERBOSE -D USBVERBOSE -D WSDISPLAY_COMPAT_USL -D WSDISPLAY_COMPAT_RAWKBD -D WSDISPLAY_DEFAULTSCREENS=6 -D X86EMU -D ONEWIREVERBOSE -D MULTIPROCESSOR -D MAXUSERS=80 -D _KERNEL -O2 -Wno-pointer-sign -Wno-address-of-packed-member -Wno-constant-conversion -Wno-unused-but-set-variable -Wno-gnu-folding-constant -fdebug-compilation-dir=/usr/src/sys/arch/amd64/compile/GENERIC.MP/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fcf-protection=branch -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -o /home/ben/Projects/scan/2024-01-11-110808-61670-1 -x c /usr/src/sys/dev/wscons/wstpad.c
1/* $OpenBSD: wstpad.c,v 1.33 2023/08/15 08:27:30 miod Exp $ */
2
3/*
4 * Copyright (c) 2015, 2016 Ulf Brosziewski
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19/*
20 * touchpad input processing
21 */
22
23#include <sys/param.h>
24#include <sys/kernel.h>
25#include <sys/malloc.h>
26#include <sys/proc.h>
27#include <sys/systm.h>
28#include <sys/signalvar.h>
29#include <sys/timeout.h>
30
31#include <dev/wscons/wsconsio.h>
32#include <dev/wscons/wsmousevar.h>
33#include <dev/wscons/wseventvar.h>
34#include <dev/wscons/wsmouseinput.h>
35
36#define BTNMASK(n)((n) > 0 && (n) <= 32 ? 1 << ((n) - 1) : 0
)
((n) > 0 && (n) <= 32 ? 1 << ((n) - 1) : 0)
37
38#define LEFTBTN((1) > 0 && (1) <= 32 ? 1 << ((1) - 1) : 0
)
BTNMASK(1)((1) > 0 && (1) <= 32 ? 1 << ((1) - 1) : 0
)
39#define MIDDLEBTN((2) > 0 && (2) <= 32 ? 1 << ((2) - 1) : 0
)
BTNMASK(2)((2) > 0 && (2) <= 32 ? 1 << ((2) - 1) : 0
)
40#define RIGHTBTN((3) > 0 && (3) <= 32 ? 1 << ((3) - 1) : 0
)
BTNMASK(3)((3) > 0 && (3) <= 32 ? 1 << ((3) - 1) : 0
)
41
42#define PRIMARYBTN((1) > 0 && (1) <= 32 ? 1 << ((1) - 1) : 0
)
LEFTBTN((1) > 0 && (1) <= 32 ? 1 << ((1) - 1) : 0
)
43
44#define PRIMARYBTN_CLICKED(tp)((tp)->btns_sync & ((1) > 0 && (1) <= 32
? 1 << ((1) - 1) : 0) & (tp)->btns)
((tp)->btns_sync & PRIMARYBTN((1) > 0 && (1) <= 32 ? 1 << ((1) - 1) : 0
)
& (tp)->btns)
45#define PRIMARYBTN_RELEASED(tp)((tp)->btns_sync & ((1) > 0 && (1) <= 32
? 1 << ((1) - 1) : 0) & ~(tp)->btns)
((tp)->btns_sync & PRIMARYBTN((1) > 0 && (1) <= 32 ? 1 << ((1) - 1) : 0
)
& ~(tp)->btns)
46
47#define IS_MT(tp)((tp)->features & (1U << 31)) ((tp)->features & WSTPAD_MT(1U << 31))
48#define DISABLE(tp)((tp)->features & (1 << 7)) ((tp)->features & WSTPAD_DISABLE(1 << 7))
49
50/*
51 * Ratios to the height or width of the touchpad surface, in
52 * [*.12] fixed-point format:
53 */
54#define V_EDGE_RATIO_DEFAULT205 205
55#define B_EDGE_RATIO_DEFAULT410 410
56#define T_EDGE_RATIO_DEFAULT512 512
57#define CENTER_RATIO_DEFAULT512 512
58
59#define TAP_MAXTIME_DEFAULT180 180
60#define TAP_CLICKTIME_DEFAULT180 180
61#define TAP_LOCKTIME_DEFAULT0 0
62#define TAP_BTNMAP_SIZE3 3
63
64#define CLICKDELAY_MS20 20
65#define FREEZE_MS100 100
66#define MATCHINTERVAL_MS45 45
67#define STOPINTERVAL_MS55 55
68
69#define MAG_LOW(10 << 12) (10 << 12)
70#define MAG_MEDIUM(18 << 12) (18 << 12)
71
72enum tpad_handlers {
73 SOFTBUTTON_HDLR,
74 TOPBUTTON_HDLR,
75 TAP_HDLR,
76 F2SCROLL_HDLR,
77 EDGESCROLL_HDLR,
78 CLICK_HDLR,
79};
80
81enum tap_state {
82 TAP_DETECT,
83 TAP_IGNORE,
84 TAP_LIFTED,
85 TAP_LOCKED,
86 TAP_LOCKED_DRAG,
87};
88
89enum tpad_cmd {
90 CLEAR_MOTION_DELTAS,
91 SOFTBUTTON_DOWN,
92 SOFTBUTTON_UP,
93 TAPBUTTON_SYNC,
94 TAPBUTTON_DOWN,
95 TAPBUTTON_UP,
96 VSCROLL,
97 HSCROLL,
98};
99
100/*
101 * tpad_touch.flags:
102 */
103#define L_EDGE(1 << 0) (1 << 0)
104#define R_EDGE(1 << 1) (1 << 1)
105#define T_EDGE(1 << 2) (1 << 2)
106#define B_EDGE(1 << 3) (1 << 3)
107#define THUMB(1 << 4) (1 << 4)
108
109#define EDGES((1 << 0) | (1 << 1) | (1 << 2) | (1 <<
3))
(L_EDGE(1 << 0) | R_EDGE(1 << 1) | T_EDGE(1 << 2) | B_EDGE(1 << 3))
110
111/*
112 * A touch is "centered" if it does not start and remain at the top
113 * edge or one of the vertical edges. Two-finger scrolling and tapping
114 * require that at least one touch is centered.
115 */
116#define CENTERED(t)(((t)->flags & ((1 << 0) | (1 << 1) | (1 <<
2))) == 0)
(((t)->flags & (L_EDGE(1 << 0) | R_EDGE(1 << 1) | T_EDGE(1 << 2))) == 0)
117
118enum touchstates {
119 TOUCH_NONE,
120 TOUCH_BEGIN,
121 TOUCH_UPDATE,
122 TOUCH_END,
123};
124
125struct tpad_touch {
126 u_int flags;
127 enum touchstates state;
128 int x;
129 int y;
130 int dir;
131 struct timespec start;
132 struct timespec match;
133 struct position *pos;
134 struct {
135 int x;
136 int y;
137 struct timespec time;
138 } orig;
139};
140
141/*
142 * wstpad.features
143 */
144#define WSTPAD_SOFTBUTTONS(1 << 0) (1 << 0)
145#define WSTPAD_SOFTMBTN(1 << 1) (1 << 1)
146#define WSTPAD_TOPBUTTONS(1 << 2) (1 << 2)
147#define WSTPAD_TWOFINGERSCROLL(1 << 3) (1 << 3)
148#define WSTPAD_EDGESCROLL(1 << 4) (1 << 4)
149#define WSTPAD_HORIZSCROLL(1 << 5) (1 << 5)
150#define WSTPAD_SWAPSIDES(1 << 6) (1 << 6)
151#define WSTPAD_DISABLE(1 << 7) (1 << 7)
152#define WSTPAD_MTBUTTONS(1 << 8) (1 << 8)
153
154#define WSTPAD_MT(1U << 31) (1U << 31)
155
156
157struct wstpad {
158 u_int features;
159 u_int handlers;
160
161 /*
162 * t always points into the tpad_touches array, which has at
163 * least one element. If there is more than one, t selects
164 * the pointer-controlling touch.
165 */
166 struct tpad_touch *t;
167 struct tpad_touch *tpad_touches;
168
169 u_int mtcycle;
170 u_int ignore;
171
172 int contacts;
173 int prev_contacts;
174 u_int btns;
175 u_int btns_sync;
176 int ratio;
177
178 struct timespec time;
179
180 u_int freeze;
181 struct timespec freeze_ts;
182
183 /* edge coordinates */
184 struct {
185 int left;
186 int right;
187 int top;
188 int bottom;
189 int center;
190 int center_left;
191 int center_right;
192 int low;
193 } edge;
194
195 struct {
196 /* ratios to the surface width or height */
197 int left_edge;
198 int right_edge;
199 int top_edge;
200 int bottom_edge;
201 int center_width;
202 /* two-finger contacts */
203 int f2pressure;
204 int f2width;
205 /* MTBUTTONS: distance limit for two-finger clicks */
206 int mtbtn_maxdist;
207 } params;
208
209 /* handler state and configuration: */
210
211 u_int softbutton;
212 u_int sbtnswap;
213
214 struct {
215 enum tap_state state;
216 int contacts;
217 int valid;
218 u_int pending;
219 u_int button;
220 int masked;
221 int maxdist;
222 struct timeout to;
223 /* parameters: */
224 struct timespec maxtime;
225 int clicktime;
226 int locktime;
227 u_int btnmap[TAP_BTNMAP_SIZE3];
228 } tap;
229
230 struct {
231 int dz;
232 int dw;
233 int hdist;
234 int vdist;
235 int mag;
236 } scroll;
237};
238
239static const struct timespec match_interval =
240 { .tv_sec = 0, .tv_nsec = MATCHINTERVAL_MS45 * 1000000 };
241
242static const struct timespec stop_interval =
243 { .tv_sec = 0, .tv_nsec = STOPINTERVAL_MS55 * 1000000 };
244
245/*
246 * Coordinates in the wstpad struct are "normalized" device coordinates,
247 * the orientation is left-to-right and upward.
248 */
249static inline int
250normalize_abs(struct axis_filter *filter, int val)
251{
252 return (filter->inv ? filter->inv - val : val);
253}
254
255static inline int
256normalize_rel(struct axis_filter *filter, int val)
257{
258 return (filter->inv ? -val : val);
259}
260
261/*
262 * Directions of motion are represented by numbers in the range 0 - 11,
263 * corresponding to clockwise counted circle sectors:
264 *
265 * 11 | 0
266 * 10 | 1
267 * 9 | 2
268 * -------+-------
269 * 8 | 3
270 * 7 | 4
271 * 6 | 5
272 *
273 */
274/* Tangent constants in [*.12] fixed-point format: */
275#define TAN_DEG_607094 7094
276#define TAN_DEG_302365 2365
277
278#define NORTH(d)((d) == 0 || (d) == 11) ((d) == 0 || (d) == 11)
279#define SOUTH(d)((d) == 5 || (d) == 6) ((d) == 5 || (d) == 6)
280#define EAST(d)((d) == 2 || (d) == 3) ((d) == 2 || (d) == 3)
281#define WEST(d)((d) == 8 || (d) == 9) ((d) == 8 || (d) == 9)
282
283static inline int
284direction(int dx, int dy, int ratio)
285{
286 int rdy, dir = -1;
287
288 if (dx || dy) {
289 rdy = abs(dy) * ratio;
290 if (abs(dx) * TAN_DEG_607094 < rdy)
291 dir = 0;
292 else if (abs(dx) * TAN_DEG_302365 < rdy)
293 dir = 1;
294 else
295 dir = 2;
296 if ((dx < 0) != (dy < 0))
297 dir = 5 - dir;
298 if (dx < 0)
299 dir += 6;
300 }
301 return dir;
302}
303
304static inline int
305dircmp(int dir1, int dir2)
306{
307 int diff = abs(dir1 - dir2);
308 return (diff <= 6 ? diff : 12 - diff);
309}
310
311/*
312 * Update direction and timespec attributes for a touch. They are used to
313 * determine whether it is moving - or resting - stably.
314 *
315 * The callers pass touches from the current frame and the touches that are
316 * no longer present in the update cycle to this function. Even though this
317 * ensures that pairs of zero deltas do not result from stale coordinates,
318 * zero deltas do not reset the state immediately. A short time span - the
319 * "stop interval" - must pass before the state is cleared, which is
320 * necessary because some touchpads report intermediate stops when a touch
321 * is moving very slowly.
322 */
323void
324wstpad_set_direction(struct wstpad *tp, struct tpad_touch *t, int dx, int dy)
325{
326 int dir;
327 struct timespec ts;
328
329 if (t->state != TOUCH_UPDATE) {
330 t->dir = -1;
331 memcpy(&t->start, &tp->time, sizeof(struct timespec))__builtin_memcpy((&t->start), (&tp->time), (sizeof
(struct timespec)))
;
332 return;
333 }
334
335 dir = direction(dx, dy, tp->ratio);
336 if (dir >= 0) {
337 if (t->dir < 0 || dircmp(dir, t->dir) > 1) {
338 memcpy(&t->start, &tp->time, sizeof(struct timespec))__builtin_memcpy((&t->start), (&tp->time), (sizeof
(struct timespec)))
;
339 }
340 t->dir = dir;
341 memcpy(&t->match, &tp->time, sizeof(struct timespec))__builtin_memcpy((&t->match), (&tp->time), (sizeof
(struct timespec)))
;
342 } else if (t->dir >= 0) {
343 timespecsub(&tp->time, &t->match, &ts)do { (&ts)->tv_sec = (&tp->time)->tv_sec - (
&t->match)->tv_sec; (&ts)->tv_nsec = (&tp
->time)->tv_nsec - (&t->match)->tv_nsec; if (
(&ts)->tv_nsec < 0) { (&ts)->tv_sec--; (&
ts)->tv_nsec += 1000000000L; } } while (0)
;
344 if (timespeccmp(&ts, &stop_interval, >=)(((&ts)->tv_sec == (&stop_interval)->tv_sec) ? (
(&ts)->tv_nsec >= (&stop_interval)->tv_nsec)
: ((&ts)->tv_sec >= (&stop_interval)->tv_sec
))
) {
345 t->dir = -1;
346 memcpy(&t->start, &t->match, sizeof(struct timespec))__builtin_memcpy((&t->start), (&t->match), (sizeof
(struct timespec)))
;
347 }
348 }
349}
350
351/*
352 * Make a rough, but quick estimation of the speed of a touch. Its
353 * distance to the previous position is scaled by factors derived
354 * from the average update rate and the deceleration parameter
355 * (filter.dclr). The unit of the result is:
356 * (filter.dclr / 100) device units per millisecond
357 *
358 * Magnitudes are returned in [*.12] fixed-point format. For purposes
359 * of filtering, they are divided into medium and high speeds
360 * (> MAG_MEDIUM), low speeds, and very low speeds (< MAG_LOW).
361 *
362 * The scale factors are not affected if deceleration is turned off.
363 */
364static inline int
365magnitude(struct wsmouseinput *input, int dx, int dy)
366{
367 int h, v;
368
369 h = abs(dx) * input->filter.h.mag_scale;
370 v = abs(dy) * input->filter.v.mag_scale;
371 /* Return an "alpha-max-plus-beta-min" approximation: */
372 return (h >= v ? h + 3 * v / 8 : v + 3 * h / 8);
373}
374
375/*
376 * Treat a touch as stable if it is moving at a medium or high speed,
377 * if it is moving continuously, or if it has stopped for a certain
378 * time span.
379 */
380int
381wstpad_is_stable(struct wsmouseinput *input, struct tpad_touch *t)
382{
383 struct timespec ts;
384
385 if (t->dir >= 0) {
386 if (magnitude(input, t->pos->dx, t->pos->dy) > MAG_MEDIUM(18 << 12))
387 return (1);
388 timespecsub(&t->match, &t->start, &ts)do { (&ts)->tv_sec = (&t->match)->tv_sec - (
&t->start)->tv_sec; (&ts)->tv_nsec = (&t
->match)->tv_nsec - (&t->start)->tv_nsec; if (
(&ts)->tv_nsec < 0) { (&ts)->tv_sec--; (&
ts)->tv_nsec += 1000000000L; } } while (0)
;
389 } else {
390 timespecsub(&input->tp->time, &t->start, &ts)do { (&ts)->tv_sec = (&input->tp->time)->
tv_sec - (&t->start)->tv_sec; (&ts)->tv_nsec
= (&input->tp->time)->tv_nsec - (&t->start
)->tv_nsec; if ((&ts)->tv_nsec < 0) { (&ts)->
tv_sec--; (&ts)->tv_nsec += 1000000000L; } } while (0)
;
391 }
392
393 return (timespeccmp(&ts, &match_interval, >=)(((&ts)->tv_sec == (&match_interval)->tv_sec) ?
((&ts)->tv_nsec >= (&match_interval)->tv_nsec
) : ((&ts)->tv_sec >= (&match_interval)->tv_sec
))
);
394}
395
396/*
397 * If a touch starts in an edge area, pointer movement will be
398 * suppressed as long as it stays in that area.
399 */
400static inline u_int
401edge_flags(struct wstpad *tp, int x, int y)
402{
403 u_int flags = 0;
404
405 if (x < tp->edge.left)
406 flags |= L_EDGE(1 << 0);
407 else if (x >= tp->edge.right)
408 flags |= R_EDGE(1 << 1);
409 if (y < tp->edge.bottom)
410 flags |= B_EDGE(1 << 3);
411 else if (y >= tp->edge.top)
412 flags |= T_EDGE(1 << 2);
413
414 return (flags);
415}
416
417static inline struct tpad_touch *
418get_2nd_touch(struct wsmouseinput *input)
419{
420 struct wstpad *tp = input->tp;
421 int slot;
422
423 if (IS_MT(tp)((tp)->features & (1U << 31))) {
424 slot = ffs(input->mt.touches & ~(input->mt.ptr | tp->ignore));
425 if (slot)
426 return &tp->tpad_touches[--slot];
427 }
428 return NULL((void *)0);
429}
430
431/* Suppress pointer motion for a short period of time. */
432static inline void
433set_freeze_ts(struct wstpad *tp, int sec, int ms)
434{
435 tp->freeze_ts.tv_sec = sec;
436 tp->freeze_ts.tv_nsec = ms * 1000000;
437 timespecadd(&tp->time, &tp->freeze_ts, &tp->freeze_ts)do { (&tp->freeze_ts)->tv_sec = (&tp->time)->
tv_sec + (&tp->freeze_ts)->tv_sec; (&tp->freeze_ts
)->tv_nsec = (&tp->time)->tv_nsec + (&tp->
freeze_ts)->tv_nsec; if ((&tp->freeze_ts)->tv_nsec
>= 1000000000L) { (&tp->freeze_ts)->tv_sec++; (
&tp->freeze_ts)->tv_nsec -= 1000000000L; } } while (
0)
;
438}
439
440
441/* Return TRUE if two-finger- or edge-scrolling would be valid. */
442int
443wstpad_scroll_coords(struct wsmouseinput *input, int *dx, int *dy)
444{
445 struct wstpad *tp = input->tp;
446
447 if (tp->contacts != tp->prev_contacts || tp->btns || tp->btns_sync) {
448 tp->scroll.dz = 0;
449 tp->scroll.dw = 0;
450 return (0);
451 }
452 if ((input->motion.sync & SYNC_POSITION((1 << 1) | (1 << 2))) == 0)
453 return (0);
454 /*
455 * Try to exclude accidental scroll events by checking whether the
456 * pointer-controlling touch is stable. The check, which may cause
457 * a short delay, is only applied initially, a touch that stops and
458 * resumes scrolling is not affected.
459 */
460 if (tp->scroll.dz || tp->scroll.dw || wstpad_is_stable(input, tp->t)) {
461 *dx = normalize_rel(&input->filter.h, input->motion.pos.dx);
462 *dy = normalize_rel(&input->filter.v, input->motion.pos.dy);
463 return (*dx || *dy);
464 }
465
466 return (0);
467}
468
469void
470wstpad_scroll(struct wstpad *tp, int dx, int dy, int mag, u_int *cmds)
471{
472 int dz, dw, n = 1;
473
474 /*
475 * The function applies strong deceleration, but only to input with
476 * very low speeds. A higher threshold might make applications
477 * without support for precision scrolling appear unresponsive.
478 */
479 mag = tp->scroll.mag = imin(MAG_MEDIUM(18 << 12),
480 (mag + 3 * tp->scroll.mag) / 4);
481 if (mag < MAG_LOW(10 << 12))
482 n = (MAG_LOW(10 << 12) - mag) / 4096 + 1;
483
484 if (dy && tp->scroll.vdist) {
485 if (tp->scroll.dw) {
486 /*
487 * Before switching the axis, wstpad_scroll_coords()
488 * should check again whether the movement is stable.
489 */
490 tp->scroll.dw = 0;
491 return;
492 }
493 dz = -dy * 4096 / (tp->scroll.vdist * n);
494 if (tp->scroll.dz) {
495 if ((dy < 0) != (tp->scroll.dz > 0))
496 tp->scroll.dz = -tp->scroll.dz;
497 dz = (dz + 3 * tp->scroll.dz) / 4;
498 }
499 if (dz) {
500 tp->scroll.dz = dz;
501 *cmds |= 1 << VSCROLL;
502 }
503
504 } else if (dx && tp->scroll.hdist) {
505 if (tp->scroll.dz) {
506 tp->scroll.dz = 0;
507 return;
508 }
509 dw = dx * 4096 / (tp->scroll.hdist * n);
510 if (tp->scroll.dw) {
511 if ((dx > 0) != (tp->scroll.dw > 0))
512 tp->scroll.dw = -tp->scroll.dw;
513 dw = (dw + 3 * tp->scroll.dw) / 4;
514 }
515 if (dw) {
516 tp->scroll.dw = dw;
517 *cmds |= 1 << HSCROLL;
518 }
519 }
520}
521
522void
523wstpad_f2scroll(struct wsmouseinput *input, u_int *cmds)
524{
525 struct wstpad *tp = input->tp;
526 struct tpad_touch *t2;
527 int dir, dx, dy, centered;
528
529 if (tp->ignore == 0) {
530 if (tp->contacts != 2)
531 return;
532 } else if (tp->contacts != 3 || (tp->ignore == input->mt.ptr)) {
533 return;
534 }
535
536 if (!wstpad_scroll_coords(input, &dx, &dy))
537 return;
538
539 dir = tp->t->dir;
540 if (!(NORTH(dir)((dir) == 0 || (dir) == 11) || SOUTH(dir)((dir) == 5 || (dir) == 6)))
541 dy = 0;
542 if (!(EAST(dir)((dir) == 2 || (dir) == 3) || WEST(dir)((dir) == 8 || (dir) == 9)))
543 dx = 0;
544
545 if (dx || dy) {
546 centered = CENTERED(tp->t)(((tp->t)->flags & ((1 << 0) | (1 << 1)
| (1 << 2))) == 0)
;
547 if (IS_MT(tp)((tp)->features & (1U << 31))) {
548 t2 = get_2nd_touch(input);
549 if (t2 == NULL((void *)0))
550 return;
551 dir = t2->dir;
552 if ((dy > 0 && !NORTH(dir)((dir) == 0 || (dir) == 11)) || (dy < 0 && !SOUTH(dir)((dir) == 5 || (dir) == 6)))
553 return;
554 if ((dx > 0 && !EAST(dir)((dir) == 2 || (dir) == 3)) || (dx < 0 && !WEST(dir)((dir) == 8 || (dir) == 9)))
555 return;
556 if (!wstpad_is_stable(input, t2) &&
557 !(tp->scroll.dz || tp->scroll.dw))
558 return;
559 centered |= CENTERED(t2)(((t2)->flags & ((1 << 0) | (1 << 1) | (1 <<
2))) == 0)
;
560 }
561 if (centered) {
562 wstpad_scroll(tp, dx, dy,
563 magnitude(input, dx, dy), cmds);
564 set_freeze_ts(tp, 0, FREEZE_MS100);
565 }
566 }
567}
568
569void
570wstpad_edgescroll(struct wsmouseinput *input, u_int *cmds)
571{
572 struct wstpad *tp = input->tp;
573 struct tpad_touch *t = tp->t;
574 u_int v_edge, b_edge;
575 int dx, dy;
576
577 if (!wstpad_scroll_coords(input, &dx, &dy) || tp->contacts != 1)
578 return;
579
580 v_edge = (tp->features & WSTPAD_SWAPSIDES(1 << 6)) ? L_EDGE(1 << 0) : R_EDGE(1 << 1);
581 b_edge = (tp->features & WSTPAD_HORIZSCROLL(1 << 5)) ? B_EDGE(1 << 3) : 0;
582
583 if ((t->flags & v_edge) == 0)
584 dy = 0;
585 if ((t->flags & b_edge) == 0)
586 dx = 0;
587
588 if (dx || dy)
589 wstpad_scroll(tp, dx, dy, magnitude(input, dx, dy), cmds);
590}
591
592static inline u_int
593sbtn(struct wstpad *tp, int x, int y)
594{
595 if (y >= tp->edge.bottom)
596 return (0);
597 if ((tp->features & WSTPAD_SOFTMBTN(1 << 1))
598 && x >= tp->edge.center_left
599 && x < tp->edge.center_right)
600 return (MIDDLEBTN((2) > 0 && (2) <= 32 ? 1 << ((2) - 1) : 0
)
);
601 return ((x < tp->edge.center ? LEFTBTN((1) > 0 && (1) <= 32 ? 1 << ((1) - 1) : 0
)
: RIGHTBTN((3) > 0 && (3) <= 32 ? 1 << ((3) - 1) : 0
)
) ^ tp->sbtnswap);
602}
603
604static inline u_int
605top_sbtn(struct wstpad *tp, int x, int y)
606{
607 if (y < tp->edge.top)
608 return (0);
609 if (x < tp->edge.center_left)
610 return (LEFTBTN((1) > 0 && (1) <= 32 ? 1 << ((1) - 1) : 0
)
^ tp->sbtnswap);
611 return (x > tp->edge.center_right
612 ? (RIGHTBTN((3) > 0 && (3) <= 32 ? 1 << ((3) - 1) : 0
)
^ tp->sbtnswap) : MIDDLEBTN((2) > 0 && (2) <= 32 ? 1 << ((2) - 1) : 0
)
);
613}
614
615u_int
616wstpad_get_sbtn(struct wsmouseinput *input, int top)
617{
618 struct wstpad *tp = input->tp;
619 struct tpad_touch *t = tp->t;
620 u_int btn;
621
622 btn = 0;
623 if (tp->contacts) {
624 btn = top ? top_sbtn(tp, t->x, t->y) : sbtn(tp, t->x, t->y);
625 /*
626 * If there is no middle-button area, but contacts in both
627 * halves of the edge zone, generate a middle-button event:
628 */
629 if (btn && IS_MT(tp)((tp)->features & (1U << 31)) && tp->contacts == 2
630 && !top && !(tp->features & WSTPAD_SOFTMBTN(1 << 1))) {
631 if ((t = get_2nd_touch(input)) != NULL((void *)0))
632 btn |= sbtn(tp, t->x, t->y);
633 if (btn == (LEFTBTN((1) > 0 && (1) <= 32 ? 1 << ((1) - 1) : 0
)
| RIGHTBTN((3) > 0 && (3) <= 32 ? 1 << ((3) - 1) : 0
)
))
634 btn = MIDDLEBTN((2) > 0 && (2) <= 32 ? 1 << ((2) - 1) : 0
)
;
635 }
636 }
637 return (btn != PRIMARYBTN((1) > 0 && (1) <= 32 ? 1 << ((1) - 1) : 0
)
? btn : 0);
638}
639
640int
641wstpad_mtbtn_contacts(struct wsmouseinput *input)
642{
643 struct wstpad *tp = input->tp;
644 struct tpad_touch *t;
645 int dx, dy, dist, limit;
646
647 if (tp->ignore != 0)
648 return (tp->contacts - 1);
649
650 if (tp->contacts == 2 && (t = get_2nd_touch(input)) != NULL((void *)0)) {
651 dx = abs(t->x - tp->t->x) << 12;
652 dy = abs(t->y - tp->t->y) * tp->ratio;
653 dist = (dx >= dy ? dx + 3 * dy / 8 : dy + 3 * dx / 8);
654 limit = tp->params.mtbtn_maxdist << 12;
655 if (input->mt.ptr_mask != 0)
656 limit = limit * 2 / 3;
657 if (dist > limit)
658 return (1);
659 }
660 return (tp->contacts);
661}
662
663u_int
664wstpad_get_mtbtn(struct wsmouseinput *input)
665{
666 int contacts = wstpad_mtbtn_contacts(input);
667 return (contacts == 2 ? RIGHTBTN((3) > 0 && (3) <= 32 ? 1 << ((3) - 1) : 0
)
: (contacts == 3 ? MIDDLEBTN((2) > 0 && (2) <= 32 ? 1 << ((2) - 1) : 0
)
: 0));
668}
669
670
671void
672wstpad_softbuttons(struct wsmouseinput *input, u_int *cmds, int hdlr)
673{
674 struct wstpad *tp = input->tp;
675 int top = (hdlr == TOPBUTTON_HDLR);
676
677 if (tp->softbutton && PRIMARYBTN_RELEASED(tp)((tp)->btns_sync & ((1) > 0 && (1) <= 32
? 1 << ((1) - 1) : 0) & ~(tp)->btns)
) {
678 *cmds |= 1 << SOFTBUTTON_UP;
679 return;
680 }
681
682 if (tp->softbutton == 0 && PRIMARYBTN_CLICKED(tp)((tp)->btns_sync & ((1) > 0 && (1) <= 32
? 1 << ((1) - 1) : 0) & (tp)->btns)
) {
683 tp->softbutton = ((tp->features & WSTPAD_MTBUTTONS(1 << 8))
684 ? wstpad_get_mtbtn(input) : wstpad_get_sbtn(input, top));
685 if (tp->softbutton)
686 *cmds |= 1 << SOFTBUTTON_DOWN;
687 }
688}
689
690/* Check whether the duration of t is within the tap limit. */
691int
692wstpad_is_tap(struct wstpad *tp, struct tpad_touch *t)
693{
694 struct timespec ts;
695
696 timespecsub(&tp->time, &t->orig.time, &ts)do { (&ts)->tv_sec = (&tp->time)->tv_sec - (
&t->orig.time)->tv_sec; (&ts)->tv_nsec = (&
tp->time)->tv_nsec - (&t->orig.time)->tv_nsec
; if ((&ts)->tv_nsec < 0) { (&ts)->tv_sec--;
(&ts)->tv_nsec += 1000000000L; } } while (0)
;
697 return (timespeccmp(&ts, &tp->tap.maxtime, <)(((&ts)->tv_sec == (&tp->tap.maxtime)->tv_sec
) ? ((&ts)->tv_nsec < (&tp->tap.maxtime)->
tv_nsec) : ((&ts)->tv_sec < (&tp->tap.maxtime
)->tv_sec))
);
698}
699
700/*
701 * At least one MT touch must remain close to its origin and end
702 * in the main area. The same conditions apply to one-finger taps
703 * on single-touch devices.
704 */
705void
706wstpad_tap_filter(struct wstpad *tp, struct tpad_touch *t)
707{
708 int dx, dy, dist = 0;
709
710 if (IS_MT(tp)((tp)->features & (1U << 31)) || tp->tap.contacts == 1) {
711 dx = abs(t->x - t->orig.x) << 12;
712 dy = abs(t->y - t->orig.y) * tp->ratio;
713 dist = (dx >= dy ? dx + 3 * dy / 8 : dy + 3 * dx / 8);
714 }
715 tp->tap.valid = (CENTERED(t)(((t)->flags & ((1 << 0) | (1 << 1) | (1 <<
2))) == 0)
&& dist <= (tp->tap.maxdist << 12));
716}
717
718
719/*
720 * Return the oldest touch in the TOUCH_END state, or NULL.
721 */
722struct tpad_touch *
723wstpad_tap_touch(struct wsmouseinput *input)
724{
725 struct wstpad *tp = input->tp;
726 struct tpad_touch *s, *t = NULL((void *)0);
727 u_int lifted;
728 int slot;
729
730 if (IS_MT(tp)((tp)->features & (1U << 31))) {
731 lifted = (input->mt.sync[MTS_TOUCH0] & ~input->mt.touches);
732 FOREACHBIT(lifted, slot)for ((slot) = ffs(lifted) - 1; (slot) != -1; (slot) = ffs((lifted
) & (~1 << (slot))) - 1)
{
733 s = &tp->tpad_touches[slot];
734 if (tp->tap.state == TAP_DETECT && !tp->tap.valid)
735 wstpad_tap_filter(tp, s);
736 if (t == NULL((void *)0) || timespeccmp(&t->orig.time,(((&t->orig.time)->tv_sec == (&s->orig.time)
->tv_sec) ? ((&t->orig.time)->tv_nsec > (&
s->orig.time)->tv_nsec) : ((&t->orig.time)->tv_sec
> (&s->orig.time)->tv_sec))
737 &s->orig.time, >)(((&t->orig.time)->tv_sec == (&s->orig.time)
->tv_sec) ? ((&t->orig.time)->tv_nsec > (&
s->orig.time)->tv_nsec) : ((&t->orig.time)->tv_sec
> (&s->orig.time)->tv_sec))
)
738 t = s;
739 }
740 } else {
741 if (tp->t->state == TOUCH_END) {
742 t = tp->t;
743 if (tp->tap.state == TAP_DETECT && !tp->tap.valid)
744 wstpad_tap_filter(tp, t);
745 }
746 }
747
748 return (t);
749}
750
751/* Determine the "tap button", keep track of whether a touch is masked. */
752u_int
753wstpad_tap_button(struct wstpad *tp)
754{
755 int n = tp->tap.contacts - tp->contacts - 1;
756
757 tp->tap.masked = tp->contacts;
758
759 return (n >= 0 && n < TAP_BTNMAP_SIZE3 ? tp->tap.btnmap[n] : 0);
760}
761
762/*
763 * In the hold/drag state, do not mask touches if no masking was involved
764 * in the preceding tap gesture.
765 */
766static inline int
767tap_unmask(struct wstpad *tp)
768{
769 return ((tp->tap.button || tp->tap.pending) && tp->tap.masked == 0);
770}
771
772/*
773 * In the default configuration, this handler maps one-, two-, and
774 * three-finger taps to left-button, right-button, and middle-button
775 * events, respectively. Setting the LOCKTIME parameter enables
776 * "locked drags", which are finished by a timeout or a tap-to-end
777 * gesture.
778 */
779void
780wstpad_tap(struct wsmouseinput *input, u_int *cmds)
781{
782 struct wstpad *tp = input->tp;
783 struct tpad_touch *t;
784 int contacts, is_tap, slot, err = 0;
785
786 /* Synchronize the button states, if necessary. */
787 if (input->btn.sync)
788 *cmds |= 1 << TAPBUTTON_SYNC;
789
790 /*
791 * It is possible to produce a click within the tap timeout.
792 * Wait for a new touch before generating new button events.
793 */
794 if (PRIMARYBTN_RELEASED(tp)((tp)->btns_sync & ((1) > 0 && (1) <= 32
? 1 << ((1) - 1) : 0) & ~(tp)->btns)
)
795 tp->tap.contacts = 0;
796
797 /* Reset the detection state whenever a new touch starts. */
798 if (tp->contacts > tp->prev_contacts || (IS_MT(tp)((tp)->features & (1U << 31)) &&
799 (input->mt.touches & input->mt.sync[MTS_TOUCH0]))) {
800 tp->tap.contacts = tp->contacts;
801 tp->tap.valid = 0;
802 }
803
804 /*
805 * The filtered number of active touches excludes a masked
806 * touch if its duration exceeds the tap limit.
807 */
808 contacts = tp->contacts;
809 if ((slot = ffs(input->mt.ptr_mask) - 1) >= 0
810 && !wstpad_is_tap(tp, &tp->tpad_touches[slot])
811 && !tap_unmask(tp)) {
812 contacts--;
813 }
814
815 switch (tp->tap.state) {
816 case TAP_DETECT:
817 /* Find the oldest touch in the TOUCH_END state. */
818 t = wstpad_tap_touch(input);
819 if (t) {
820 is_tap = wstpad_is_tap(tp, t);
821 if (is_tap && contacts == 0) {
822 if (tp->tap.button)
823 *cmds |= 1 << TAPBUTTON_UP;
824 tp->tap.pending = (tp->tap.valid
825 ? wstpad_tap_button(tp) : 0);
826 if (tp->tap.pending) {
827 tp->tap.state = TAP_LIFTED;
828 err = !timeout_add_msec(&tp->tap.to,
829 CLICKDELAY_MS20);
830 }
831 } else if (!is_tap && tp->tap.locktime == 0) {
832 if (contacts == 0 && tp->tap.button)
833 *cmds |= 1 << TAPBUTTON_UP;
834 else if (contacts)
835 tp->tap.state = TAP_IGNORE;
836 } else if (!is_tap && tp->tap.button) {
837 if (contacts == 0) {
838 tp->tap.state = TAP_LOCKED;
839 err = !timeout_add_msec(&tp->tap.to,
840 tp->tap.locktime);
841 } else {
842 tp->tap.state = TAP_LOCKED_DRAG;
843 }
844 }
845 }
846 break;
847 case TAP_IGNORE:
848 if (contacts == 0) {
849 tp->tap.state = TAP_DETECT;
850 if (tp->tap.button)
851 *cmds |= 1 << TAPBUTTON_UP;
852 }
853 break;
854 case TAP_LIFTED:
855 if (contacts) {
856 timeout_del(&tp->tap.to);
857 tp->tap.state = TAP_DETECT;
858 if (tp->tap.pending)
859 *cmds |= 1 << TAPBUTTON_DOWN;
860 }
861 break;
862 case TAP_LOCKED:
863 if (contacts) {
864 timeout_del(&tp->tap.to);
865 tp->tap.state = TAP_LOCKED_DRAG;
866 }
867 break;
868 case TAP_LOCKED_DRAG:
869 if (contacts == 0) {
870 t = wstpad_tap_touch(input);
871 if (t && wstpad_is_tap(tp, t)) {
872 /* "tap-to-end" */
873 *cmds |= 1 << TAPBUTTON_UP;
874 tp->tap.state = TAP_DETECT;
875 } else {
876 tp->tap.state = TAP_LOCKED;
877 err = !timeout_add_msec(&tp->tap.to,
878 tp->tap.locktime);
879 }
880 }
881 break;
882 }
883
884 if (err) { /* Did timeout_add fail? */
885 input->sbtn.buttons &= ~tp->tap.button;
886 input->sbtn.sync |= tp->tap.button;
887 tp->tap.pending = 0;
888 tp->tap.button = 0;
889 tp->tap.state = TAP_DETECT;
890 }
891}
892
893int
894wstpad_tap_sync(struct wsmouseinput *input) {
895 struct wstpad *tp = input->tp;
896
897 return ((tp->tap.button & (input->btn.buttons | tp->softbutton)) == 0
898 || (tp->tap.button == PRIMARYBTN((1) > 0 && (1) <= 32 ? 1 << ((1) - 1) : 0
)
&& tp->softbutton));
899}
900
901void
902wstpad_tap_timeout(void *p)
903{
904 struct wsmouseinput *input = p;
905 struct wstpad *tp = input->tp;
906 struct evq_access evq;
907 u_int btn;
908 int s, ev;
909
910 s = spltty()splraise(0x9);
911 evq.evar = *input->evar;
912 if (evq.evar != NULL((void *)0) && tp != NULL((void *)0)) {
913 ev = 0;
914 if (tp->tap.pending) {
915 tp->tap.button = tp->tap.pending;
916 tp->tap.pending = 0;
917 input->sbtn.buttons |= tp->tap.button;
918 timeout_add_msec(&tp->tap.to, tp->tap.clicktime);
919 if (wstpad_tap_sync(input)) {
920 ev = BTN_DOWN_EV5;
921 btn = ffs(tp->tap.button) - 1;
922 }
923 } else {
924 if (wstpad_tap_sync(input)) {
925 ev = BTN_UP_EV4;
926 btn = ffs(tp->tap.button) - 1;
927 }
928 if (tp->tap.button != tp->softbutton)
929 input->sbtn.buttons &= ~tp->tap.button;
930 tp->tap.button = 0;
931 tp->tap.state = TAP_DETECT;
932 }
933 if (ev) {
934 evq.put = evq.evar->put;
935 evq.result = EVQ_RESULT_NONE0;
936 getnanotime(&evq.ts);
937 wsmouse_evq_put(&evq, ev, btn);
938 wsmouse_evq_put(&evq, SYNC_EV18, 0);
939 if (evq.result == EVQ_RESULT_SUCCESS1) {
940 if (input->flags & LOG_EVENTS(1 << 20)) {
941 wsmouse_log_events(input, &evq);
942 }
943 evq.evar->put = evq.put;
944 WSEVENT_WAKEUP(evq.evar){ selwakeup(&(evq.evar)->sel); if ((evq.evar)->wanted
) { (evq.evar)->wanted = 0; wakeup((caddr_t)(evq.evar)); }
if ((evq.evar)->async) pgsigio(&(evq.evar)->sigio,
23, 0); }
;
945 } else {
946 input->sbtn.sync |= tp->tap.button;
947 }
948 }
949 }
950 splx(s)spllower(s);
951}
952
953/*
954 * Suppress accidental pointer movements after a click on a clickpad.
955 */
956void
957wstpad_click(struct wsmouseinput *input)
958{
959 struct wstpad *tp = input->tp;
960
961 if (tp->contacts == 1 &&
962 (PRIMARYBTN_CLICKED(tp)((tp)->btns_sync & ((1) > 0 && (1) <= 32
? 1 << ((1) - 1) : 0) & (tp)->btns)
|| PRIMARYBTN_RELEASED(tp)((tp)->btns_sync & ((1) > 0 && (1) <= 32
? 1 << ((1) - 1) : 0) & ~(tp)->btns)
))
963 set_freeze_ts(tp, 0, FREEZE_MS100);
964}
965
966/* Translate the "command" bits into the sync-state of wsmouse. */
967void
968wstpad_cmds(struct wsmouseinput *input, u_int cmds)
969{
970 struct wstpad *tp = input->tp;
971 int n;
972
973 FOREACHBIT(cmds, n)for ((n) = ffs(cmds) - 1; (n) != -1; (n) = ffs((cmds) & (
~1 << (n))) - 1)
{
974 switch (n) {
975 case CLEAR_MOTION_DELTAS:
976 input->motion.dx = input->motion.dy = 0;
977 if (input->motion.dz == 0 && input->motion.dw == 0)
978 input->motion.sync &= ~SYNC_DELTAS(1 << 0);
979 continue;
980 case SOFTBUTTON_DOWN:
981 input->btn.sync &= ~PRIMARYBTN((1) > 0 && (1) <= 32 ? 1 << ((1) - 1) : 0
)
;
982 input->sbtn.buttons |= tp->softbutton;
983 if (tp->softbutton != tp->tap.button)
984 input->sbtn.sync |= tp->softbutton;
985 continue;
986 case SOFTBUTTON_UP:
987 input->btn.sync &= ~PRIMARYBTN((1) > 0 && (1) <= 32 ? 1 << ((1) - 1) : 0
)
;
988 if (tp->softbutton != tp->tap.button) {
989 input->sbtn.buttons &= ~tp->softbutton;
990 input->sbtn.sync |= tp->softbutton;
991 }
992 tp->softbutton = 0;
993 continue;
994 case TAPBUTTON_SYNC:
995 if (tp->tap.button)
996 input->btn.sync &= ~tp->tap.button;
997 continue;
998 case TAPBUTTON_DOWN:
999 tp->tap.button = tp->tap.pending;
1000 tp->tap.pending = 0;
1001 input->sbtn.buttons |= tp->tap.button;
1002 if (wstpad_tap_sync(input))
1003 input->sbtn.sync |= tp->tap.button;
1004 continue;
1005 case TAPBUTTON_UP:
1006 if (tp->tap.button != tp->softbutton)
1007 input->sbtn.buttons &= ~tp->tap.button;
1008 if (wstpad_tap_sync(input))
1009 input->sbtn.sync |= tp->tap.button;
1010 tp->tap.button = 0;
1011 continue;
1012 case HSCROLL:
1013 input->motion.dw = tp->scroll.dw;
1014 input->motion.sync |= SYNC_DELTAS(1 << 0);
1015 continue;
1016 case VSCROLL:
1017 input->motion.dz = tp->scroll.dz;
1018 input->motion.sync |= SYNC_DELTAS(1 << 0);
1019 continue;
1020 default:
1021 printf("[wstpad] invalid cmd %d\n", n);
1022 break;
1023 }
1024 }
1025}
1026
1027
1028/*
1029 * Set the state of touches that have ended. TOUCH_END is a transitional
1030 * state and will be changed to TOUCH_NONE before process_input() returns.
1031 */
1032static inline void
1033clear_touchstates(struct wsmouseinput *input, enum touchstates state)
1034{
1035 u_int touches;
1036 int slot;
1037
1038 touches = input->mt.sync[MTS_TOUCH0] & ~input->mt.touches;
1039 FOREACHBIT(touches, slot)for ((slot) = ffs(touches) - 1; (slot) != -1; (slot) = ffs((touches
) & (~1 << (slot))) - 1)
1040 input->tp->tpad_touches[slot].state = state;
1041}
1042
1043void
1044wstpad_mt_inputs(struct wsmouseinput *input)
1045{
1046 struct wstpad *tp = input->tp;
1047 struct tpad_touch *t;
1048 int slot, dx, dy;
1049 u_int touches, inactive;
1050
1051 /* TOUCH_BEGIN */
1052 touches = input->mt.touches & input->mt.sync[MTS_TOUCH0];
1053 FOREACHBIT(touches, slot)for ((slot) = ffs(touches) - 1; (slot) != -1; (slot) = ffs((touches
) & (~1 << (slot))) - 1)
{
1054 t = &tp->tpad_touches[slot];
1055 t->state = TOUCH_BEGIN;
1056 t->x = normalize_abs(&input->filter.h, t->pos->x);
1057 t->y = normalize_abs(&input->filter.v, t->pos->y);
1058 t->orig.x = t->x;
1059 t->orig.y = t->y;
1060 memcpy(&t->orig.time, &tp->time, sizeof(struct timespec))__builtin_memcpy((&t->orig.time), (&tp->time), (
sizeof(struct timespec)))
;
1061 t->flags = edge_flags(tp, t->x, t->y);
1062 wstpad_set_direction(tp, t, 0, 0);
1063 }
1064
1065 /* TOUCH_UPDATE */
1066 touches = input->mt.touches & input->mt.frame;
1067 if (touches & tp->mtcycle) {
1068 /*
1069 * Slot data may be synchronized separately, in any order,
1070 * or not at all if there is no delta. Identify the touches
1071 * without deltas.
1072 */
1073 inactive = input->mt.touches & ~tp->mtcycle;
1074 tp->mtcycle = touches;
1075 } else {
1076 inactive = 0;
1077 tp->mtcycle |= touches;
1078 }
1079 touches = input->mt.touches & ~input->mt.sync[MTS_TOUCH0];
1080 FOREACHBIT(touches, slot)for ((slot) = ffs(touches) - 1; (slot) != -1; (slot) = ffs((touches
) & (~1 << (slot))) - 1)
{
1081 t = &tp->tpad_touches[slot];
1082 t->state = TOUCH_UPDATE;
1083 if ((1 << slot) & input->mt.frame) {
1084 dx = normalize_abs(&input->filter.h, t->pos->x) - t->x;
1085 t->x += dx;
1086 dy = normalize_abs(&input->filter.v, t->pos->y) - t->y;
1087 t->y += dy;
1088 t->flags &= (~EDGES((1 << 0) | (1 << 1) | (1 << 2) | (1 <<
3))
| edge_flags(tp, t->x, t->y));
1089 if (wsmouse_hysteresis(input, t->pos))
1090 dx = dy = 0;
1091 wstpad_set_direction(tp, t, dx, dy);
1092 } else if ((1 << slot) & inactive) {
1093 wstpad_set_direction(tp, t, 0, 0);
1094 }
1095 }
1096
1097 clear_touchstates(input, TOUCH_END);
1098}
1099
1100/*
1101 * Identify "thumb" contacts in the bottom area. The identification
1102 * has three stages:
1103 * 1. If exactly one of two or more touches is in the bottom area, it
1104 * is masked, which means it does not receive pointer control as long
1105 * as there are alternatives. Once set, the mask will only be cleared
1106 * when the touch is released.
1107 * Tap detection ignores a masked touch if it does not participate in
1108 * a tap gesture.
1109 * 2. If the pointer-controlling touch is moving stably while a masked
1110 * touch in the bottom area is resting, or only moving minimally, the
1111 * pointer mask is copied to tp->ignore. In this stage, the masked
1112 * touch does not block pointer movement, and it is ignored by
1113 * wstpad_f2scroll().
1114 * Decisions are made more or less immediately, there may be errors
1115 * in edge cases. If a fast or long upward movement is detected,
1116 * tp->ignore is cleared. There is no other transition from stage 2
1117 * to scrolling, or vice versa, for a pair of touches.
1118 * 3. If tp->ignore is set and the touch is resting, it is marked as
1119 * thumb, and it will be ignored until it ends.
1120 */
1121void
1122wstpad_mt_masks(struct wsmouseinput *input)
1123{
1124 struct wstpad *tp = input->tp;
1125 struct tpad_touch *t;
1126 struct position *pos;
1127 u_int mask;
1128 int slot;
1129
1130 tp->ignore &= input->mt.touches;
1131
1132 if (tp->contacts < 2)
1133 return;
1134
1135 if (tp->ignore) {
1136 slot = ffs(tp->ignore) - 1;
1137 t = &tp->tpad_touches[slot];
1138 if (t->flags & THUMB(1 << 4))
1139 return;
1140 if (t->dir < 0 && wstpad_is_stable(input, t)) {
1141 t->flags |= THUMB(1 << 4);
1142 return;
1143 }
1144 /* The edge.low area is a bit larger than the bottom area. */
1145 if (t->y >= tp->edge.low || (NORTH(t->dir)((t->dir) == 0 || (t->dir) == 11) &&
1146 magnitude(input, t->pos->dx, t->pos->dy) >= MAG_MEDIUM(18 << 12)))
1147 tp->ignore = 0;
1148 return;
1149 }
1150
1151 if (input->mt.ptr_mask == 0) {
1152 mask = ~0;
1153 FOREACHBIT(input->mt.touches, slot)for ((slot) = ffs(input->mt.touches) - 1; (slot) != -1; (slot
) = ffs((input->mt.touches) & (~1 << (slot))) - 1
)
{
1154 t = &tp->tpad_touches[slot];
1155 if (t->flags & B_EDGE(1 << 3)) {
1156 mask &= (1 << slot);
1157 input->mt.ptr_mask = mask;
1158 }
1159 }
1160 }
1161
1162 if ((input->mt.ptr_mask & ~input->mt.ptr)
1163 && !(tp->scroll.dz || tp->scroll.dw)
1164 && tp->t->dir >= 0
1165 && wstpad_is_stable(input, tp->t)) {
1166
1167 slot = ffs(input->mt.ptr_mask) - 1;
1168 t = &tp->tpad_touches[slot];
1169
1170 if (t->y >= tp->edge.low)
1171 return;
1172
1173 if (!wstpad_is_stable(input, t))
1174 return;
1175
1176 /* Default hysteresis limits are low. Make a strict check. */
1177 pos = tp->t->pos;
1178 if (abs(pos->acc_dx) < 3 * input->filter.h.hysteresis
1179 && abs(pos->acc_dy) < 3 * input->filter.v.hysteresis)
1180 return;
1181
1182 if (t->dir >= 0) {
1183 /* Treat t as thumb if it is slow while tp->t is fast. */
1184 if (magnitude(input, t->pos->dx, t->pos->dy) > MAG_LOW(10 << 12)
1185 || magnitude(input, pos->dx, pos->dy) < MAG_MEDIUM(18 << 12))
1186 return;
1187 }
1188
1189 tp->ignore = input->mt.ptr_mask;
1190 }
1191}
1192
1193void
1194wstpad_touch_inputs(struct wsmouseinput *input)
1195{
1196 struct wstpad *tp = input->tp;
1197 struct tpad_touch *t;
1198 int slot, x, y, dx, dy;
1199
1200 tp->btns = input->btn.buttons;
1201 tp->btns_sync = input->btn.sync;
1202
1203 tp->prev_contacts = tp->contacts;
1204 tp->contacts = input->touch.contacts;
1205
1206 if (tp->contacts == 1 &&
1207 ((tp->params.f2width &&
1208 input->touch.width >= tp->params.f2width)
1209 || (tp->params.f2pressure &&
1210 input->touch.pressure >= tp->params.f2pressure)))
1211 tp->contacts = 2;
1212
1213 if (IS_MT(tp)((tp)->features & (1U << 31))) {
1214 wstpad_mt_inputs(input);
1215 if (input->mt.ptr) {
1216 slot = ffs(input->mt.ptr) - 1;
1217 tp->t = &tp->tpad_touches[slot];
1218 }
1219 wstpad_mt_masks(input);
1220 } else {
1221 t = tp->t;
1222 if (tp->contacts)
1223 t->state = (tp->prev_contacts ?
1224 TOUCH_UPDATE : TOUCH_BEGIN);
1225 else
1226 t->state = (tp->prev_contacts ?
1227 TOUCH_END : TOUCH_NONE);
1228
1229 dx = dy = 0;
1230 x = normalize_abs(&input->filter.h, t->pos->x);
1231 y = normalize_abs(&input->filter.v, t->pos->y);
1232 if (t->state == TOUCH_BEGIN) {
1233 t->x = t->orig.x = x;
1234 t->y = t->orig.y = y;
1235 memcpy(&t->orig.time, &tp->time,__builtin_memcpy((&t->orig.time), (&tp->time), (
sizeof(struct timespec)))
1236 sizeof(struct timespec))__builtin_memcpy((&t->orig.time), (&tp->time), (
sizeof(struct timespec)))
;
1237 t->flags = edge_flags(tp, x, y);
1238 } else if (input->motion.sync & SYNC_POSITION((1 << 1) | (1 << 2))) {
1239 if (!wsmouse_hysteresis(input, t->pos)) {
1240 dx = x - t->x;
1241 dy = y - t->y;
1242 }
1243 t->x = x;
1244 t->y = y;
1245 t->flags &= (~EDGES((1 << 0) | (1 << 1) | (1 << 2) | (1 <<
3))
| edge_flags(tp, x, y));
1246 }
1247 wstpad_set_direction(tp, t, dx, dy);
1248 }
1249}
1250
1251static inline int
1252t2_ignore(struct wsmouseinput *input)
1253{
1254 /*
1255 * If there are two touches, do not block pointer movement if they
1256 * perform a click-and-drag action, or if the second touch is
1257 * resting in the bottom area.
1258 */
1259 return (input->tp->contacts == 2 && ((input->tp->btns & PRIMARYBTN((1) > 0 && (1) <= 32 ? 1 << ((1) - 1) : 0
)
)
1260 || (input->tp->ignore & ~input->mt.ptr)));
1261}
1262
1263void
1264wstpad_process_input(struct wsmouseinput *input, struct evq_access *evq)
1265{
1266 struct wstpad *tp = input->tp;
1267 u_int handlers, hdlr, cmds;
1268
1269 memcpy(&tp->time, &evq->ts, sizeof(struct timespec))__builtin_memcpy((&tp->time), (&evq->ts), (sizeof
(struct timespec)))
;
1270 wstpad_touch_inputs(input);
1271
1272 cmds = 0;
1273 handlers = tp->handlers;
1274 if (DISABLE(tp)((tp)->features & (1 << 7)))
8
Assuming the condition is false
9
Taking false branch
1275 handlers &= ((1 << TOPBUTTON_HDLR) | (1 << SOFTBUTTON_HDLR));
1276
1277 FOREACHBIT(handlers, hdlr)for ((hdlr) = ffs(handlers) - 1; (hdlr) != -1; (hdlr) = ffs((
handlers) & (~1 << (hdlr))) - 1)
{
10
Assuming the condition is true
11
Loop condition is true. Entering loop body
13
Execution continues on line 1277
14
The result of the left shift is undefined because the left operand is negative
1278 switch (hdlr) {
12
Control jumps to 'case CLICK_HDLR:' at line 1292
1279 case SOFTBUTTON_HDLR:
1280 case TOPBUTTON_HDLR:
1281 wstpad_softbuttons(input, &cmds, hdlr);
1282 continue;
1283 case TAP_HDLR:
1284 wstpad_tap(input, &cmds);
1285 continue;
1286 case F2SCROLL_HDLR:
1287 wstpad_f2scroll(input, &cmds);
1288 continue;
1289 case EDGESCROLL_HDLR:
1290 wstpad_edgescroll(input, &cmds);
1291 continue;
1292 case CLICK_HDLR:
1293 wstpad_click(input);
1294 continue;
1295 }
1296 }
1297
1298 /* Check whether pointer movement should be blocked. */
1299 if (input->motion.dx || input->motion.dy) {
1300 if (DISABLE(tp)((tp)->features & (1 << 7))
1301 || (tp->t->flags & tp->freeze)
1302 || timespeccmp(&tp->time, &tp->freeze_ts, <)(((&tp->time)->tv_sec == (&tp->freeze_ts)->
tv_sec) ? ((&tp->time)->tv_nsec < (&tp->freeze_ts
)->tv_nsec) : ((&tp->time)->tv_sec < (&tp
->freeze_ts)->tv_sec))
1303 || (tp->contacts > 1 && !t2_ignore(input))) {
1304
1305 cmds |= 1 << CLEAR_MOTION_DELTAS;
1306 }
1307 }
1308
1309 wstpad_cmds(input, cmds);
1310
1311 if (IS_MT(tp)((tp)->features & (1U << 31)))
1312 clear_touchstates(input, TOUCH_NONE);
1313}
1314
1315/*
1316 * Try to determine the average interval between two updates. Various
1317 * conditions are checked in order to ensure that only valid samples enter
1318 * into the calculation. Above all, it is restricted to motion events
1319 * occurring when there is only one contact. MT devices may need more than
1320 * one packet to transmit their state if there are multiple touches, and
1321 * the update frequency may be higher in this case.
1322 */
1323void
1324wstpad_track_interval(struct wsmouseinput *input, struct timespec *time)
1325{
1326 static const struct timespec limit = { 0, 30 * 1000000L };
1327 struct timespec ts;
1328 int samples;
1329
1330 if (input->motion.sync == 0
1331 || (input->touch.sync & SYNC_CONTACTS(1 << 1))
1332 || (input->touch.contacts > 1)) {
1333 input->intv.track = 0;
1334 return;
1335 }
1336 if (input->intv.track) {
1337 timespecsub(time, &input->intv.ts, &ts)do { (&ts)->tv_sec = (time)->tv_sec - (&input->
intv.ts)->tv_sec; (&ts)->tv_nsec = (time)->tv_nsec
- (&input->intv.ts)->tv_nsec; if ((&ts)->tv_nsec
< 0) { (&ts)->tv_sec--; (&ts)->tv_nsec += 1000000000L
; } } while (0)
;
1338 if (timespeccmp(&ts, &limit, <)(((&ts)->tv_sec == (&limit)->tv_sec) ? ((&ts
)->tv_nsec < (&limit)->tv_nsec) : ((&ts)->
tv_sec < (&limit)->tv_sec))
) {
1339 /* The unit of the sum is 4096 nanoseconds. */
1340 input->intv.sum += ts.tv_nsec >> 12;
1341 samples = ++input->intv.samples;
1342 /*
1343 * Make the first calculation quickly and later
1344 * a more reliable one:
1345 */
1346 if (samples == 8) {
1347 input->intv.avg = input->intv.sum << 9;
1348 wstpad_init_deceleration(input);
1349 } else if (samples == 128) {
1350 input->intv.avg = input->intv.sum << 5;
1351 wstpad_init_deceleration(input);
1352 input->intv.samples = 0;
1353 input->intv.sum = 0;
1354 input->flags &= ~TRACK_INTERVAL(1 << 17);
1355 }
1356 }
1357 }
1358 memcpy(&input->intv.ts, time, sizeof(struct timespec))__builtin_memcpy((&input->intv.ts), (time), (sizeof(struct
timespec)))
;
1359 input->intv.track = 1;
1360}
1361
1362
1363
1364/*
1365 * The default acceleration options of X don't work convincingly with
1366 * touchpads (the synaptics driver installs its own "acceleration
1367 * profile" and callback function). As a preliminary workaround, this
1368 * filter applies a simple deceleration scheme to small deltas, based
1369 * on the "magnitude" of the delta pair. A magnitude of 8 corresponds,
1370 * roughly, to a speed of (filter.dclr / 12.5) device units per milli-
1371 * second. If its magnitude is smaller than 7 a delta will be downscaled
1372 * by the factor 2/8, deltas with magnitudes from 7 to 11 by factors
1373 * ranging from 3/8 to 7/8.
1374 */
1375int
1376wstpad_decelerate(struct wsmouseinput *input, int *dx, int *dy)
1377{
1378 int mag, n, h, v;
1379
1380 mag = magnitude(input, *dx, *dy);
1381
1382 /* Don't change deceleration levels abruptly. */
1383 mag = (mag + 7 * input->filter.mag) / 8;
1384 /* Don't use arbitrarily high values. */
1385 input->filter.mag = imin(mag, 24 << 12);
1386
1387 n = imax((mag >> 12) - 4, 2);
1388 if (n < 8) {
1389 /* Scale by (n / 8). */
1390 h = *dx * n + input->filter.h.dclr_rmdr;
1391 v = *dy * n + input->filter.v.dclr_rmdr;
1392 input->filter.h.dclr_rmdr = (h >= 0 ? h & 7 : -(-h & 7));
1393 input->filter.v.dclr_rmdr = (v >= 0 ? v & 7 : -(-v & 7));
1394 *dx = h / 8;
1395 *dy = v / 8;
1396 return (1);
1397 }
1398 return (0);
1399}
1400
1401void
1402wstpad_filter(struct wsmouseinput *input)
1403{
1404 struct axis_filter *h = &input->filter.h;
1405 struct axis_filter *v = &input->filter.v;
1406 struct position *pos = &input->motion.pos;
1407 int strength = input->filter.mode & 7;
1408 int dx, dy;
1409
1410 if (!(input->motion.sync & SYNC_POSITION((1 << 1) | (1 << 2)))
1411 || (h->dmax && (abs(pos->dx) > h->dmax))
1412 || (v->dmax && (abs(pos->dy) > v->dmax))) {
1413 dx = dy = 0;
1414 } else {
1415 dx = pos->dx;
1416 dy = pos->dy;
1417 }
1418
1419 if (wsmouse_hysteresis(input, pos))
1420 dx = dy = 0;
1421
1422 if (input->filter.dclr && wstpad_decelerate(input, &dx, &dy))
1423 /* Strong smoothing may hamper the precision at low speeds. */
1424 strength = imin(strength, 2);
1425
1426 if (strength) {
1427 if ((input->touch.sync & SYNC_CONTACTS(1 << 1))
1428 || input->mt.ptr != input->mt.prev_ptr) {
1429 h->avg = v->avg = 0;
1430 }
1431 /* Use a weighted decaying average for smoothing. */
1432 dx = dx * (8 - strength) + h->avg * strength + h->avg_rmdr;
1433 dy = dy * (8 - strength) + v->avg * strength + v->avg_rmdr;
1434 h->avg_rmdr = (dx >= 0 ? dx & 7 : -(-dx & 7));
1435 v->avg_rmdr = (dy >= 0 ? dy & 7 : -(-dy & 7));
1436 dx = h->avg = dx / 8;
1437 dy = v->avg = dy / 8;
1438 }
1439
1440 input->motion.dx = dx;
1441 input->motion.dy = dy;
1442}
1443
1444
1445/*
1446 * Compatibility-mode conversions. wstpad_filter transforms and filters
1447 * the coordinate inputs, extended functionality is provided by
1448 * wstpad_process_input.
1449 */
1450void
1451wstpad_compat_convert(struct wsmouseinput *input, struct evq_access *evq)
1452{
1453 if (input->flags & TRACK_INTERVAL(1 << 17))
1
Assuming the condition is false
2
Taking false branch
1454 wstpad_track_interval(input, &evq->ts);
1455
1456 wstpad_filter(input);
1457
1458 if ((input->motion.dx || input->motion.dy)
3
Assuming field 'dx' is 0
4
Assuming field 'dy' is 0
1459 && !(input->motion.sync & SYNC_DELTAS(1 << 0))) {
1460 input->motion.dz = input->motion.dw = 0;
1461 input->motion.sync |= SYNC_DELTAS(1 << 0);
1462 }
1463
1464 if (input->tp != NULL((void *)0))
5
Assuming field 'tp' is not equal to NULL
6
Taking true branch
1465 wstpad_process_input(input, evq);
7
Calling 'wstpad_process_input'
1466
1467 input->motion.sync &= ~SYNC_POSITION((1 << 1) | (1 << 2));
1468 input->touch.sync = 0;
1469}
1470
1471int
1472wstpad_init(struct wsmouseinput *input)
1473{
1474 struct wstpad *tp = input->tp;
1475 int i, slots;
1476
1477 if (tp != NULL((void *)0))
1478 return (0);
1479
1480 input->tp = tp = malloc(sizeof(struct wstpad),
1481 M_DEVBUF2, M_WAITOK0x0001 | M_ZERO0x0008);
1482 if (tp == NULL((void *)0))
1483 return (-1);
1484
1485 slots = imax(input->mt.num_slots, 1);
1486 tp->tpad_touches = malloc(slots * sizeof(struct tpad_touch),
1487 M_DEVBUF2, M_WAITOK0x0001 | M_ZERO0x0008);
1488 if (tp->tpad_touches == NULL((void *)0)) {
1489 free(tp, M_DEVBUF2, sizeof(struct wstpad));
1490 return (-1);
1491 }
1492
1493 tp->t = &tp->tpad_touches[0];
1494 if (input->mt.num_slots) {
1495 tp->features |= WSTPAD_MT(1U << 31);
1496 for (i = 0; i < input->mt.num_slots; i++)
1497 tp->tpad_touches[i].pos = &input->mt.slots[i].pos;
1498 } else {
1499 tp->t->pos = &input->motion.pos;
1500 }
1501
1502 timeout_set(&tp->tap.to, wstpad_tap_timeout, input);
1503
1504 tp->ratio = input->filter.ratio;
1505
1506 return (0);
1507}
1508
1509/*
1510 * Integer square root (Halleck's method)
1511 *
1512 * An adaption of code from John B. Halleck (from
1513 * http://www.cc.utah.edu/~nahaj/factoring/code.html). This version is
1514 * used and published under the OpenBSD license terms with his permission.
1515 *
1516 * Cf. also Martin Guy's "Square root by abacus" method.
1517 */
1518static inline u_int
1519isqrt(u_int n)
1520{
1521 u_int root, sqbit;
1522
1523 root = 0;
1524 sqbit = 1 << (sizeof(u_int) * 8 - 2);
1525 while (sqbit) {
1526 if (n >= (sqbit | root)) {
1527 n -= (sqbit | root);
1528 root = (root >> 1) | sqbit;
1529 } else {
1530 root >>= 1;
1531 }
1532 sqbit >>= 2;
1533 }
1534 return (root);
1535}
1536
1537void
1538wstpad_init_deceleration(struct wsmouseinput *input)
1539{
1540 int n, dclr;
1541
1542 if ((dclr = input->filter.dclr) == 0)
1543 return;
1544
1545 dclr = imax(dclr, 4);
1546
1547 /*
1548 * For a standard update rate of about 80Hz, (dclr) units
1549 * will be mapped to a magnitude of 8. If the average rate
1550 * is significantly higher or lower, adjust the coefficient
1551 * accordingly:
1552 */
1553 if (input->intv.avg == 0) {
1554 n = 8;
1555 } else {
1556 n = 8 * 13000000 / input->intv.avg;
1557 n = imax(imin(n, 32), 4);
1558 }
1559 input->filter.h.mag_scale = (n << 12) / dclr;
1560 input->filter.v.mag_scale = (input->filter.ratio ?
1561 n * input->filter.ratio : n << 12) / dclr;
1562 input->filter.h.dclr_rmdr = 0;
1563 input->filter.v.dclr_rmdr = 0;
1564 input->flags |= TRACK_INTERVAL(1 << 17);
1565}
1566
1567int
1568wstpad_configure(struct wsmouseinput *input)
1569{
1570 struct wstpad *tp;
1571 int width, height, diag, offset, h_res, v_res, h_unit, v_unit, i;
1572
1573 width = abs(input->hw.x_max - input->hw.x_min);
1574 height = abs(input->hw.y_max - input->hw.y_min);
1575 if (width == 0 || height == 0)
1576 return (-1); /* We can't do anything. */
1577
1578 if (input->tp == NULL((void *)0) && wstpad_init(input))
1579 return (-1);
1580 tp = input->tp;
1581
1582 if (!(input->flags & CONFIGURED(1 << 18))) {
1583 /*
1584 * The filter parameters are derived from the length of the
1585 * diagonal in device units, with some magic constants which
1586 * are partly adapted from libinput or synaptics code, or are
1587 * based on tests and guess work. The absolute resolution
1588 * values might not be reliable, but if they are present the
1589 * settings are adapted to the ratio.
1590 */
1591 h_res = input->hw.h_res;
1592 v_res = input->hw.v_res;
1593 if (h_res == 0 || v_res == 0)
1594 h_res = v_res = 1;
1595 diag = isqrt(width * width + height * height);
1596 input->filter.h.scale = (imin(920, diag) << 12) / diag;
1597 input->filter.v.scale = input->filter.h.scale * h_res / v_res;
1598 h_unit = imax(diag / 280, 3);
1599 v_unit = imax((h_unit * v_res + h_res / 2) / h_res, 3);
1600 input->filter.h.hysteresis = h_unit;
1601 input->filter.v.hysteresis = v_unit;
1602 input->filter.mode = FILTER_MODE_DEFAULT0;
1603 input->filter.dclr = h_unit - h_unit / 5;
1604 wstpad_init_deceleration(input);
1605
1606 tp->features &= (WSTPAD_MT(1U << 31) | WSTPAD_DISABLE(1 << 7));
1607
1608 if (input->hw.contacts_max != 1)
1609 tp->features |= WSTPAD_TWOFINGERSCROLL(1 << 3);
1610 else
1611 tp->features |= WSTPAD_EDGESCROLL(1 << 4);
1612
1613 if (input->hw.hw_type == WSMOUSEHW_CLICKPAD) {
1614 if (input->hw.type == WSMOUSE_TYPE_SYNAP_SBTN19) {
1615 tp->features |= WSTPAD_TOPBUTTONS(1 << 2);
1616 } else {
1617 tp->features |= WSTPAD_SOFTBUTTONS(1 << 0);
1618 tp->features |= WSTPAD_SOFTMBTN(1 << 1);
1619 }
1620 }
1621
1622 tp->params.left_edge = V_EDGE_RATIO_DEFAULT205;
1623 tp->params.right_edge = V_EDGE_RATIO_DEFAULT205;
1624 tp->params.bottom_edge = ((tp->features & WSTPAD_SOFTBUTTONS(1 << 0))
1625 ? B_EDGE_RATIO_DEFAULT410 : 0);
1626 tp->params.top_edge = ((tp->features & WSTPAD_TOPBUTTONS(1 << 2))
1627 ? T_EDGE_RATIO_DEFAULT512 : 0);
1628 tp->params.center_width = CENTER_RATIO_DEFAULT512;
1629
1630 tp->tap.maxtime.tv_nsec = TAP_MAXTIME_DEFAULT180 * 1000000;
1631 tp->tap.clicktime = TAP_CLICKTIME_DEFAULT180;
1632 tp->tap.locktime = TAP_LOCKTIME_DEFAULT0;
1633
1634 tp->scroll.hdist = 4 * h_unit;
1635 tp->scroll.vdist = 4 * v_unit;
1636 tp->tap.maxdist = 4 * h_unit;
1637
1638 if (IS_MT(tp)((tp)->features & (1U << 31)) && h_res > 1 && v_res > 1 &&
1639 input->hw.hw_type == WSMOUSEHW_CLICKPAD &&
1640 (width + h_res / 2) / h_res > 100 &&
1641 (height + v_res / 2) / v_res > 60) {
1642 tp->params.mtbtn_maxdist = h_res * 35;
1643 } else {
1644 tp->params.mtbtn_maxdist = -1; /* not available */
1645 }
1646 }
1647
1648 /* A touch with a flag set in this mask does not move the pointer. */
1649 tp->freeze = EDGES((1 << 0) | (1 << 1) | (1 << 2) | (1 <<
3))
;
1650
1651 offset = width * tp->params.left_edge / 4096;
1652 tp->edge.left = (offset ? input->hw.x_min + offset : INT_MIN(-0x7fffffff-1));
1653 offset = width * tp->params.right_edge / 4096;
1654 tp->edge.right = (offset ? input->hw.x_max - offset : INT_MAX0x7fffffff);
1655 offset = height * tp->params.bottom_edge / 4096;
1656 tp->edge.bottom = (offset ? input->hw.y_min + offset : INT_MIN(-0x7fffffff-1));
1657 tp->edge.low = tp->edge.bottom + offset / 2;
1658 offset = height * tp->params.top_edge / 4096;
1659 tp->edge.top = (offset ? input->hw.y_max - offset : INT_MAX0x7fffffff);
1660
1661 offset = width * abs(tp->params.center_width) / 8192;
1662 tp->edge.center = input->hw.x_min + width / 2;
1663 tp->edge.center_left = tp->edge.center - offset;
1664 tp->edge.center_right = tp->edge.center + offset;
1665
1666 /*
1667 * Make the MTBUTTONS configuration consistent. A non-negative 'maxdist'
1668 * value makes the feature visible in wsconsctl. 0-values are replaced
1669 * by a default (one fourth of the length of the touchpad diagonal).
1670 */
1671 if (tp->params.mtbtn_maxdist < 0) {
1672 tp->features &= ~WSTPAD_MTBUTTONS(1 << 8);
1673 } else if (tp->params.mtbtn_maxdist == 0) {
1674 diag = isqrt(width * width + height * height);
1675 tp->params.mtbtn_maxdist = diag / 4;
1676 }
1677
1678 tp->handlers = 0;
1679
1680 if (tp->features & (WSTPAD_SOFTBUTTONS(1 << 0) | WSTPAD_MTBUTTONS(1 << 8)))
1681 tp->handlers |= 1 << SOFTBUTTON_HDLR;
1682 if (tp->features & WSTPAD_TOPBUTTONS(1 << 2))
1683 tp->handlers |= 1 << TOPBUTTON_HDLR;
1684 if (tp->features & WSTPAD_TWOFINGERSCROLL(1 << 3))
1685 tp->handlers |= 1 << F2SCROLL_HDLR;
1686 else if (tp->features & WSTPAD_EDGESCROLL(1 << 4))
1687 tp->handlers |= 1 << EDGESCROLL_HDLR;
1688
1689 for (i = 0; i < TAP_BTNMAP_SIZE3; i++) {
1690 if (tp->tap.btnmap[i] == 0)
1691 continue;
1692
1693 tp->tap.clicktime = imin(imax(tp->tap.clicktime, 80), 350);
1694 if (tp->tap.locktime)
1695 tp->tap.locktime =
1696 imin(imax(tp->tap.locktime, 150), 5000);
1697 tp->handlers |= 1 << TAP_HDLR;
1698 break;
1699 }
1700
1701 if (input->hw.hw_type == WSMOUSEHW_CLICKPAD)
1702 tp->handlers |= 1 << CLICK_HDLR;
1703
1704 tp->sbtnswap = ((tp->features & WSTPAD_SWAPSIDES(1 << 6))
1705 ? (LEFTBTN((1) > 0 && (1) <= 32 ? 1 << ((1) - 1) : 0
)
| RIGHTBTN((3) > 0 && (3) <= 32 ? 1 << ((3) - 1) : 0
)
) : 0);
1706
1707 return (0);
1708}
1709
1710void
1711wstpad_reset(struct wsmouseinput *input)
1712{
1713 struct wstpad *tp = input->tp;
1714
1715 if (tp != NULL((void *)0)) {
1716 timeout_del(&tp->tap.to);
1717 tp->tap.state = TAP_DETECT;
1718 }
1719
1720 if (input->sbtn.buttons) {
1721 input->sbtn.sync = input->sbtn.buttons;
1722 input->sbtn.buttons = 0;
1723 }
1724}
1725
1726void
1727wstpad_cleanup(struct wsmouseinput *input)
1728{
1729 struct wstpad *tp = input->tp;
1730 int slots;
1731
1732 timeout_del(&tp->tap.to);
1733 slots = imax(input->mt.num_slots, 1);
1734 free(tp->tpad_touches, M_DEVBUF2, slots * sizeof(struct tpad_touch));
1735 free(tp, M_DEVBUF2, sizeof(struct wstpad));
1736 input->tp = NULL((void *)0);
1737}
1738
1739int
1740wstpad_set_param(struct wsmouseinput *input, int key, int val)
1741{
1742 struct wstpad *tp = input->tp;
1743 u_int flag;
1744
1745 if (tp == NULL((void *)0))
1746 return (EINVAL22);
1747
1748 switch (key) {
1749 case WSMOUSECFG_SOFTBUTTONS ... WSMOUSECFG_MTBUTTONS:
1750 switch (key) {
1751 case WSMOUSECFG_SOFTBUTTONS:
1752 flag = WSTPAD_SOFTBUTTONS(1 << 0);
1753 break;
1754 case WSMOUSECFG_SOFTMBTN:
1755 flag = WSTPAD_SOFTMBTN(1 << 1);
1756 break;
1757 case WSMOUSECFG_TOPBUTTONS:
1758 flag = WSTPAD_TOPBUTTONS(1 << 2);
1759 break;
1760 case WSMOUSECFG_TWOFINGERSCROLL:
1761 flag = WSTPAD_TWOFINGERSCROLL(1 << 3);
1762 break;
1763 case WSMOUSECFG_EDGESCROLL:
1764 flag = WSTPAD_EDGESCROLL(1 << 4);
1765 break;
1766 case WSMOUSECFG_HORIZSCROLL:
1767 flag = WSTPAD_HORIZSCROLL(1 << 5);
1768 break;
1769 case WSMOUSECFG_SWAPSIDES:
1770 flag = WSTPAD_SWAPSIDES(1 << 6);
1771 break;
1772 case WSMOUSECFG_DISABLE:
1773 flag = WSTPAD_DISABLE(1 << 7);
1774 break;
1775 case WSMOUSECFG_MTBUTTONS:
1776 flag = WSTPAD_MTBUTTONS(1 << 8);
1777 break;
1778 }
1779 if (val)
1780 tp->features |= flag;
1781 else
1782 tp->features &= ~flag;
1783 break;
1784 case WSMOUSECFG_LEFT_EDGE:
1785 tp->params.left_edge = val;
1786 break;
1787 case WSMOUSECFG_RIGHT_EDGE:
1788 tp->params.right_edge = val;
1789 break;
1790 case WSMOUSECFG_TOP_EDGE:
1791 tp->params.top_edge = val;
1792 break;
1793 case WSMOUSECFG_BOTTOM_EDGE:
1794 tp->params.bottom_edge = val;
1795 break;
1796 case WSMOUSECFG_CENTERWIDTH:
1797 tp->params.center_width = val;
1798 break;
1799 case WSMOUSECFG_HORIZSCROLLDIST:
1800 tp->scroll.hdist = val;
1801 break;
1802 case WSMOUSECFG_VERTSCROLLDIST:
1803 tp->scroll.vdist = val;
1804 break;
1805 case WSMOUSECFG_F2WIDTH:
1806 tp->params.f2width = val;
1807 break;
1808 case WSMOUSECFG_F2PRESSURE:
1809 tp->params.f2pressure = val;
1810 break;
1811 case WSMOUSECFG_TAP_MAXTIME:
1812 tp->tap.maxtime.tv_nsec = imin(val, 999) * 1000000;
1813 break;
1814 case WSMOUSECFG_TAP_CLICKTIME:
1815 tp->tap.clicktime = val;
1816 break;
1817 case WSMOUSECFG_TAP_LOCKTIME:
1818 tp->tap.locktime = val;
1819 break;
1820 case WSMOUSECFG_TAP_ONE_BTNMAP:
1821 tp->tap.btnmap[0] = BTNMASK(val)((val) > 0 && (val) <= 32 ? 1 << ((val) -
1) : 0)
;
1822 break;
1823 case WSMOUSECFG_TAP_TWO_BTNMAP:
1824 tp->tap.btnmap[1] = BTNMASK(val)((val) > 0 && (val) <= 32 ? 1 << ((val) -
1) : 0)
;
1825 break;
1826 case WSMOUSECFG_TAP_THREE_BTNMAP:
1827 tp->tap.btnmap[2] = BTNMASK(val)((val) > 0 && (val) <= 32 ? 1 << ((val) -
1) : 0)
;
1828 break;
1829 case WSMOUSECFG_MTBTN_MAXDIST:
1830 if (IS_MT(tp)((tp)->features & (1U << 31)))
1831 tp->params.mtbtn_maxdist = val;
1832 break;
1833 default:
1834 return (ENOTSUP91);
1835 }
1836
1837 return (0);
1838}
1839
1840int
1841wstpad_get_param(struct wsmouseinput *input, int key, int *pval)
1842{
1843 struct wstpad *tp = input->tp;
1844 u_int flag;
1845
1846 if (tp == NULL((void *)0))
1847 return (EINVAL22);
1848
1849 switch (key) {
1850 case WSMOUSECFG_SOFTBUTTONS ... WSMOUSECFG_MTBUTTONS:
1851 switch (key) {
1852 case WSMOUSECFG_SOFTBUTTONS:
1853 flag = WSTPAD_SOFTBUTTONS(1 << 0);
1854 break;
1855 case WSMOUSECFG_SOFTMBTN:
1856 flag = WSTPAD_SOFTMBTN(1 << 1);
1857 break;
1858 case WSMOUSECFG_TOPBUTTONS:
1859 flag = WSTPAD_TOPBUTTONS(1 << 2);
1860 break;
1861 case WSMOUSECFG_TWOFINGERSCROLL:
1862 flag = WSTPAD_TWOFINGERSCROLL(1 << 3);
1863 break;
1864 case WSMOUSECFG_EDGESCROLL:
1865 flag = WSTPAD_EDGESCROLL(1 << 4);
1866 break;
1867 case WSMOUSECFG_HORIZSCROLL:
1868 flag = WSTPAD_HORIZSCROLL(1 << 5);
1869 break;
1870 case WSMOUSECFG_SWAPSIDES:
1871 flag = WSTPAD_SWAPSIDES(1 << 6);
1872 break;
1873 case WSMOUSECFG_DISABLE:
1874 flag = WSTPAD_DISABLE(1 << 7);
1875 break;
1876 case WSMOUSECFG_MTBUTTONS:
1877 flag = WSTPAD_MTBUTTONS(1 << 8);
1878 break;
1879 }
1880 *pval = !!(tp->features & flag);
1881 break;
1882 case WSMOUSECFG_LEFT_EDGE:
1883 *pval = tp->params.left_edge;
1884 break;
1885 case WSMOUSECFG_RIGHT_EDGE:
1886 *pval = tp->params.right_edge;
1887 break;
1888 case WSMOUSECFG_TOP_EDGE:
1889 *pval = tp->params.top_edge;
1890 break;
1891 case WSMOUSECFG_BOTTOM_EDGE:
1892 *pval = tp->params.bottom_edge;
1893 break;
1894 case WSMOUSECFG_CENTERWIDTH:
1895 *pval = tp->params.center_width;
1896 break;
1897 case WSMOUSECFG_HORIZSCROLLDIST:
1898 *pval = tp->scroll.hdist;
1899 break;
1900 case WSMOUSECFG_VERTSCROLLDIST:
1901 *pval = tp->scroll.vdist;
1902 break;
1903 case WSMOUSECFG_F2WIDTH:
1904 *pval = tp->params.f2width;
1905 break;
1906 case WSMOUSECFG_F2PRESSURE:
1907 *pval = tp->params.f2pressure;
1908 break;
1909 case WSMOUSECFG_TAP_MAXTIME:
1910 *pval = tp->tap.maxtime.tv_nsec / 1000000;
1911 break;
1912 case WSMOUSECFG_TAP_CLICKTIME:
1913 *pval = tp->tap.clicktime;
1914 break;
1915 case WSMOUSECFG_TAP_LOCKTIME:
1916 *pval = tp->tap.locktime;
1917 break;
1918 case WSMOUSECFG_TAP_ONE_BTNMAP:
1919 *pval = ffs(tp->tap.btnmap[0]);
1920 break;
1921 case WSMOUSECFG_TAP_TWO_BTNMAP:
1922 *pval = ffs(tp->tap.btnmap[1]);
1923 break;
1924 case WSMOUSECFG_TAP_THREE_BTNMAP:
1925 *pval = ffs(tp->tap.btnmap[2]);
1926 break;
1927 case WSMOUSECFG_MTBTN_MAXDIST:
1928 *pval = tp->params.mtbtn_maxdist;
1929 break;
1930 default:
1931 return (ENOTSUP91);
1932 }
1933
1934 return (0);
1935}