Re: [PATCH libinput 01/10] Replace pointer acceleration with a much simpler linear one

2014-09-21 Thread Peter Hutterer
On Fri, Sep 19, 2014 at 10:09:58AM +0100, Steven Newbury wrote:
 On Fri, 2014-09-19 at 15:44 +1000, Peter Hutterer wrote:
  We ran a userstudy, evaluating three different accel methods. 
  Detailed results are
  available at:
 
  http://www.who-t.net/publications/hutterer2014_libinput_ptraccel_study.pdf
  
  We found that there was little difference between the method we had 
  in
  libinput 0.6 and this three-line function. Users didn't really 
  notice a
  difference, but measured data suggests that it has slight advantages 
  in some
  use-cases.
  
  The method proposed here is the one labeled linear in the paper, 
  its profile
  looks roughly like this:
  
  _
 /
/
   /
  /
  
  where the x axis is the speed, y is the acceleration factor.
  The first plateau is at the acceleration factor 1 (i.e. unaccelerated
  movement), the second plateau is at the max acceleration factor. The 
  threshold
  in the code defines where and how long the plateau is.
  
  Differences to the previous accel function:
  - both inclines are linear rather than curved
  - the second incline is less steep than the current method
  
  From a maintainer's point-of-view, this function is significantly 
  easier to
  understand and manipulate than the previous one.
  
 Hi Peter,
 I'm going to read the report, but I would like to just state my 
 initial feeling on this.  Right now, in my experience*, the pointer 
 acceleration in libinput/weston is inferior** to that currently in 
 Xorg.  I find it relatively difficult to accurately position the 
 pointer in Weston while at the same time being able to comfortably 
 move the pointer over larger distances in a predictable manner.  The 
 Xorg code has been very well tuned over the last few years and IMHO is 
 at least as good as the Windows implementation (from a user point of 
 view).

The X server code has a bug where the size of the output screen influences
the acceleration on certain devices, touchpad devices included. Plug a
second monitor in and you'll see the touchpad speed go up. I've tried to fix
this a number of times, but stuff is so convoluted that I need to find a
spare week or two to fix this across synaptics and the server and update
both in lockstep.

I'd have to check the git logs too, but afaict the pointer accel code hasn't
seen any real updates since when it was introduced, with the exception of
some general cleanups and dropping of configurable schemes. So well tuned
is stretching the term, it just happens to work and you've had up to ~6
years of exposure to it. This is of course not discrediting your
experience, just remember that there is a learning curve associate with
pointer acceleration and you need some time to get used to how things work
before they become muscle memory.
 
 Any comparison between the libinput/weston implementations needs to be 
 base-lined against current Xorg and Windows/OSX!  I'm certainly not 
 opposed to improvements!

how? That's the main problem I had trying to analyse.
the study I ran has a couple of flaws in its design and mostly the
small data set but one of the abilities libinput provides us is comparable
data from multiple runs. That is almost impossible with X (see the screen
size issue for example), on top of the difficulty of getting this data out
of X in the first place.  Comparing to Windows/OS X is even more difficult
since we can't even assume the same events as baseline. I've looked at the
windows and OS X acceleration curves (see the Casiez et al. paper I
referenced in the study writeup for graphs of how they work) and tested them
locally but did not see a significant improvement.  If anything an OS X-like
curve made it even harder to use for some reason (note: subjective!).

Note that I'm not against further improvements down the road. I still think
that touchpads and mice need different accel curves (the linear one here is
not as easy to use on a touchpad as on my mouse here) but I don't have the
data to be sure. I could run the study again, modified a bit but right now
there are more pressing issues. The code is online however so if you want to
take it and continue with it I'm happy to help (or if you just find general
bugs that invalidate some of the results, though I guess my help won't be as
happy then ;)

There's also an argument that the delta smoothening that we do before
applying the accel curve may not be necessary. That needs separate
evaluation though and the day has only so many hours.

 Maybe my concerns are unfounded, I'll have a read of the report now... 
 :-)
 
 * Using a Synaptics touchpad
 
 ** For all I know it's the same code (haven't checked) but the 
 parameters must be different or there's a bug somewhere.

