[PATCH 2/2] touchpad: Add edge-scrolling support

Hans de Goede hdegoede at redhat.com
Wed Nov 19 02:50:13 PST 2014


Hi,

On 11/11/2014 02:20 AM, Peter Hutterer wrote:
> sorry for the duplicate Hans, wayland-devel got dropped of the first reply.
> 
> On Fri, Nov 07, 2014 at 02:25:06PM +0100, Hans de Goede wrote:
>> Add edge-scrolling support for non multi-touch touchpads as well as for
>> users who prefer edge-scrolling (as long as they don't have a clickpad).
>>
>> Note the percentage to use of the width / height as scroll-edge differs from
>> one manufacturer to the next, the various per model percentages were taken
>> from xf86-input-synaptics.
>>
>> Signed-off-by: Hans de Goede <hdegoede at redhat.com>
>> ---
>>  src/Makefile.am                     |   1 +
>>  src/evdev-mt-touchpad-edge-scroll.c | 366 ++++++++++++++++++++++++++++++++++++
>>  src/evdev-mt-touchpad.c             |  59 +++++-
>>  src/evdev-mt-touchpad.h             |  46 +++++
>>  4 files changed, 463 insertions(+), 9 deletions(-)
>>  create mode 100644 src/evdev-mt-touchpad-edge-scroll.c
>>
>> diff --git a/src/Makefile.am b/src/Makefile.am
>> index 5cc52a6..027e08c 100644
>> --- a/src/Makefile.am
>> +++ b/src/Makefile.am
>> @@ -15,6 +15,7 @@ libinput_la_SOURCES =			\
>>  	evdev-mt-touchpad.h		\
>>  	evdev-mt-touchpad-tap.c		\
>>  	evdev-mt-touchpad-buttons.c	\
>> +	evdev-mt-touchpad-edge-scroll.c	\
>>  	filter.c			\
>>  	filter.h			\
>>  	filter-private.h		\
>> diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c
>> new file mode 100644
>> index 0000000..2968db4
>> --- /dev/null
>> +++ b/src/evdev-mt-touchpad-edge-scroll.c
>> @@ -0,0 +1,366 @@
>> +/*
>> + * Copyright © 2014 Red Hat, Inc.
>> + *
>> + * Permission to use, copy, modify, distribute, and sell this software and
>> + * its documentation for any purpose is hereby granted without fee, provided
>> + * that the above copyright notice appear in all copies and that both that
>> + * copyright notice and this permission notice appear in supporting
>> + * documentation, and that the name of the copyright holders not be used in
>> + * advertising or publicity pertaining to distribution of the software
>> + * without specific, written prior permission.  The copyright holders make
>> + * no representations about the suitability of this software for any
>> + * purpose.  It is provided "as is" without express or implied warranty.
>> + *
>> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
>> + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
>> + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
>> + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
>> + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
>> + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
>> + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
>> + */
>> +
>> +#include <errno.h>
>> +#include <limits.h>
>> +#include <math.h>
>> +#include <string.h>
>> +#include <unistd.h>
>> +#include "linux/input.h"
>> +
>> +#include "evdev-mt-touchpad.h"
>> +
>> +#define DEFAULT_SCROLL_LOCK_TIMEOUT 300 /* ms */
>> +/* Use a reasonably large threshold until locked into scrolling mode, to
>> +   avoid accidentally locking in scrolling mode when trying to use the entire
>> +   touchpad to move the pointer. The user can wait for the timeout to trigger
>> +   to do a small scroll. */
>> +#define DEFAULT_SCROLL_THRESHOLD 10.0
>> +
>> +enum scroll_event {
>> +	SCROLL_EVENT_TOUCH,
>> +	SCROLL_EVENT_MOTION,
>> +	SCROLL_EVENT_RELEASE,
>> +	SCROLL_EVENT_TIMEOUT,
>> +};
>> +
>> +static uint32_t
>> +tp_touch_get_edge(struct tp_dispatch *tp, struct tp_touch *touch)
>> +{
>> +	uint32_t edge = EDGE_NONE;
>> +
>> +	if (tp->scroll.mode != LIBINPUT_CONFIG_SCROLL_EDGE)
>> +		return 0;
>> +
>> +	if (touch->x > tp->scroll.right_edge)
>> +		edge |= EDGE_RIGHT;
>> +
>> +	if (touch->y > tp->scroll.bottom_edge)
>> +		edge |= EDGE_BOTTOM;
>> +
>> +	return edge;
>> +}
>> +
>> +static void
>> +tp_edge_scroll_set_state(struct tp_dispatch *tp, struct tp_touch *t,
>> +			 enum tp_edge_scroll_touch_state state)
> 
> fwiw, I prefer all args on separate lines if you need to split them anway.

Ok, will fix for v2.

>> +{
>> +	libinput_timer_cancel(&t->scroll.timer);
>> +
>> +	t->scroll.state = state;
>> +
>> +	switch (state) {
>> +	case EDGE_SCROLL_TOUCH_STATE_NONE:
>> +		t->scroll.edge = EDGE_NONE;
>> +		t->scroll.threshold = DEFAULT_SCROLL_THRESHOLD;
>> +		break;
>> +	case EDGE_SCROLL_TOUCH_STATE_EDGE_NEW:
>> +		libinput_timer_set(&t->scroll.timer,
>> +				   t->millis + DEFAULT_SCROLL_LOCK_TIMEOUT);
>> +		break;
>> +	case EDGE_SCROLL_TOUCH_STATE_EDGE:
>> +		t->scroll.threshold = 0.01; /* Do not allow 0.0 events */
>> +		break;
>> +	case EDGE_SCROLL_TOUCH_STATE_AREA:
>> +		t->scroll.edge = EDGE_NONE;
>> +		tp_set_pointer(tp, t);
>> +		break;
>> +	}
>> +}
> 
> can we make a pretty diagram for this statemachine? makes it a lot easier to
> visualize.

Done for v2.

>> +
>> +static void
>> +tp_edge_scroll_handle_none(struct tp_dispatch *tp, struct tp_touch *t,
>> +			   enum scroll_event event)
>> +{
>> +	struct libinput *libinput = tp->device->base.seat->libinput;
>> +
>> +	switch (event) {
>> +	case SCROLL_EVENT_TOUCH:
>> +		t->scroll.edge = tp_touch_get_edge(tp, t);
>> +		if (t->scroll.edge) {
>> +			tp_edge_scroll_set_state(tp, t,
>> +					EDGE_SCROLL_TOUCH_STATE_EDGE_NEW);
>> +		} else {
>> +			tp_edge_scroll_set_state(tp, t,
>> +					EDGE_SCROLL_TOUCH_STATE_AREA);
>> +		}
>> +		break;
>> +	case SCROLL_EVENT_MOTION:
>> +	case SCROLL_EVENT_RELEASE:
>> +	case SCROLL_EVENT_TIMEOUT:
>> +		log_bug_libinput(libinput,
>> +				 "unexpect scroll event in none state\n");
>> +		break;
>> +	}
>> +}
>> +
>> +static void
>> +tp_edge_scroll_handle_edge_new(struct tp_dispatch *tp, struct tp_touch *t,
>> +			       enum scroll_event event)
>> +{
>> +	struct libinput *libinput = tp->device->base.seat->libinput;
>> +
>> +	switch (event) {
>> +	case SCROLL_EVENT_TOUCH:
>> +		log_bug_libinput(libinput,
>> +				 "unexpect scroll event in edge new state\n");
>> +		break;
>> +	case SCROLL_EVENT_MOTION:
>> +		t->scroll.edge &= tp_touch_get_edge(tp, t);
>> +		if (!t->scroll.edge)
>> +			tp_edge_scroll_set_state(tp, t,
>> +					EDGE_SCROLL_TOUCH_STATE_AREA);
>> +		break;
>> +	case SCROLL_EVENT_RELEASE:
>> +		tp_edge_scroll_set_state(tp, t, EDGE_SCROLL_TOUCH_STATE_NONE);
>> +		break;
>> +	case SCROLL_EVENT_TIMEOUT:
>> +		tp_edge_scroll_set_state(tp, t, EDGE_SCROLL_TOUCH_STATE_EDGE);
>> +		break;
>> +	}
>> +}
>> +
>> +static void
>> +tp_edge_scroll_handle_edge(struct tp_dispatch *tp, struct tp_touch *t,
>> +			   enum scroll_event event)
>> +{
>> +	struct libinput *libinput = tp->device->base.seat->libinput;
>> +
>> +	switch (event) {
>> +	case SCROLL_EVENT_TOUCH:
>> +	case SCROLL_EVENT_TIMEOUT:
>> +		log_bug_libinput(libinput,
>> +				 "unexpect scroll event in edge state\n");
>> +		break;
>> +	case SCROLL_EVENT_MOTION:
>> +		/* If started at the bottom right, decide in which dir to scroll */
>> +		if (t->scroll.edge == (EDGE_RIGHT | EDGE_BOTTOM)) {
>> +			t->scroll.edge &= tp_touch_get_edge(tp, t);
>> +			if (!t->scroll.edge)
>> +				tp_edge_scroll_set_state(tp, t,
>> +						EDGE_SCROLL_TOUCH_STATE_AREA);
>> +		}
>> +		break;
>> +	case SCROLL_EVENT_RELEASE:
>> +		tp_edge_scroll_set_state(tp, t, EDGE_SCROLL_TOUCH_STATE_NONE);
>> +		break;
>> +	}
>> +}
>> +
>> +static void
>> +tp_edge_scroll_handle_area(struct tp_dispatch *tp, struct tp_touch *t,
>> +			   enum scroll_event event)
>> +{
>> +	struct libinput *libinput = tp->device->base.seat->libinput;
>> +
>> +	switch (event) {
>> +	case SCROLL_EVENT_TOUCH:
>> +	case SCROLL_EVENT_TIMEOUT:
>> +		log_bug_libinput(libinput,
>> +				 "unexpect scroll event in area state\n");
>> +		break;
>> +	case SCROLL_EVENT_MOTION:
>> +		break;
>> +	case SCROLL_EVENT_RELEASE:
>> +		tp_edge_scroll_set_state(tp, t, EDGE_SCROLL_TOUCH_STATE_NONE);
>> +		break;
>> +	}
>> +}
>> +
>> +static void
>> +tp_edge_scroll_handle_event(struct tp_dispatch *tp, struct tp_touch *t,
>> +			    enum scroll_event event)
>> +{
>> +	switch (t->scroll.state) {
>> +	case EDGE_SCROLL_TOUCH_STATE_NONE:
>> +		tp_edge_scroll_handle_none(tp, t, event);
>> +		break;
>> +	case EDGE_SCROLL_TOUCH_STATE_EDGE_NEW:
>> +		tp_edge_scroll_handle_edge_new(tp, t, event);
>> +		break;
>> +	case EDGE_SCROLL_TOUCH_STATE_EDGE:
>> +		tp_edge_scroll_handle_edge(tp, t, event);
>> +		break;
>> +	case EDGE_SCROLL_TOUCH_STATE_AREA:
>> +		tp_edge_scroll_handle_area(tp, t, event);
>> +		break;
>> +	}
>> +}
>> +
>> +static void
>> +tp_edge_scroll_handle_timeout(uint64_t now, void *data)
>> +{
>> +	struct tp_touch *t = data;
>> +
>> +	tp_edge_scroll_handle_event(t->tp, t, SCROLL_EVENT_TIMEOUT);
>> +}
>> +
>> +int
>> +tp_edge_scroll_init(struct tp_dispatch *tp, struct evdev_device *device)
>> +{
>> +	struct tp_touch *t;
>> +	int width, height;
>> +	int edge_width, edge_height;
>> +
>> +	width = abs(device->abs.absinfo_x->maximum -
>> +		    device->abs.absinfo_x->minimum);
>> +	height = abs(device->abs.absinfo_y->maximum -
>> +		     device->abs.absinfo_y->minimum);
> 
> huh, I only just noticed that. we don't need the abs() here.

Fixed for v2.

> 
>> +
>> +	switch (tp->model) {
>> +	case MODEL_SYNAPTICS:
>> +		edge_width = width * .07;
>> +		edge_height = height * .07;
>> +		break;
>> +	case MODEL_ALPS:
>> +		edge_width = width * .15;
>> +		edge_height = height * .15;
>> +		break;
>> +	case MODEL_APPLETOUCH:
>> +	case MODEL_UNIBODY_MACBOOK:
> 
> the unibody macbooks already had a clickpad, so this isn't needed
> 
>> +		edge_width = width * .085;
>> +		edge_height = height * .085;
>> +		break;
>> +	default:
>> +		edge_width = width * .04;
>> +		edge_height = height * .054;
>> +	}
> 
> fwiw, there is a bit of history there and many oddities are based on a
> the edges in synaptics being 'wrong'. the kernel exports the synaptics
> dimensions as the coordinates produced by 'an average finger', but it's not
> hard to go beyond min/max. iirc for alps and other devices the edges are the
> actual edges (and for the T440-style synaptics pads that we manually fixed
> up). Hence the need for different edge zones on synaptics.
> 
> And the default numbers come from the synaptics user interface guide.
>     Typical bezel limits: x 1472–5472  y 1408–4448 
>     Typical edge margins: x 1632–5312  y 1568–4288 
> i.e. 4% and 5.4% are interpolated from those
> 
> so in short, we don't need MODEL_SYNAPTICS, it's covered by the defaults.
> 
> that just leaves APPLETOUCH and ALPS, I wonder if these have resolutions
> set (the synaptics code pre-dates resolution support in the kernel). if so,
> just defining a sensible size for the edge is enough (10mm?). or based on
> the diagonal.

As also mentioned in a private mail not all alps devices have resolution set,
so at least for some devices we will need to take a percentage of width / height
(no idea why you mention diagonal here).

> 
>> +
>> +	tp->scroll.right_edge = device->abs.absinfo_x->maximum - edge_width;
>> +	tp->scroll.bottom_edge = device->abs.absinfo_y->maximum - edge_height;
>> +
>> +	tp_for_each_touch(tp, t) {
>> +		t->scroll.last_axis = -1;
>> +		t->scroll.threshold = DEFAULT_SCROLL_THRESHOLD;
>> +		libinput_timer_init(&t->scroll.timer, 
>> +				    device->base.seat->libinput,
>> +				    tp_edge_scroll_handle_timeout, t);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +void
>> +tp_destroy_edge_scroll(struct tp_dispatch *tp)
>> +{
>> +	struct tp_touch *t;
>> +
>> +	tp_for_each_touch(tp, t)
>> +		libinput_timer_cancel(&t->scroll.timer);
>> +}
>> +
>> +void
>> +tp_edge_scroll_handle_state(struct tp_dispatch *tp, uint64_t time)
>> +{
>> +	struct tp_touch *t;
>> +
>> +	tp_for_each_touch(tp, t) {
>> +		if (!t->dirty)
>> +			continue;
>> +
>> +		switch (t->state) {
>> +		case TOUCH_NONE:
>> +			break;
>> +		case TOUCH_BEGIN:
>> +			tp_edge_scroll_handle_event(tp, t, SCROLL_EVENT_TOUCH);
>> +			break;
>> +		case TOUCH_UPDATE:
>> +			tp_edge_scroll_handle_event(tp, t, SCROLL_EVENT_MOTION);
>> +			break;
>> +		case TOUCH_END:
>> +			tp_edge_scroll_handle_event(tp, t, SCROLL_EVENT_RELEASE);
>> +			break;
>> +		}
>> +	}
>> +}
>> +
>> +int 
>> +tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
>> +{
>> +	struct libinput_device *device = &tp->device->base;
>> +	struct tp_touch *t;
>> +	enum libinput_pointer_axis axis;
>> +	double dx, dy, *delta;
>> +
>> +	tp_for_each_touch(tp, t) {
>> +		if (!t->dirty)
>> +			continue;
>> +
>> +		switch (t->scroll.edge) {
>> +			case EDGE_NONE:
>> +				if (t->scroll.last_axis != -1) {
>> +					/* Send stop scroll event */
>> +					pointer_notify_axis(device, time,
>> +						t->scroll.last_axis, 0.0);
>> +					t->scroll.last_axis = -1;
>> +				}
>> +				continue;
>> +			case EDGE_RIGHT:
>> +				axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
>> +				delta = &dy;
>> +				break;
>> +			case EDGE_BOTTOM:
>> +				axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
>> +				delta = &dx;
>> +				break;
>> +			default: /* EDGE_RIGHT | EDGE_BOTTOM */
>> +				continue; /* Don't know direction yet, skip */
>> +		}
>> +
>> +		tp_get_delta(t, &dx, &dy);
>> +		tp_filter_motion(tp, &dx, &dy, time);
>> +
>> +		if (fabs(*delta) < t->scroll.threshold)
>> +			continue;
>> +
>> +		pointer_notify_axis(device, time, axis, *delta);
>> +		t->scroll.last_axis = axis;
>> +
>> +		if (t->scroll.state == EDGE_SCROLL_TOUCH_STATE_EDGE_NEW) {
>> +			tp_edge_scroll_set_state(tp, t,
>> +						 EDGE_SCROLL_TOUCH_STATE_EDGE);
>> +		}
> 
> what happens if two touches are moving in the same edge zone? should we
> simply say "we've moved vertically already, ignore any other vertical edge
> movement"?

I've chosen to make edge scrolling 101% er per touch thing, which keeps
things nice and simple. So if the user does this both fingers generate independent
scroll events.

> 
>> +	}
>> +
>> +	return 0; /* Edge touches are suppressed by edge_scroll_touch_active */
>> +}
>> +
>> +void
>> +tp_edge_scroll_stop(struct tp_dispatch *tp, uint64_t time)
>> +{
>> +	struct libinput_device *device = &tp->device->base;
>> +	struct tp_touch *t;
>> +
>> +	tp_for_each_touch(tp, t) {
>> +		if (t->scroll.last_axis != -1) {
>> +			pointer_notify_axis(device, time,
>> +					    t->scroll.last_axis, 0.0);
>> +			t->scroll.last_axis = -1;
>> +		}
>> +	}
>> +}
>> +
>> +int
>> +tp_edge_scroll_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
>> +{
>> +	return t->scroll.state == EDGE_SCROLL_TOUCH_STATE_AREA;
>> +}
>> diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
>> index d2ee6ea..fb78fcd 100644
>> --- a/src/evdev-mt-touchpad.c
>> +++ b/src/evdev-mt-touchpad.c
>> @@ -56,7 +56,7 @@ tp_motion_history_offset(struct tp_touch *t, int offset)
>>  	return &t->history.samples[offset_index];
>>  }
>>  
>> -static void
>> +void
>>  tp_filter_motion(struct tp_dispatch *tp,
>>  	         double *dx, double *dy, uint64_t time)
>>  {
>> @@ -339,7 +339,9 @@ tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
>>  {
>>  	return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
>>  		!t->palm.is_palm &&
>> -		!t->pinned.is_pinned && tp_button_touch_active(tp, t);
>> +		!t->pinned.is_pinned &&
>> +		tp_button_touch_active(tp, t) &&
>> +		tp_edge_scroll_touch_active(tp, t);
>>  }
>>  
>>  void
>> @@ -431,6 +433,7 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time)
>>  	}
>>  
>>  	tp_button_handle_state(tp, time);
>> +	tp_edge_scroll_handle_state(tp, time);
> 
> let's move scrolling into a 2-tier hierarchy, so any call is to
> tp_scroll_foo() and then splits up to:
>    -> tp_scroll_2fg_foo
>    -> tp_scroll_edge_foo
> 
> that makes the code easier to follow and more consistent. that hierarchy is
> partially there already (tp_post_scroll_events() for example) but missing
> from here and e.g. tp_init.

Good idea, will fix for v2.

> 
>>  
>>  	/*
>>  	 * We have a physical button down event on a clickpad. To avoid
>> @@ -503,15 +506,12 @@ tp_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
>>  }
>>  
>>  static int
>> -tp_post_scroll_events(struct tp_dispatch *tp, uint64_t time)
>> +tp_2fg_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
> 
> we have at least one call "...twofinger_scroll", this needs to be consistent.

Will fix for v2.

>>  {
>>  	struct tp_touch *t;
>>  	int nfingers_down = 0;
>>  
>> -	if (tp->scroll.mode != LIBINPUT_CONFIG_SCROLL_2FG)
>> -		return 0;
>> -
>> -	/* No scrolling during tap-n-drag */
>> +	/* No 2fg scrolling during tap-n-drag */
>>  	if (tp_tap_dragging(tp))
>>  		return 0;
>>  
>> @@ -530,6 +530,22 @@ tp_post_scroll_events(struct tp_dispatch *tp, uint64_t time)
>>  	return 1;
>>  }
>>  
>> +static int
>> +tp_post_scroll_events(struct tp_dispatch *tp, uint64_t time)
>> +{
>> +	switch (tp->scroll.mode) {
>> +	case LIBINPUT_CONFIG_SCROLL_NO_SCROLL:
>> +		return 0;
>> +	case LIBINPUT_CONFIG_SCROLL_2FG:
>> +		return tp_2fg_scroll_post_events(tp, time);
>> +	case LIBINPUT_CONFIG_SCROLL_EDGE:
>> +		return tp_edge_scroll_post_events(tp, time);
>> +	case LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN:
>> +		return 0; /* Not supported */
> 
> libinput_log_bug()? 
> 
>> +	}
>> +	return 0; /* Never reached */
>> +}
>> +
>>  static void
>>  tp_post_events(struct tp_dispatch *tp, uint64_t time)
>>  {
>> @@ -626,6 +642,7 @@ tp_destroy(struct evdev_dispatch *dispatch)
>>  	tp_destroy_tap(tp);
>>  	tp_destroy_buttons(tp);
>>  	tp_destroy_sendevents(tp);
>> +	tp_destroy_edge_scroll(tp);
> 
> tp_destroy_scroll() -> tp_destroy_edge_scroll()
> 
>>  
>>  	free(tp->touches);
>>  	free(tp);
>> @@ -958,6 +975,9 @@ tp_scroll_config_scroll_mode_get_modes(struct libinput_device *device)
>>  	if (tp->ntouches >= 2)
>>  		modes |= LIBINPUT_CONFIG_SCROLL_2FG;
>>  
>> +	if (!tp->buttons.is_clickpad)
>> +		modes |= LIBINPUT_CONFIG_SCROLL_EDGE;
>> +
>>  	return modes;
>>  }
>>  
>> @@ -971,7 +991,18 @@ tp_scroll_config_scroll_mode_set_mode(struct libinput_device *device,
>>  	if (mode == tp->scroll.mode)
>>  		return LIBINPUT_CONFIG_STATUS_SUCCESS;
>>  
>> -	evdev_stop_scroll(evdev, libinput_now(device->seat->libinput));
>> +	switch (tp->scroll.mode) {
>> +	case LIBINPUT_CONFIG_SCROLL_NO_SCROLL:
>> +	case LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN:
>> +		break; /* Not supported */
>> +	case LIBINPUT_CONFIG_SCROLL_2FG:
>> +		evdev_stop_scroll(evdev, libinput_now(device->seat->libinput));
>> +		break;
>> +	case LIBINPUT_CONFIG_SCROLL_EDGE:
>> +		tp_edge_scroll_stop(tp, libinput_now(device->seat->libinput));
>> +		break;
>> +	}
>> +
>>  	tp->scroll.mode = mode;
>>  
>>  	return LIBINPUT_CONFIG_STATUS_SUCCESS;
>> @@ -989,7 +1020,13 @@ tp_scroll_config_scroll_mode_get_mode(struct libinput_device *device)
>>  static enum libinput_config_scroll_mode
>>  tp_scroll_config_scroll_mode_get_default_mode(struct libinput_device *device)
>>  {
>> -	return LIBINPUT_CONFIG_SCROLL_2FG;
>> +	struct evdev_device *evdev = (struct evdev_device*)device;
>> +	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
>> +
>> +	if (tp->ntouches >= 2)
>> +		return LIBINPUT_CONFIG_SCROLL_2FG;
>> +	else
>> +		return LIBINPUT_CONFIG_SCROLL_EDGE;
>>  }
>>  
>>  static int
>> @@ -1096,6 +1133,9 @@ tp_init(struct tp_dispatch *tp,
>>  	if (tp_init_scroll(tp) != 0)
>>  		return -1;
>>  
>> +	if (tp_edge_scroll_init(tp, device) != 0)
>> +		return -1;
> 
> tp_scroll_init() -> tp_edge_scroll_init()
> 
>> +
>>  	device->seat_caps |= EVDEV_DEVICE_POINTER;
>>  
>>  	return 0;
>> @@ -1239,6 +1279,7 @@ evdev_mt_touchpad_create(struct evdev_device *device)
>>  
>>  	tp->model = tp_get_model(device);
>>  
>> +	device->dispatch = &tp->base;
> 
> I guess this is needed for some check or another? if so, can we either pass
> in the data that's needed, or alternatively make it consistent that the
> create method sets device->dispatch for the fallback interface as well.

This is needed because the initial value for tp->scroll.mode is set
by calling tp_scroll_config_scroll_mode_get_default_mode(device).

Regards,

Hans


More information about the wayland-devel mailing list