-/* $OpenBSD: wsmouse.c,v 1.41 2017/11/23 22:59:42 bru Exp $ */
+/* $OpenBSD: wsmouse.c,v 1.42 2018/01/11 23:50:49 bru Exp $ */
/* $NetBSD: wsmouse.c,v 1.35 2005/02/27 00:27:52 perry Exp $ */
/*
motion->sync |= SYNC_DELTAS;
}
-/*
- * Handle absolute coordinates.
- *
- * x_delta/y_delta are used by touchpad code. The values are only
- * valid if the SYNC-flags are set, and will be cleared by update- or
- * conversion-functions if a touch shouldn't trigger pointer motion.
- */
+static inline void
+set_x(struct position *pos, int x, u_int *sync, u_int mask)
+{
+ if (*sync & mask) {
+ pos->x -= pos->dx;
+ pos->acc_dx -= pos->dx;
+ }
+ if ((pos->dx = x - pos->x)) {
+ pos->x = x;
+ pos->acc_dx += pos->dx;
+ *sync |= mask;
+ }
+}
+
+static inline void
+set_y(struct position *pos, int y, u_int *sync, u_int mask)
+{
+ if (*sync & mask) {
+ pos->y -= pos->dy;
+ pos->acc_dy -= pos->dy;
+ }
+ if ((pos->dy = y - pos->y)) {
+ pos->y = y;
+ pos->acc_dy += pos->dy;
+ *sync |= mask;
+ }
+}
+
+static inline void
+cleardeltas(struct position *pos)
+{
+ pos->dx = pos->acc_dx = 0;
+ pos->dy = pos->acc_dy = 0;
+}
+
void
wsmouse_position(struct device *sc, int x, int y)
{
struct motion_state *motion =
&((struct wsmouse_softc *) sc)->sc_input.motion;
- int delta;
- delta = x - motion->x;
- if (delta) {
- motion->x = x;
- motion->sync |= SYNC_X;
- motion->x_delta = delta;
- }
- delta = y - motion->y;
- if (delta) {
- motion->y = y;
- motion->sync |= SYNC_Y;
- motion->y_delta = delta;
- }
+ set_x(&motion->pos, x, &motion->sync, SYNC_X);
+ set_y(&motion->pos, y, &motion->sync, SYNC_Y);
}
static inline int
touch->pressure = pressure;
touch->sync |= SYNC_PRESSURE;
}
- touch->prev_contacts = touch->contacts;
if (contacts != touch->contacts) {
touch->contacts = contacts;
touch->sync |= SYNC_CONTACTS;
initial = ((mt->touches & bit) == (mt->sync[MTS_TOUCH] & bit));
mts = &mt->slots[slot];
- if (x != mts->x || initial) {
- mts->x = x;
+
+ if (initial) {
+ mts->pos.x = x;
+ mts->pos.y = y;
+ cleardeltas(&mts->pos);
mt->sync[MTS_X] |= bit;
- }
- if (y != mts->y || initial) {
- mts->y = y;
mt->sync[MTS_Y] |= bit;
+ } else {
+ set_x(&mts->pos, x, mt->sync + MTS_X, bit);
+ set_y(&mts->pos, y, mt->sync + MTS_Y, bit);
}
+
pressure = normalized_pressure(input, pressure);
if (pressure != mts->pressure || initial) {
mts->pressure = pressure;
switch (type) {
case WSMOUSE_REL_X:
- value += input->motion.x; /* fall through */
+ value += input->motion.pos.x; /* fall through */
case WSMOUSE_ABS_X:
- wsmouse_position(sc, value, input->motion.y);
+ wsmouse_position(sc, value, input->motion.pos.y);
return;
case WSMOUSE_REL_Y:
- value += input->motion.y;
+ value += input->motion.pos.y;
case WSMOUSE_ABS_Y:
- wsmouse_position(sc, input->motion.x, value);
+ wsmouse_position(sc, input->motion.pos.x, value);
return;
case WSMOUSE_PRESSURE:
wsmouse_touch(sc, value, input->touch.contacts);
}
return;
case WSMOUSE_MT_REL_X:
- value += mts->x; /* fall through */
+ value += mts->pos.x; /* fall through */
case WSMOUSE_MT_ABS_X:
- wsmouse_mtstate(sc, aux, value, mts->y, mts->pressure);
+ wsmouse_mtstate(sc, aux, value, mts->pos.y, mts->pressure);
return;
case WSMOUSE_MT_REL_Y:
- value += mts->y;
+ value += mts->pos.y;
case WSMOUSE_MT_ABS_Y:
- wsmouse_mtstate(sc, aux, mts->x, value, mts->pressure);
+ wsmouse_mtstate(sc, aux, mts->pos.x, value, mts->pressure);
return;
case WSMOUSE_MT_PRESSURE:
- wsmouse_mtstate(sc, aux, mts->x, mts->y, value);
+ wsmouse_mtstate(sc, aux, mts->pos.x, mts->pos.y, value);
return;
}
}
struct touch_state *touch = &input->touch;
if (touch->pressure == 0) {
- /* Restore valid coordinates. */
- if (motion->sync & SYNC_X)
- motion->x -= motion->x_delta;
- if (motion->sync & SYNC_Y)
- motion->y -= motion->y_delta;
- /* Don't generate motion/position events. */
- motion->sync &= ~SYNC_POSITION;
+ /*
+ * There may be zero coordinates, or coordinates of
+ * touches with pressure values below min_pressure.
+ */
+ if (motion->sync & SYNC_POSITION) {
+ /* Restore valid coordinates. */
+ motion->pos.x -= motion->pos.dx;
+ motion->pos.y -= motion->pos.dy;
+ motion->sync &= ~SYNC_POSITION;
+ }
+
+ if (touch->prev_contacts == 0)
+ touch->sync &= ~SYNC_PRESSURE;
+
}
+
if (touch->sync & SYNC_CONTACTS)
- /* Suppress pointer motion. */
- motion->x_delta = motion->y_delta = 0;
+ /* Suppress pointer movement. */
+ cleardeltas(&motion->pos);
if ((touch->sync & SYNC_PRESSURE) && touch->min_pressure) {
if (touch->pressure >= input->filter.pressure_hi)
}
}
+/* Return TRUE if a coordinate update may be noise. */
+int
+wsmouse_hysteresis(struct wsmouseinput *input, struct position *pos)
+{
+
+ if (!(input->filter.h.hysteresis && input->filter.v.hysteresis))
+ return (0);
+
+ if ((pos->dx > 0 && pos->dx > pos->acc_dx)
+ || (pos->dx < 0 && pos->dx < pos->acc_dx))
+ pos->acc_dx = pos->dx;
+
+ if ((pos->dy > 0 && pos->dy > pos->acc_dy)
+ || (pos->dy < 0 && pos->dy < pos->acc_dy))
+ pos->acc_dy = pos->dy;
+
+ return (abs(pos->acc_dx) < input->filter.h.hysteresis
+ && abs(pos->acc_dy) < input->filter.v.hysteresis);
+}
+
/*
* Select the pointer-controlling MT slot.
*
* contain the pointer-controlling slot, a new slot will be selected.
*/
void
-wsmouse_ptr_ctrl(struct mt_state *mt)
+wsmouse_ptr_ctrl(struct wsmouseinput *input)
{
+ struct mt_state *mt = &input->mt;
u_int updates;
int select, slot;
+ updates = (mt->sync[MTS_X] | mt->sync[MTS_Y]) & ~mt->sync[MTS_TOUCH];
+ FOREACHBIT(updates, slot) {
+ /*
+ * Touches that just produce noise are no problem if the
+ * frequency of zero deltas is high enough, but there might
+ * be no guarantee for that.
+ */
+ if (wsmouse_hysteresis(input, &mt->slots[slot].pos))
+ updates ^= (1 << slot);
+ }
+
mt->prev_ptr = mt->ptr;
if (mt->num_touches <= 1) {
*/
select = ((mt->ptr & mt->touches & ~mt->ptr_mask) == 0);
- /* Remove slots without X/Y deltas from the cycle. */
- updates = (mt->sync[MTS_X] | mt->sync[MTS_Y]) & ~mt->sync[MTS_TOUCH];
+ /* Remove slots without coordinate deltas from the cycle. */
mt->ptr_cycle &= ~(mt->frame ^ updates);
if (mt->ptr_cycle & updates) {
struct mt_slot *mts;
int slot, pressure;
- wsmouse_ptr_ctrl(mt);
+ wsmouse_ptr_ctrl(input);
if (mt->ptr) {
slot = ffs(mt->ptr) - 1;
mts = &mt->slots[slot];
- wsmouse_position(sc, mts->x, mts->y);
+ if (mts->pos.x != input->motion.pos.x)
+ input->motion.sync |= SYNC_X;
+ if (mts->pos.y != input->motion.pos.y)
+ input->motion.sync |= SYNC_Y;
if (mt->ptr != mt->prev_ptr)
- /* Suppress pointer motion. */
- input->motion.x_delta = input->motion.y_delta = 0;
+ /* Suppress pointer movement. */
+ mts->pos.dx = mts->pos.dy = 0;
+ memcpy(&input->motion.pos, &mts->pos, sizeof(struct position));
+
pressure = mts->pressure;
} else {
pressure = 0;
}
if (motion->sync & SYNC_POSITION) {
if (motion->sync & SYNC_X) {
- x = (h->inv ? h->inv - motion->x : motion->x);
+ x = (h->inv ? h->inv - motion->pos.x : motion->pos.x);
wsmouse_evq_put(evq, ABS_X_EV(input), x);
}
if (motion->sync & SYNC_Y) {
- y = (v->inv ? v->inv - motion->y : motion->y);
+ y = (v->inv ? v->inv - motion->pos.y : motion->pos.y);
wsmouse_evq_put(evq, ABS_Y_EV(input), y);
}
- if (motion->x_delta == 0 && motion->y_delta == 0
- && (input->flags & TPAD_NATIVE_MODE))
+ if (motion->pos.dx == 0 && motion->pos.dy == 0
+ && (input->flags & TPAD_NATIVE_MODE ))
/* Suppress pointer motion. */
wsmouse_evq_put(evq, WSCONS_EVENT_TOUCH_RESET, 0);
}
input->sbtn.sync = 0;
input->motion.sync = 0;
input->touch.sync = 0;
+ input->touch.prev_contacts = input->touch.contacts;
if (input->mt.frame) {
input->mt.frame = 0;
for (i = 0; i < MTS_SIZE; i++)
add_mouse_randomness(input->btn.buttons
^ input->motion.dx ^ input->motion.dy
- ^ input->motion.x ^ input->motion.y
+ ^ input->motion.pos.x ^ input->motion.pos.y
^ input->motion.dz ^ input->motion.dw);
if (input->mt.frame) {
if (input->flags & RESYNC) {
input->flags &= ~RESYNC;
input->motion.sync &= SYNC_POSITION;
- input->motion.x_delta = input->motion.y_delta = 0;
}
if (input->btn.sync)
if (mt->num_touches >= size) {
FOREACHBIT(touches, slot)
for (i = 0; i < size; i++) {
- dx = pt[i].x - mt->slots[slot].x;
- dy = pt[i].y - mt->slots[slot].y;
+ dx = pt[i].x - mt->slots[slot].pos.x;
+ dy = pt[i].y - mt->slots[slot].pos.y;
*p++ = dx * dx + dy * dy;
}
m = mt->num_touches;
} else {
for (i = 0; i < size; i++)
FOREACHBIT(touches, slot) {
- dx = pt[i].x - mt->slots[slot].x;
- dy = pt[i].y - mt->slots[slot].y;
+ dx = pt[i].x - mt->slots[slot].pos.x;
+ dy = pt[i].y - mt->slots[slot].pos.y;
*p++ = dx * dx + dy * dy;
}
m = size;
break;
case WSMOUSECFG_X_HYSTERESIS:
input->filter.h.hysteresis = val;
- input->filter.h.acc = 0;
break;
case WSMOUSECFG_Y_HYSTERESIS:
input->filter.v.hysteresis = val;
- input->filter.v.acc = 0;
break;
case WSMOUSECFG_DECELERATION:
input->filter.dclr = val;
-/* $OpenBSD: wstpad.c,v 1.15 2017/12/23 10:50:15 bru Exp $ */
+/* $OpenBSD: wstpad.c,v 1.16 2018/01/11 23:50:49 bru Exp $ */
/*
* Copyright (c) 2015, 2016 Ulf Brosziewski
return (filter->inv ? -val : val);
}
-static inline int
-raw_dx(struct wsmouseinput *input)
-{
- return ((input->motion.sync & SYNC_X) ? input->motion.x_delta : 0);
-}
-
-static inline int
-raw_dy(struct wsmouseinput *input)
-{
- return ((input->motion.sync & SYNC_Y) ? input->motion.y_delta : 0);
-}
-
/*
* Directions of motion are represented by numbers in the range 0 - 11,
* corresponding to clockwise counted circle sectors:
t = &tp->tpad_touches[slot];
t->state = TOUCH_BEGIN;
mts = &input->mt.slots[slot];
- t->x = normalize_abs(&input->filter.h, mts->x);
- t->y = normalize_abs(&input->filter.v, mts->y);
+ t->x = normalize_abs(&input->filter.h, mts->pos.x);
+ t->y = normalize_abs(&input->filter.v, mts->pos.y);
t->orig.x = t->x;
t->orig.y = t->y;
memcpy(&t->orig.time, &tp->time, sizeof(struct timespec));
t->state = TOUCH_UPDATE;
if ((1 << slot) & input->mt.frame) {
mts = &input->mt.slots[slot];
- dx = normalize_abs(&input->filter.h, mts->x) - t->x;
+ dx = normalize_abs(&input->filter.h, mts->pos.x) - t->x;
t->x += dx;
- dy = normalize_abs(&input->filter.v, mts->y) - t->y;
+ dy = normalize_abs(&input->filter.v, mts->pos.y) - t->y;
t->y += dy;
t->flags &= (~EDGES | edge_flags(tp, t->x, t->y));
wstpad_set_direction(t, dx, dy, tp->ratio);
int slot;
/* Use the unfiltered deltas. */
- tp->dx = normalize_rel(&input->filter.h, raw_dx(input));
- tp->dy = normalize_rel(&input->filter.v, raw_dy(input));
+ tp->dx = normalize_rel(&input->filter.h, input->motion.pos.dx);
+ tp->dy = normalize_rel(&input->filter.v, input->motion.pos.dy);
tp->btns = input->btn.buttons;
tp->btns_sync = input->btn.sync;
wstpad_mt_masks(input);
} else {
t = tp->t;
- t->x = normalize_abs(&input->filter.h, input->motion.x);
- t->y = normalize_abs(&input->filter.v, input->motion.y);
+ t->x = normalize_abs(&input->filter.h, input->motion.pos.x);
+ t->y = normalize_abs(&input->filter.v, input->motion.pos.y);
if (tp->contacts)
t->state = (tp->prev_contacts ?
TOUCH_UPDATE : TOUCH_BEGIN);
* movements. The "strong" variant applies independently to the axes,
* and it is applied continuously. It takes effect whenever the
* orientation on an axis changes, which makes pointer paths more stable.
- * The "weak" variant is more precise and does not affect paths, it just
- * filters noise at the start- and stop-points of a movement.
+ *
+ * The default variant, wsmouse_hysteresis, is more precise and does not
+ * affect paths, it just filters noise when a touch starts or is resting.
*/
-void
-wstpad_strong_hysteresis(int *dx, int *dy,
- int *h_acc, int *v_acc, int h_threshold, int v_threshold)
-{
- *h_acc += *dx;
- *v_acc += *dy;
- if (*h_acc > h_threshold)
- *dx = *h_acc - h_threshold;
- else if (*h_acc < -h_threshold)
- *dx = *h_acc + h_threshold;
- else
- *dx = 0;
- *h_acc -= *dx;
- if (*v_acc > v_threshold)
- *dy = *v_acc - v_threshold;
- else if (*v_acc < -v_threshold)
- *dy = *v_acc + v_threshold;
- else
- *dy = 0;
- *v_acc -= *dy;
-}
-
-void
-wstpad_weak_hysteresis(int *dx, int *dy,
- int *h_acc, int *v_acc, int h_threshold, int v_threshold)
+static inline void
+strong_hysteresis(int *delta, int *acc, int threshold)
{
- *h_acc += *dx;
- *v_acc += *dy;
- if ((*dx > 0 && *h_acc < *dx) || (*dx < 0 && *h_acc > *dx))
- *h_acc = *dx;
- if ((*dy > 0 && *v_acc < *dy) || (*dy < 0 && *v_acc > *dy))
- *v_acc = *dy;
- if (abs(*h_acc) < h_threshold && abs(*v_acc) < v_threshold)
- *dx = *dy = 0;
+ int d;
+
+ if (*delta > 0) {
+ if (*delta > *acc)
+ *acc = *delta;
+ if ((d = *acc - threshold) < *delta)
+ *delta = (d < 0 ? 0 : d);
+ } else if (*delta < 0) {
+ if (*delta < *acc)
+ *acc = *delta;
+ if ((d = *acc + threshold) > *delta)
+ *delta = (d > 0 ? 0 : d);
+ }
}
void
-wstpad_filter(struct wsmouseinput *input, int *dx, int *dy)
+wstpad_filter(struct wsmouseinput *input)
{
struct axis_filter *h = &input->filter.h;
struct axis_filter *v = &input->filter.v;
+ struct position *pos = &input->motion.pos;
int strength = input->filter.mode & 7;
+ int dx, dy;
- if ((h->dmax && (abs(*dx) > h->dmax))
- || (v->dmax && (abs(*dy) > v->dmax)))
- *dx = *dy = 0;
+ if (!(input->motion.sync & SYNC_POSITION)
+ || (h->dmax && (abs(pos->dx) > h->dmax))
+ || (v->dmax && (abs(pos->dy) > v->dmax)))
+ pos->dx = pos->dy = 0;
- if (h->hysteresis || v->hysteresis) {
- if (input->filter.mode & STRONG_HYSTERESIS)
- wstpad_strong_hysteresis(dx, dy, &h->acc,
- &v->acc, h->hysteresis, v->hysteresis);
- else
- wstpad_weak_hysteresis(dx, dy, &h->acc,
- &v->acc, h->hysteresis, v->hysteresis);
+ dx = pos->dx;
+ dy = pos->dy;
+
+ if (input->filter.mode & STRONG_HYSTERESIS) {
+ strong_hysteresis(&dx, &pos->acc_dx, h->hysteresis);
+ strong_hysteresis(&dy, &pos->acc_dy, v->hysteresis);
+ } else if (wsmouse_hysteresis(input, pos)) {
+ dx = dy = 0;
}
- if (input->filter.dclr && wstpad_decelerate(input, dx, dy))
+ if (input->filter.dclr && wstpad_decelerate(input, &dx, &dy))
/* Strong smoothing may hamper the precision at low speeds. */
strength = imin(strength, 2);
if (strength) {
+ if ((input->touch.sync & SYNC_CONTACTS)
+ || input->mt.ptr != input->mt.prev_ptr) {
+ h->avg = v->avg = 0;
+ }
/* Use a weighted decaying average for smoothing. */
- *dx = *dx * (8 - strength) + h->avg * strength + h->avg_rmdr;
- *dy = *dy * (8 - strength) + v->avg * strength + v->avg_rmdr;
- h->avg_rmdr = (*dx >= 0 ? *dx & 7 : -(-*dx & 7));
- v->avg_rmdr = (*dy >= 0 ? *dy & 7 : -(-*dy & 7));
- *dx = h->avg = *dx / 8;
- *dy = v->avg = *dy / 8;
+ dx = dx * (8 - strength) + h->avg * strength + h->avg_rmdr;
+ dy = dy * (8 - strength) + v->avg * strength + v->avg_rmdr;
+ h->avg_rmdr = (dx >= 0 ? dx & 7 : -(-dx & 7));
+ v->avg_rmdr = (dy >= 0 ? dy & 7 : -(-dy & 7));
+ dx = h->avg = dx / 8;
+ dy = v->avg = dy / 8;
}
+
+ input->motion.dx = dx;
+ input->motion.dy = dy;
}
/*
- * Compatibility-mode conversions. This function transforms and filters
+ * Compatibility-mode conversions. wstpad_filter transforms and filters
* the coordinate inputs, extended functionality is provided by
* wstpad_process_input.
*/
void
wstpad_compat_convert(struct wsmouseinput *input, struct evq_access *evq)
{
- int dx, dy;
-
if (input->flags & TRACK_INTERVAL)
wstpad_track_interval(input, &evq->ts);
- dx = raw_dx(input);
- dy = raw_dy(input);
-
- if ((input->touch.sync & SYNC_CONTACTS)
- || input->mt.ptr != input->mt.prev_ptr) {
- input->filter.h.acc = input->filter.v.acc = 0;
- input->filter.h.avg = input->filter.v.avg = 0;
- }
+ wstpad_filter(input);
- wstpad_filter(input, &dx, &dy);
-
- input->motion.dx = dx;
- input->motion.dy = dy;
- if ((dx || dy) && !(input->motion.sync & SYNC_DELTAS)) {
+ if ((input->motion.dx || input->motion.dy)
+ && !(input->motion.sync & SYNC_DELTAS)) {
input->motion.dz = input->motion.dw = 0;
input->motion.sync |= SYNC_DELTAS;
}
tp->freeze = EDGES;
offset = width * tp->params.left_edge / 4096;
- tp->edge.left = input->hw.x_min + offset;
+ tp->edge.left = (offset ? input->hw.x_min + offset : INT_MIN);
offset = width * tp->params.right_edge / 4096;
- tp->edge.right = input->hw.x_max - offset;
+ tp->edge.right = (offset ? input->hw.x_max - offset : INT_MAX);
offset = height * tp->params.bottom_edge / 4096;
- tp->edge.bottom = input->hw.y_min + offset;
+ tp->edge.bottom = (offset ? input->hw.y_min + offset : INT_MIN);
offset = height * tp->params.top_edge / 4096;
- tp->edge.top = input->hw.y_max - offset;
+ tp->edge.top = (offset ? input->hw.y_max - offset : INT_MAX);
offset = width * abs(tp->params.center_width) / 8192;
tp->edge.center = (input->hw.x_min + input->hw.x_max) / 2;