It should be mostly the same code (for relative devices anyway), but I'd
need to do a full audit to verify.

Cheers,
   Peter

___
wayland-devel mailing list
wayland-devel@lists.freedesktop.org

[PATCH v2 libinput 05.5/10] touchpad: use the evdev device's filter struct

2014-09-21 Thread Peter Hutterer
We don't need a separate filter struct, we can use the parent evdev device.

Signed-off-by: Peter Hutterer peter.hutte...@who-t.net
---
Changes to v1:
- didn't exist in the first set of patches, slots in after 05/10
- prep work for having the same code for pointer accel functions

 src/evdev-mt-touchpad.c | 5 ++---
 src/evdev-mt-touchpad.h | 2 --
 2 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index 0b51809..79f5357 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -65,7 +65,7 @@ tp_filter_motion(struct tp_dispatch *tp,
motion.dy = *dy * tp-accel.y_scale_coeff;
 
if (motion.dx != 0.0 || motion.dy != 0.0)
-   filter_dispatch(tp-filter, motion, tp, time);
+   filter_dispatch(tp-device-pointer.filter, motion, tp, time);
 
*dx = motion.dx;
*dy = motion.dy;
@@ -602,7 +602,6 @@ tp_destroy(struct evdev_dispatch *dispatch)
tp_destroy_tap(tp);
tp_destroy_buttons(tp);
 
-   filter_destroy(tp-filter);
free(tp-touches);
free(tp);
 }
@@ -845,7 +844,7 @@ tp_init_accel(struct tp_dispatch *tp, double diagonal)
if (accel == NULL)
return -1;
 
-   tp-filter = accel;
+   tp-device-pointer.filter = accel;
 
return 0;
 }
diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
index 3d3932b..73d0c91 100644
--- a/src/evdev-mt-touchpad.h
+++ b/src/evdev-mt-touchpad.h
@@ -167,8 +167,6 @@ struct tp_dispatch {
int32_t margin_y;
} hysteresis;
 
-   struct motion_filter *filter;
-
struct {
double x_scale_coeff;
double y_scale_coeff;
-- 
1.9.3

___
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/wayland-devel


[PATCH v2 libinput 08/10] touchpad: hook up pointer acceleration configuration

2014-09-21 Thread Peter Hutterer
Signed-off-by: Peter Hutterer peter.hutte...@who-t.net
---
Changes to v1:
- don't duplicate the accel config code, just re-use it for touchpad


 src/evdev-mt-touchpad.c | 7 +--
 src/evdev.c | 6 +++---
 src/evdev.h | 3 +++
 3 files changed, 7 insertions(+), 9 deletions(-)

diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index 79f5357..167cea9 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -802,7 +802,6 @@ tp_init_slots(struct tp_dispatch *tp,
 static int
 tp_init_accel(struct tp_dispatch *tp, double diagonal)
 {
-   struct motion_filter *accel;
int res_x, res_y;
 
if (tp-has_mt) {
@@ -839,13 +838,9 @@ tp_init_accel(struct tp_dispatch *tp, double diagonal)
tp-accel.y_scale_coeff = DEFAULT_ACCEL_NUMERATOR / diagonal;
}
 
-   accel = create_pointer_accelator_filter(
-   pointer_accel_profile_linear);
-   if (accel == NULL)
+   if (evdev_device_init_pointer_acceleration(tp-device) == -1)
return -1;
 
-   tp-device-pointer.filter = accel;
-
return 0;
 }
 
diff --git a/src/evdev.c b/src/evdev.c
index a5a8372..4c0669c 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -893,8 +893,8 @@ evdev_accel_config_get_default_speed(struct libinput_device 
*device)
return 0.0;
 }
 
-static int
-configure_pointer_acceleration(struct evdev_device *device)
+int
+evdev_device_init_pointer_acceleration(struct evdev_device *device)
 {
device-pointer.filter =
create_pointer_accelator_filter(
@@ -1090,7 +1090,7 @@ evdev_configure_device(struct evdev_device *device)
has_keyboard = 1;
 
if ((has_abs || has_rel)  has_button) {
-   if (configure_pointer_acceleration(device) == -1)
+   if (evdev_device_init_pointer_acceleration(device) == -1)
return -1;
 
device-seat_caps |= EVDEV_DEVICE_POINTER;
diff --git a/src/evdev.h b/src/evdev.h
index 301903a..23615f9 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -177,6 +177,9 @@ evdev_device_create(struct libinput_seat *seat,
const char *sysname,
const char *syspath);
 
+int
+evdev_device_init_pointer_acceleration(struct evdev_device *device);
+
 struct evdev_dispatch *
 evdev_touchpad_create(struct evdev_device *device);
 
-- 
1.9.3

___
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/wayland-devel


[PATCH v2 libinput 05/10] filter: adjust acceleration curve depending on speed

2014-09-21 Thread Peter Hutterer
The acceleration curve consists of four parts, in ascii-art like this:
_
   /
  /
 /
/

where the x axis is the speed, y is the acceleration factor.
The first plateau is at the acceleration factor 1 (i.e. unaccelerated
movement), the second plateau is at the max acceleration factor. The threshold
in the code defines where and how long the plateau is.

This patch adjusts the curve based on a [-1, 1] range. For anything below 0,
the plateau is longer (i.e. accel kicks in at a higher speed), the second
incline is flatter (i.e. accel kicks in slower) and the max accel factor is
lower (i.e. maximum speed is slower). For anything above 0, the inverse is
true, acceleration kicks in earlier, harder and is faster in general. So the
default/min/max curves overlaid look something like this:
   max
 | ___ default
| /  _ min
  _|_/_/
 /
/

Note that there's a limit to what ascii art can do...

Note that there are additional tweaks we can introduce later, such as
decreaseing the unaccelerated speed of the device (i.e. lowering the first
plateau).

Signed-off-by: Peter Hutterer peter.hutte...@who-t.net
---
Changes to v1:
- change accel adjustments to use the defaults at speed 0

 src/filter.c | 19 +--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/src/filter.c b/src/filter.c
index 327dbce..defbcae 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -69,6 +69,7 @@ filter_get_speed(struct motion_filter *filter)
 
 #define DEFAULT_THRESHOLD 0.4  /* in units/ms */
 #define DEFAULT_ACCELERATION 2.0   /* unitless factor */
+#define DEFAULT_INCLINE 1.1/* unitless factor */
 
 /*
  * Pointer acceleration filter constants
@@ -101,6 +102,7 @@ struct pointer_accelerator {
 
double threshold;   /* units/ms */
double accel;   /* unitless factor */
+   double incline; /* incline of the function */
 };
 
 static void
@@ -255,10 +257,21 @@ static bool
 accelerator_set_speed(struct motion_filter *filter,
  double speed)
 {
+   struct pointer_accelerator *accel_filter =
+   (struct pointer_accelerator *)filter;
+
assert(speed = -1.0  speed = 1.0);
 
+   /* delay when accel kicks in */
+   accel_filter-threshold = DEFAULT_THRESHOLD - speed/6.0;
+
+   /* adjust max accel factor */
+   accel_filter-accel = DEFAULT_ACCELERATION + speed;
+
+   /* higher speed - faster to reach max */
+   accel_filter-incline = DEFAULT_INCLINE + speed/2.0;
+
filter-speed = speed;
-
return true;
 }
 
@@ -290,6 +303,7 @@ create_pointer_accelator_filter(accel_profile_func_t 
profile)
 
filter-threshold = DEFAULT_THRESHOLD;
filter-accel = DEFAULT_ACCELERATION;
+   filter-incline = DEFAULT_INCLINE;
 
return filter-base;
 }
@@ -314,9 +328,10 @@ pointer_accel_profile_linear(struct motion_filter *filter,
double s1, s2;
const double max_accel = accel_filter-accel; /* unitless factor */
const double threshold = accel_filter-threshold; /* units/ms */
+   const double incline = accel_filter-incline;
 
s1 = min(1, speed_in * 5);
-   s2 = 1 + (speed_in - threshold) * 1.1;
+   s2 = 1 + (speed_in - threshold) * incline;
 
return min(max_accel, s2  1 ? s2 : s1);
 }
-- 
1.9.3

___
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/wayland-devel


Re: [PATCH libinput 1/3] core: Add internal event notification mechanism

2014-09-21 Thread Peter Hutterer
On Thu, Sep 18, 2014 at 01:11:03PM +0200, Hans de Goede wrote:
 For features like e.g. disable-touchpad-while-typing, it is necessary for one
 device to be able to listen into another device's events.
 
 It is tempting to use the existing device_added / device_removed mechanism
 to give e.g. the keyboard a link to the touchpad, and make the keyboard code
 disable / re-enable the touchpad but this wrong. This needs to be a setting
 of the touchpad, and the policy for things like which events to count as
 activity, and what sort of timeout to use to consider the device idle, belongs
 in the touchpad code not in the keyboard code.
 
 Add an event listeners mechanism so that the touchpad can listen for (e.g.)
 keyboard events, and respond to these itself.
 
 Signed-off-by: Hans de Goede hdego...@redhat.com
 ---
  src/libinput-private.h | 18 ++
  src/libinput.c | 27 +++
  2 files changed, 45 insertions(+)
 
 diff --git a/src/libinput-private.h b/src/libinput-private.h
 index cf03c03..94d6580 100644
 --- a/src/libinput-private.h
 +++ b/src/libinput-private.h
 @@ -118,12 +118,19 @@ struct libinput_device_config {
  struct libinput_device {
   struct libinput_seat *seat;
   struct list link;
 + struct list event_listeners;
   void *user_data;
   int terminated;
   int refcount;
   struct libinput_device_config config;
  };
  
 +struct libinput_event_listener {
 + struct list link;
 + void (*notify_func)(struct libinput_event *ev, void *notify_func_data);
 + void *notify_func_data;
 +};
 +
  typedef void (*libinput_source_dispatch_t)(void *data);
  
  
 @@ -180,6 +187,17 @@ libinput_device_init(struct libinput_device *device,
struct libinput_seat *seat);
  
  void
 +libinput_device_add_eventlistener(struct libinput_device *device,

event_listener vs eventlistener here, please use the former. otherwise:
Reviewed-by: Peter Hutterer peter.hutte...@who-t.net

Cheers,
   Peter


 +   struct libinput_event_listener *listener,
 +   void (*notify_func)(
 + struct libinput_event *event,
 + void *notify_func_data),
 +   void *notify_func_data);
 +
 +void
 +libinput_device_remove_eventlistener(struct libinput_event_listener 
 *listener);
 +
 +void
  notify_added_device(struct libinput_device *device);
  
  void
 diff --git a/src/libinput.c b/src/libinput.c
 index 14f0257..79ae90a 100644
 --- a/src/libinput.c
 +++ b/src/libinput.c
 @@ -674,6 +674,7 @@ libinput_device_init(struct libinput_device *device,
  {
   device-seat = seat;
   device-refcount = 1;
 + list_init(device-event_listeners);
  }
  
  LIBINPUT_EXPORT struct libinput_device *
 @@ -686,6 +687,7 @@ libinput_device_ref(struct libinput_device *device)
  static void
  libinput_device_destroy(struct libinput_device *device)
  {
 + assert(list_empty(device-event_listeners));
   evdev_device_destroy((struct evdev_device *) device);
  }
  
 @@ -732,6 +734,25 @@ libinput_dispatch(struct libinput *libinput)
   return 0;
  }
  
 +void
 +libinput_device_add_eventlistener(struct libinput_device *device,
 +   struct libinput_event_listener *listener,
 +   void (*notify_func)(
 + struct libinput_event *event,
 + void *notify_func_data),
 +   void *notify_func_data)
 +{
 + listener-notify_func = notify_func;
 + listener-notify_func_data = notify_func_data;
 + list_insert(device-event_listeners, listener-link);
 +}
 +
 +void
 +libinput_device_remove_eventlistener(struct libinput_event_listener 
 *listener)
 +{
 + list_remove(listener-link);
 +}
 +
  static uint32_t
  update_seat_key_count(struct libinput_seat *seat,
 int32_t key,
 @@ -798,7 +819,13 @@ post_device_event(struct libinput_device *device,
 enum libinput_event_type type,
 struct libinput_event *event)
  {
 + struct libinput_event_listener *listener, *tmp;
 +
   init_event_base(event, device, type);
 +
 + list_for_each_safe(listener, tmp, device-event_listeners, link)
 + listener-notify_func(event, listener-notify_func_data);
 +
   libinput_post_event(device-seat-libinput, event);
  }
  
 -- 
 2.1.0
 
 ___
 wayland-devel mailing list
 wayland-devel@lists.freedesktop.org
 http://lists.freedesktop.org/mailman/listinfo/wayland-devel
 
___
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/wayland-devel


Re: [PATCH libinput 2/3] core: Move libinput_event definition to libinput-private.h

2014-09-21 Thread Peter Hutterer
On Thu, Sep 18, 2014 at 01:11:04PM +0200, Hans de Goede wrote:
 Signed-off-by: Hans de Goede hdego...@redhat.com

Reviewed-by: Peter Hutterer peter.hutte...@who-t.net


 ---
  src/libinput-private.h | 5 +
  src/libinput.c | 5 -
  2 files changed, 5 insertions(+), 5 deletions(-)
 
 diff --git a/src/libinput-private.h b/src/libinput-private.h
 index 94d6580..3b46c27 100644
 --- a/src/libinput-private.h
 +++ b/src/libinput-private.h
 @@ -125,6 +125,11 @@ struct libinput_device {
   struct libinput_device_config config;
  };
  
 +struct libinput_event {
 + enum libinput_event_type type;
 + struct libinput_device *device;
 +};
 +
  struct libinput_event_listener {
   struct list link;
   void (*notify_func)(struct libinput_event *ev, void *notify_func_data);
 diff --git a/src/libinput.c b/src/libinput.c
 index 79ae90a..364890e 100644
 --- a/src/libinput.c
 +++ b/src/libinput.c
 @@ -42,11 +42,6 @@ struct libinput_source {
   struct list link;
  };
  
 -struct libinput_event {
 - enum libinput_event_type type;
 - struct libinput_device *device;
 -};
 -
  struct libinput_event_device_notify {
   struct libinput_event base;
  };
 -- 
 2.1.0
 
 ___
 wayland-devel mailing list
 wayland-devel@lists.freedesktop.org
 http://lists.freedesktop.org/mailman/listinfo/wayland-devel
 
___
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/wayland-devel


Re: [PATCH libinput 3/3] touchpad: Disable touchpads on trackpoint activity

2014-09-21 Thread Peter Hutterer
On Thu, Sep 18, 2014 at 01:11:05PM +0200, Hans de Goede wrote:
 Some laptops with both a clickpad and a trackpoint have such a large touchpad,
 that parts of the users hands will touch the pad when using the trackpoint.
 Examples of this are the Lenovo T440s and the Toshiba Tecra Z40-A.
 
 This commit makes libinput automatically disable the touchpad while using
 the trackpoint on these devices, except for the buttons, as people may want
 to use the touchpad (hardware or soft) buttons while using the trackpoint.
 
 Signed-off-by: Hans de Goede hdego...@redhat.com
 ---
  src/evdev-mt-touchpad.c | 64 
 +++--
  src/evdev-mt-touchpad.h |  3 +++
  2 files changed, 65 insertions(+), 2 deletions(-)
 
 diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
 index 1c32cc6..31e2e83 100644
 --- a/src/evdev-mt-touchpad.c
 +++ b/src/evdev-mt-touchpad.c
 @@ -31,6 +31,7 @@
  
  #define DEFAULT_ACCEL_NUMERATOR 1200.0
  #define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0
 +#define DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT 500 /* ms */
  
  static inline int
  tp_hysteresis(int in, int center, int margin)
 @@ -523,8 +524,8 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time)
   double dx, dy;
   int consumed = 0;
  
 - /* Only post (top) button events while suspended */
 - if (tp-device-suspended) {
 + /* Only post (top) button events? */
 + if (tp-device-suspended || tp-sendevents.trackpoint_active) {
   tp_post_button_events(tp, time);
   return;
   }
 @@ -594,6 +595,16 @@ tp_process(struct evdev_dispatch *dispatch,
  }
  
  static void
 +tp_destroy_sendevents(struct tp_dispatch *tp)
 +{
 + libinput_timer_cancel(tp-sendevents.trackpoint_timer);
 +
 + if (tp-buttons.trackpoint)
 + libinput_device_remove_eventlistener(
 + tp-sendevents.trackpoint_listener);
 +}
 +
 +static void
  tp_destroy(struct evdev_dispatch *dispatch)
  {
   struct tp_dispatch *tp =
 @@ -601,6 +612,7 @@ tp_destroy(struct evdev_dispatch *dispatch)
  
   tp_destroy_tap(tp);
   tp_destroy_buttons(tp);
 + tp_destroy_sendevents(tp);
  
   filter_destroy(tp-filter);
   free(tp-touches);
 @@ -667,6 +679,36 @@ tp_resume(struct tp_dispatch *tp, struct evdev_device 
 *device)
  }
  
  static void
 +tp_trackpoint_timeout(uint64_t now, void *data)
 +{
 + struct tp_dispatch *tp = data;
 +
 + tp_clear_state(tp, tp-device);
 + tp-sendevents.trackpoint_active = false;
 +}
 +
 +static void
 +tp_trackpoint_event(struct libinput_event *event, void *data)
 +{
 + struct tp_dispatch *tp = data;
 +
 + /* Only movement counts as trackpad activity, as people may use
 +the trackpoint buttons in combination with the touchpad. */
 + if (event-type != LIBINPUT_EVENT_POINTER_MOTION)
 + return;

the wheel emulation code posts axis events, you'll need to include those
here as well. 

 +
 + if (!tp-sendevents.trackpoint_active) {
 + tp_clear_state(tp, tp-device);
 + tp-sendevents.trackpoint_active = true;
 + }
 +
 + /* We don't use the event time as that may wrap */
 + libinput_timer_set(tp-sendevents.trackpoint_timer,
 +libinput_now(tp-device-base.seat-libinput) +
 + DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT);

Can you expand on this?
it seems like an odd comment given that we use the event time elsewhere.
it also provides inconsistent behaviour, if libinput is slow to read the
event the timeout is set from when it gets processes as opposed to when the
physical interaction happened. That's something we should avoid.

other than that, Reviewed-by: Peter Hutterer peter.hutte...@who-t.net

Cheers,
   Peter

 +}
 +
 +static void
  tp_device_added(struct evdev_device *device,
   struct evdev_device *added_device)
  {
 @@ -677,6 +719,9 @@ tp_device_added(struct evdev_device *device,
   /* Don't send any pending releases to the new trackpoint */
   tp-buttons.active_is_top = 0;
   tp-buttons.trackpoint = added_device;
 + libinput_device_add_eventlistener(added_device-base,
 + tp-sendevents.trackpoint_listener,
 + tp_trackpoint_event, tp);
   }
  
   if (tp-sendevents.current_mode !=
 @@ -700,6 +745,8 @@ tp_device_removed(struct evdev_device *device,
   tp-buttons.active = 0;
   tp-buttons.active_is_top = 0;
   }
 + libinput_device_remove_eventlistener(
 + tp-sendevents.trackpoint_listener);
   tp-buttons.trackpoint = NULL;
   }
  
 @@ -886,6 +933,16 @@ tp_init_palmdetect(struct tp_dispatch *tp,
  }
  
  static int
 +tp_init_sendevents(struct tp_dispatch *tp,
 +struct evdev_device *device)
 +{
 +