Merge branch 'master' into tablet-support

This commit is contained in:
Peter Hutterer 2015-07-14 13:13:11 +10:00
commit 9e705bf334
61 changed files with 1732 additions and 284 deletions

View file

@ -194,6 +194,12 @@ AM_CONDITIONAL(HAVE_VALGRIND, [test "x$VALGRIND" != "x"])
AM_CONDITIONAL(BUILD_TESTS, [test "x$build_tests" = "xyes"])
AM_CONDITIONAL(BUILD_DOCS, [test "x$build_documentation" = "xyes"])
# Used by the udev rules so we can use callouts during testing without
# installing everything first. Default is the empty string so the installed
# rule will use udev's default path. Override is in udev/Makefile.am
AC_SUBST(UDEV_TEST_PATH, "")
AC_PATH_PROG(SED, [sed])
AC_CONFIG_FILES([Makefile
doc/Makefile
doc/libinput.doxygen
@ -202,7 +208,9 @@ AC_CONFIG_FILES([Makefile
src/libinput-version.h
test/Makefile
tools/Makefile
udev/Makefile])
udev/Makefile
udev/80-libinput-device-groups.rules
udev/90-libinput-model-quirks.rules])
AC_CONFIG_FILES([test/symbols-leak-test],
[chmod +x test/symbols-leak-test])
AC_OUTPUT

View file

@ -67,6 +67,10 @@ to normalize them.
<dd><b>This prefix is reserved as private API, do not use.</b>. See @ref
model_specific_configuration for details.
</dd>
<dt>LIBINPUT_ATTR_*</dt>
<dd><b>This prefix is reserved as private API, do not use.</b>. See @ref
model_specific_configuration for details.
</dd>
</dl>
Below is an example udev rule to assign "seat1" to a device from vendor
@ -103,13 +107,13 @@ ENV{ID_MODEL_ID}=="034b", ENV{ID_INPUT_TOUCHPAD}="", ENV{ID_INPUT_TABLET}="1"
@section model_specific_configuration Model-specific configuration
libinput reserves the property prefix <b>LIBINPUT_MODEL_</b> for
model-specific configuration. <b>This prefix is reserved as private API, do
not use.</b>
libinput reserves the property prefixes <b>LIBINPUT_MODEL_</b> and
<b>LIBINPUT_ATTR_*</b> for model-specific configuration. <b>These prefixes
are reserved as private API, do not use.</b>
The effect of this property may be to enable or disable certain
The effect of these properties may be to enable or disable certain
features on a specific device or set of devices, to change configuration
defaults or any other reason. The effects of setting this property, the
defaults or any other reason. The effects of setting these properties, the
format of the property and the value of the property are subject to change
at any time.

View file

@ -55,10 +55,8 @@ scroll).
Due to the layout of the edges, diagonal scrolling is not possible. The
behavior of edge scrolling using both edges at the same time is undefined.
Edge scrolling conflicts with @ref clickpad_softbuttons and is
not usually available on clickpads. See
http://who-t.blogspot.com.au/2015/03/why-libinput-doesnt-support-edge.html
for details.
Edge scrolling overlaps with @ref clickpad_softbuttons. A physical click on
a clickpad ends scrolling.
@section button_scrolling On-Button scrolling

View file

@ -1179,5 +1179,23 @@
[Not supported by viewer]</text>
</switch>
</g>
<path d="M 2095.54 407 C 2098.34 403.86 2102.32 402.05 2106.52 402 L 2140.49 402 C 2144.69 402.05 2148.67 403.86 2151.47 407 L 2171.45 430 C 2172.01 431.28 2172.01 432.72 2171.45 434 L 2151.47 457 C 2148.67 460.14 2144.69 461.95 2140.49 462 L 2106.52 462 C 2102.32 461.95 2098.34 460.14 2095.54 457 L 2075.56 434 C 2075 432.72 2075 431.28 2075.56 430 L 2095.54 407 Z" fill="#ff99cc" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="2123.25" y="435.75">
thumb</text>
</g>
<path d="M 1801.42 332 L 2068.92 415.05" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 2073.93 416.61 L 2066.21 417.88 L 2068.92 415.05 L 2068.28 411.19 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 2152.5 488.7 C 2168.62 494.91 2186.38 494.91 2202.5 488.7 C 2218.62 482.5 2236.38 482.5 2252.5 488.7 L 2252.5 541.28 C 2236.38 535.08 2218.62 535.08 2202.5 541.28 C 2186.38 547.49 2168.62 547.49 2152.5 541.28 L 2152.5 488.7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="2202.25" y="518.75">
TOUCH_DEAD</text>
</g>
<path d="M 2152.05 462 L 2167.18 477.89" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 2170.8 481.69 L 2163.43 479.03 L 2167.18 477.89 L 2168.5 474.21 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 2217.81 547.5 L 2324.65 774.24" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 2326.89 778.99 L 2320.74 774.15 L 2324.65 774.24 L 2327.07 771.16 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1959.61 577 L 2084.82 466.22" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 2088.75 462.74 L 2085.83 470 L 2084.82 466.22 L 2081.19 464.76 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 122 KiB

View file

@ -525,20 +525,19 @@ tp_init_softbuttons(struct tp_dispatch *tp,
absinfo_y = device->abs.absinfo_y;
xoffset = absinfo_x->minimum,
yoffset = absinfo_y->minimum;
yoffset = absinfo_y->minimum,
yres = absinfo_y->resolution;
width = device->abs.dimensions.x;
height = device->abs.dimensions.y;
/* button height: 10mm or 15% of the touchpad height,
/* button height: 10mm or 15% orf the touchpad height,
whichever is smaller */
if (!device->abs.fake_resolution && (height * 0.15/yres) > 10) {
if ((height * 0.15)/yres > 10) {
tp->buttons.bottom_area.top_edge =
absinfo_y->maximum - 10 * yres;
absinfo_y->maximum - 10 * yres;
} else {
tp->buttons.bottom_area.top_edge = height * .85 + yoffset;
}
tp->buttons.bottom_area.rightbutton_left_edge = width/2 + xoffset;
}
@ -547,7 +546,7 @@ tp_init_top_softbuttons(struct tp_dispatch *tp,
struct evdev_device *device,
double topbutton_size_mult)
{
int width, height;
int width;
const struct input_absinfo *absinfo_x, *absinfo_y;
int xoffset, yoffset;
int yres;
@ -559,7 +558,6 @@ tp_init_top_softbuttons(struct tp_dispatch *tp,
yoffset = absinfo_y->minimum;
yres = absinfo_y->resolution;
width = device->abs.dimensions.x;
height = device->abs.dimensions.y;
if (tp->buttons.has_topbuttons) {
/* T440s has the top button line 5mm from the top, event
@ -567,14 +565,8 @@ tp_init_top_softbuttons(struct tp_dispatch *tp,
top - which maps to 15%. We allow the caller to enlarge the
area using a multiplier for the touchpad disabled case. */
double topsize_mm = 10 * topbutton_size_mult;
double topsize_pct = .15 * topbutton_size_mult;
if (!device->abs.fake_resolution) {
tp->buttons.top_area.bottom_edge =
yoffset + topsize_mm * yres;
} else {
tp->buttons.top_area.bottom_edge = height * topsize_pct + yoffset;
}
tp->buttons.top_area.bottom_edge = yoffset + topsize_mm * yres;
tp->buttons.top_area.rightbutton_left_edge = width * .58 + xoffset;
tp->buttons.top_area.leftbutton_right_edge = width * .42 + xoffset;
} else {
@ -713,8 +705,6 @@ tp_init_buttons(struct tp_dispatch *tp,
{
struct libinput *libinput = tp_libinput_context(tp);
struct tp_touch *t;
int width, height;
double diagonal;
const struct input_absinfo *absinfo_x, *absinfo_y;
tp->buttons.is_clickpad = libevdev_has_property(device->evdev,
@ -738,19 +728,9 @@ tp_init_buttons(struct tp_dispatch *tp,
absinfo_x = device->abs.absinfo_x;
absinfo_y = device->abs.absinfo_y;
/* pinned-finger motion threshold, see tp_unpin_finger.
The MAGIC for resolution-less touchpads ends up as 2% of the diagonal */
if (device->abs.fake_resolution) {
const double BUTTON_MOTION_MAGIC = 0.007;
width = device->abs.dimensions.x;
height = device->abs.dimensions.y;
diagonal = sqrt(width*width + height*height);
tp->buttons.motion_dist.x_scale_coeff = diagonal * BUTTON_MOTION_MAGIC;
tp->buttons.motion_dist.y_scale_coeff = diagonal * BUTTON_MOTION_MAGIC;
} else {
tp->buttons.motion_dist.x_scale_coeff = 1.0/absinfo_x->resolution;
tp->buttons.motion_dist.y_scale_coeff = 1.0/absinfo_y->resolution;
}
/* pinned-finger motion threshold, see tp_unpin_finger. */
tp->buttons.motion_dist.x_scale_coeff = 1.0/absinfo_x->resolution;
tp->buttons.motion_dist.y_scale_coeff = 1.0/absinfo_y->resolution;
tp->buttons.config_method.get_methods = tp_button_config_click_get_methods;
tp->buttons.config_method.set_method = tp_button_config_click_set_method;
@ -825,33 +805,53 @@ tp_check_clickfinger_distance(struct tp_dispatch *tp,
struct tp_touch *t2)
{
double x, y;
int within_distance = 0;
int xres, yres;
int bottom_threshold;
if (!t1 || !t2)
return 0;
if (t1->is_thumb || t2->is_thumb)
return 0;
x = abs(t1->point.x - t2->point.x);
y = abs(t1->point.y - t2->point.y);
/* no resolution, so let's assume they're close enough together if
they're within 30% of the touchpad width or height */
if (tp->device->abs.fake_resolution) {
int w, h;
w = tp->device->abs.dimensions.x;
h = tp->device->abs.dimensions.y;
xres = tp->device->abs.absinfo_x->resolution;
yres = tp->device->abs.absinfo_y->resolution;
x /= xres;
y /= yres;
return (x < w * 0.3 && y < h * 0.3) ? 1 : 0;
} else {
/* maximum spread is 40mm horiz, 20mm vert. Anything wider than that
* is probably a gesture. The y spread is small so we ignore clicks
* with thumbs at the bottom of the touchpad while the pointer
* moving finger is still on the pad */
/* maximum horiz spread is 40mm horiz, 30mm vert, anything wider
* than that is probably a gesture. */
if (x > 40 || y > 30)
goto out;
x /= tp->device->abs.absinfo_x->resolution;
y /= tp->device->abs.absinfo_y->resolution;
within_distance = 1;
return (x < 40 && y < 20) ? 1 : 0;
}
/* if y spread is <= 20mm, they're definitely together. */
if (y <= 20)
goto out;
/* if they're vertically spread between 20-40mm, they're not
* together if:
* - the touchpad's vertical size is >50mm, anything smaller is
* unlikely to have a thumb resting on it
* - and one of the touches is in the bottom 20mm of the touchpad
* and the other one isn't
*/
if (tp->device->abs.dimensions.y/yres < 50)
goto out;
bottom_threshold = tp->device->abs.absinfo_y->maximum - 20 * yres;
if ((t1->point.y > bottom_threshold) !=
(t2->point.y > bottom_threshold))
within_distance = 0;
out:
return within_distance;
}
static uint32_t
@ -941,6 +941,10 @@ tp_notify_clickpadbutton(struct tp_dispatch *tp,
if (tp->device->suspended)
return 0;
/* A button click always terminates edge scrolling, even if we
* don't end up sending a button event. */
tp_edge_scroll_stop_events(tp, time);
/*
* If the user has requested clickfinger replace the button chosen
* by the softbutton code with one based on the number of fingers.

View file

@ -34,7 +34,6 @@
#define CASE_RETURN_STRING(a) case a: return #a
#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
@ -92,6 +91,23 @@ tp_touch_get_edge(struct tp_dispatch *tp, struct tp_touch *t)
return edge;
}
static inline void
tp_edge_scroll_set_timer(struct tp_dispatch *tp,
struct tp_touch *t)
{
const int DEFAULT_SCROLL_LOCK_TIMEOUT = 300; /* ms */
/* if we use software buttons, we disable timeout-based
* edge scrolling. A finger resting on the button areas is
* likely there to trigger a button event.
*/
if (tp->buttons.click_method ==
LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS)
return;
libinput_timer_set(&t->scroll.timer,
t->millis + DEFAULT_SCROLL_LOCK_TIMEOUT);
}
static void
tp_edge_scroll_set_state(struct tp_dispatch *tp,
struct tp_touch *t,
@ -108,8 +124,7 @@ tp_edge_scroll_set_state(struct tp_dispatch *tp,
case EDGE_SCROLL_TOUCH_STATE_EDGE_NEW:
t->scroll.edge = tp_touch_get_edge(tp, t);
t->scroll.initial = t->point;
libinput_timer_set(&t->scroll.timer,
t->millis + DEFAULT_SCROLL_LOCK_TIMEOUT);
tp_edge_scroll_set_timer(tp, t);
break;
case EDGE_SCROLL_TOUCH_STATE_EDGE:
break;
@ -290,8 +305,7 @@ tp_edge_scroll_init(struct tp_dispatch *tp, struct evdev_device *device)
default:
/* For elantech and synaptics, note for lenovo #40 series,
* e.g. the T440s min/max are the absolute edges, not the
* recommended ones as usual with synaptics. But these are
* clickpads, so N/A.
* recommended ones as usual with synaptics.
*/
edge_width = width * .04;
edge_height = height * .054;

View file

@ -32,6 +32,20 @@
#define DEFAULT_GESTURE_SWITCH_TIMEOUT 100 /* ms */
#define DEFAULT_GESTURE_2FG_SCROLL_TIMEOUT 1000 /* ms */
#define CASE_RETURN_STRING(a) case a: return #a
static inline const char*
gesture_state_to_str(enum tp_gesture_2fg_state state)
{
switch (state) {
CASE_RETURN_STRING(GESTURE_2FG_STATE_NONE);
CASE_RETURN_STRING(GESTURE_2FG_STATE_UNKNOWN);
CASE_RETURN_STRING(GESTURE_2FG_STATE_SCROLL);
CASE_RETURN_STRING(GESTURE_2FG_STATE_PINCH);
}
return NULL;
}
static struct normalized_coords
tp_get_touches_delta(struct tp_dispatch *tp, bool average)
{
@ -380,6 +394,8 @@ tp_gesture_twofinger_handle_state_pinch(struct tp_dispatch *tp, uint64_t time)
static void
tp_gesture_post_twofinger(struct tp_dispatch *tp, uint64_t time)
{
enum tp_gesture_2fg_state oldstate = tp->gesture.twofinger_state;
if (tp->gesture.twofinger_state == GESTURE_2FG_STATE_NONE)
tp->gesture.twofinger_state =
tp_gesture_twofinger_handle_state_none(tp, time);
@ -395,6 +411,11 @@ tp_gesture_post_twofinger(struct tp_dispatch *tp, uint64_t time)
if (tp->gesture.twofinger_state == GESTURE_2FG_STATE_PINCH)
tp->gesture.twofinger_state =
tp_gesture_twofinger_handle_state_pinch(tp, time);
log_debug(tp_libinput_context(tp),
"gesture state: %s → %s\n",
gesture_state_to_str(oldstate),
gesture_state_to_str(tp->gesture.twofinger_state));
}
static void
@ -525,11 +546,34 @@ tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time)
{
unsigned int active_touches = 0;
struct tp_touch *t;
uint32_t old_thumb_mask, thumb_mask = 0;
int i = 0;
tp_for_each_touch(tp, t)
tp_for_each_touch(tp, t) {
if (tp_touch_active(tp, t))
active_touches++;
if (t->is_thumb)
thumb_mask |= 1 << i;
i++;
}
old_thumb_mask = tp->gesture.thumb_mask;
tp->gesture.thumb_mask = thumb_mask;
/* active touches does not include thumb touches, need to count those
* separately, in a bitmask.
* then, if the finger count changes and/or the thumb count changes
* -> cancel gesture.
*/
if (thumb_mask != old_thumb_mask) {
/* if a thumb is detected during a gesture, that gesture is
* cancelled and the user effectively needs to restart. we
* could be smarter, but the complexity isn't worth it */
tp_gesture_cancel(tp, time);
return;
}
if (active_touches != tp->gesture.finger_count) {
/* If all fingers are lifted immediately end the gesture */
if (active_touches == 0) {

View file

@ -47,6 +47,7 @@ enum tap_event {
TAP_EVENT_RELEASE,
TAP_EVENT_BUTTON,
TAP_EVENT_TIMEOUT,
TAP_EVENT_THUMB,
};
/*****************************************
@ -93,6 +94,7 @@ tap_event_to_str(enum tap_event event)
CASE_RETURN_STRING(TAP_EVENT_RELEASE);
CASE_RETURN_STRING(TAP_EVENT_TIMEOUT);
CASE_RETURN_STRING(TAP_EVENT_BUTTON);
CASE_RETURN_STRING(TAP_EVENT_THUMB);
}
return NULL;
}
@ -166,6 +168,10 @@ tp_tap_idle_handle_event(struct tp_dispatch *tp,
case TAP_EVENT_BUTTON:
tp->tap.state = TAP_STATE_DEAD;
break;
case TAP_EVENT_THUMB:
log_bug_libinput(libinput,
"invalid tap event, no fingers down, no thumb\n");
break;
}
}
@ -193,6 +199,12 @@ tp_tap_touch_handle_event(struct tp_dispatch *tp,
case TAP_EVENT_BUTTON:
tp->tap.state = TAP_STATE_DEAD;
break;
case TAP_EVENT_THUMB:
tp->tap.state = TAP_STATE_IDLE;
t->tap.is_thumb = true;
t->tap.state = TAP_TOUCH_STATE_DEAD;
tp_tap_clear_timer(tp);
break;
}
}
@ -216,6 +228,11 @@ tp_tap_hold_handle_event(struct tp_dispatch *tp,
case TAP_EVENT_BUTTON:
tp->tap.state = TAP_STATE_DEAD;
break;
case TAP_EVENT_THUMB:
tp->tap.state = TAP_STATE_IDLE;
t->tap.is_thumb = true;
t->tap.state = TAP_TOUCH_STATE_DEAD;
break;
}
}
@ -244,6 +261,8 @@ tp_tap_tapped_handle_event(struct tp_dispatch *tp,
tp->tap.state = TAP_STATE_DEAD;
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
break;
case TAP_EVENT_THUMB:
break;
}
}
@ -275,6 +294,8 @@ tp_tap_touch2_handle_event(struct tp_dispatch *tp,
case TAP_EVENT_BUTTON:
tp->tap.state = TAP_STATE_DEAD;
break;
case TAP_EVENT_THUMB:
break;
}
}
@ -299,6 +320,8 @@ tp_tap_touch2_hold_handle_event(struct tp_dispatch *tp,
case TAP_EVENT_BUTTON:
tp->tap.state = TAP_STATE_DEAD;
break;
case TAP_EVENT_THUMB:
break;
}
}
@ -328,6 +351,8 @@ tp_tap_touch3_handle_event(struct tp_dispatch *tp,
case TAP_EVENT_BUTTON:
tp->tap.state = TAP_STATE_DEAD;
break;
case TAP_EVENT_THUMB:
break;
}
}
@ -351,6 +376,8 @@ tp_tap_touch3_hold_handle_event(struct tp_dispatch *tp,
case TAP_EVENT_BUTTON:
tp->tap.state = TAP_STATE_DEAD;
break;
case TAP_EVENT_THUMB:
break;
}
}
@ -375,6 +402,8 @@ tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp,
tp->tap.state = TAP_STATE_DEAD;
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
break;
case TAP_EVENT_THUMB:
break;
}
}
@ -408,6 +437,8 @@ tp_tap_dragging_handle_event(struct tp_dispatch *tp,
tp->tap.state = TAP_STATE_DEAD;
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
break;
case TAP_EVENT_THUMB:
break;
}
}
@ -433,6 +464,8 @@ tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp,
tp->tap.state = TAP_STATE_DEAD;
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
break;
case TAP_EVENT_THUMB:
break;
}
}
@ -459,6 +492,8 @@ tp_tap_dragging_tap_handle_event(struct tp_dispatch *tp,
tp->tap.state = TAP_STATE_DEAD;
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
break;
case TAP_EVENT_THUMB:
break;
}
}
@ -484,6 +519,8 @@ tp_tap_dragging2_handle_event(struct tp_dispatch *tp,
tp->tap.state = TAP_STATE_DEAD;
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
break;
case TAP_EVENT_THUMB:
break;
}
}
@ -518,6 +555,8 @@ tp_tap_multitap_handle_event(struct tp_dispatch *tp,
tp->tap.state = TAP_STATE_IDLE;
tp_tap_clear_timer(tp);
break;
case TAP_EVENT_THUMB:
break;
}
}
@ -556,6 +595,8 @@ tp_tap_multitap_down_handle_event(struct tp_dispatch *tp,
tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
tp_tap_clear_timer(tp);
break;
case TAP_EVENT_THUMB:
break;
}
}
@ -576,6 +617,8 @@ tp_tap_dead_handle_event(struct tp_dispatch *tp,
case TAP_EVENT_TIMEOUT:
case TAP_EVENT_BUTTON:
break;
case TAP_EVENT_THUMB:
break;
}
}
@ -691,7 +734,20 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS)
t->tap.state = TAP_TOUCH_STATE_DEAD;
/* If a touch was considered thumb for tapping once, we
* ignore it for the rest of lifetime */
if (t->tap.is_thumb)
continue;
if (t->state == TOUCH_BEGIN) {
/* The simple version: if a touch is a thumb on
* begin we ignore it. All other thumb touches
* follow the normal tap state for now */
if (t->is_thumb) {
t->tap.is_thumb = true;
continue;
}
t->tap.state = TAP_TOUCH_STATE_TOUCH;
t->tap.initial = t->point;
tp_tap_handle_event(tp, t, TAP_EVENT_TOUCH, time);
@ -718,6 +774,10 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
}
tp_tap_handle_event(tp, t, TAP_EVENT_MOTION, time);
} else if (tp->tap.state != TAP_STATE_IDLE &&
t->is_thumb &&
!t->tap.is_thumb) {
tp_tap_handle_event(tp, t, TAP_EVENT_THUMB, time);
}
}

View file

@ -212,6 +212,8 @@ tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
t->millis = time;
tp->nfingers_down++;
t->palm.time = time;
t->is_thumb = false;
t->tap.is_thumb = false;
assert(tp->nfingers_down >= 1);
}
@ -314,6 +316,9 @@ tp_process_absolute(struct tp_dispatch *tp,
else
tp_end_sequence(tp, t, time);
break;
case ABS_MT_PRESSURE:
t->pressure = e->value;
break;
}
}
@ -461,6 +466,7 @@ tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
t->palm.state == PALM_NONE &&
!t->pinned.is_pinned &&
!t->is_thumb &&
tp_button_touch_active(tp, t) &&
tp_edge_scroll_touch_active(tp, t);
}
@ -496,8 +502,7 @@ tp_palm_detect_dwt(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
return 1;
} else if (!tp->dwt.keyboard_active &&
t->state == TOUCH_UPDATE &&
t->palm.state == PALM_TYPING)
{
t->palm.state == PALM_TYPING) {
/* If a touch has started before the first or after the last
key press, release it on timeout. Benefit: a palm rested
while typing on the touchpad will be ignored, but a touch
@ -603,6 +608,33 @@ out:
t->palm.state == PALM_TYPING ? "typing" : "trackpoint");
}
static void
tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t)
{
/* once a thumb, always a thumb */
if (!tp->thumb.detect_thumbs || t->is_thumb)
return;
/* Note: a thumb at the edge of the touchpad won't trigger the
* threshold, the surface areas is usually too small.
*/
if (t->pressure < tp->thumb.threshold)
return;
t->is_thumb = true;
/* now what? we marked it as thumb, so:
*
* - pointer motion must ignore this touch
* - clickfinger must ignore this touch for finger count
* - software buttons are unaffected
* - edge scrolling unaffected
* - gestures: cancel
* - tapping: honour thumb on begin, ignore it otherwise for now,
* this gets a tad complicated otherwise
*/
}
static void
tp_unhover_abs_distance(struct tp_dispatch *tp, uint64_t time)
{
@ -721,6 +753,7 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time)
if (!t->dirty)
continue;
tp_thumb_detect(tp, t);
tp_palm_detect(tp, t, time);
tp_motion_hysteresis(tp, t);
@ -1319,17 +1352,8 @@ tp_init_accel(struct tp_dispatch *tp, double diagonal)
* and y resolution, so that a circle on the
* touchpad does not turn into an elipse on the screen.
*/
if (!tp->device->abs.fake_resolution) {
tp->accel.x_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_x;
tp->accel.y_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_y;
} else {
/*
* For touchpads where the driver does not provide resolution, fall
* back to scaling motion events based on the diagonal size in units.
*/
tp->accel.x_scale_coeff = DEFAULT_ACCEL_NUMERATOR / diagonal;
tp->accel.y_scale_coeff = DEFAULT_ACCEL_NUMERATOR / diagonal;
}
tp->accel.x_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_x;
tp->accel.y_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_y;
switch (tp->device->model) {
case EVDEV_MODEL_LENOVO_X230:
@ -1351,14 +1375,11 @@ tp_scroll_config_scroll_method_get_methods(struct libinput_device *device)
{
struct evdev_device *evdev = (struct evdev_device*)device;
struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
uint32_t methods = LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
uint32_t methods = LIBINPUT_CONFIG_SCROLL_EDGE;
if (tp->ntouches >= 2)
methods |= LIBINPUT_CONFIG_SCROLL_2FG;
if (!tp->buttons.is_clickpad)
methods |= LIBINPUT_CONFIG_SCROLL_EDGE;
return methods;
}
@ -1481,6 +1502,30 @@ tp_init_sendevents(struct tp_dispatch *tp,
return 0;
}
static int
tp_init_thumb(struct tp_dispatch *tp)
{
struct evdev_device *device = tp->device;
const struct input_absinfo *abs;
abs = libevdev_get_abs_info(device->evdev, ABS_MT_PRESSURE);
if (!abs)
return 0;
if (abs->maximum - abs->minimum < 255)
return 0;
/* The touchpads we looked at so far have a clear thumb threshold of
* ~100, you don't reach that with a normal finger interaction.
* Note: "thumb" means massive touch that should not interact, not
* "using the tip of my thumb for a pinch gestures".
*/
tp->thumb.threshold = 100;
tp->thumb.detect_thumbs = true;
return 0;
}
static int
tp_sanity_check(struct tp_dispatch *tp,
struct evdev_device *device)
@ -1505,12 +1550,50 @@ error:
return -1;
}
static int
tp_init_default_resolution(struct tp_dispatch *tp,
struct evdev_device *device)
{
const int touchpad_width_mm = 69, /* 1 under palm detection */
touchpad_height_mm = 50;
int xres, yres;
if (!device->abs.fake_resolution)
return 0 ;
/* we only get here if
* - the touchpad provides no resolution
* - the udev hwdb didn't override the resoluion
* - no ATTR_SIZE_HINT is set
*
* The majority of touchpads that triggers all these conditions
* are old ones, so let's assume a small touchpad size and assume
* that.
*/
log_info(tp_libinput_context(tp),
"%s: no resolution or size hints, assuming a size of %dx%dmm\n",
device->devname,
touchpad_width_mm,
touchpad_height_mm);
xres = device->abs.dimensions.x/touchpad_width_mm;
yres = device->abs.dimensions.y/touchpad_height_mm;
libevdev_set_abs_resolution(device->evdev, ABS_X, xres);
libevdev_set_abs_resolution(device->evdev, ABS_Y, yres);
libevdev_set_abs_resolution(device->evdev, ABS_MT_POSITION_X, xres);
libevdev_set_abs_resolution(device->evdev, ABS_MT_POSITION_Y, yres);
device->abs.fake_resolution = 0;
return 0;
}
static int
tp_init(struct tp_dispatch *tp,
struct evdev_device *device)
{
int width, height;
double diagonal;
int res_x, res_y;
tp->base.interface = &tp_interface;
tp->device = device;
@ -1518,9 +1601,14 @@ tp_init(struct tp_dispatch *tp,
if (tp_sanity_check(tp, device) != 0)
return -1;
if (tp_init_default_resolution(tp, device) != 0)
return -1;
if (tp_init_slots(tp, device) != 0)
return -1;
res_x = tp->device->abs.absinfo_x->resolution;
res_y = tp->device->abs.absinfo_y->resolution;
width = device->abs.dimensions.x;
height = device->abs.dimensions.y;
diagonal = sqrt(width*width + height*height);
@ -1529,18 +1617,8 @@ tp_init(struct tp_dispatch *tp,
EV_ABS,
ABS_MT_DISTANCE);
if (device->abs.fake_resolution) {
tp->hysteresis_margin.x =
diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
tp->hysteresis_margin.y =
diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
} else {
int res_x = tp->device->abs.absinfo_x->resolution,
res_y = tp->device->abs.absinfo_y->resolution;
tp->hysteresis_margin.x = res_x/2;
tp->hysteresis_margin.y = res_y/2;
}
tp->hysteresis_margin.x = res_x/2;
tp->hysteresis_margin.y = res_y/2;
if (tp_init_accel(tp, diagonal) != 0)
return -1;
@ -1563,6 +1641,9 @@ tp_init(struct tp_dispatch *tp,
if (tp_init_gesture(tp) != 0)
return -1;
if (tp_init_thumb(tp) != 0)
return -1;
device->seat_caps |= EVDEV_DEVICE_POINTER;
return 0;

View file

@ -141,9 +141,11 @@ struct tp_touch {
enum touch_state state;
bool has_ended; /* TRACKING_ID == -1 */
bool dirty;
bool is_thumb;
struct device_coords point;
uint64_t millis;
int distance; /* distance == 0 means touch */
int pressure;
struct {
struct device_coords samples[TOUCHPAD_HISTORY_LENGTH];
@ -173,6 +175,7 @@ struct tp_touch {
struct {
enum tp_tap_touch_state state;
struct device_coords initial;
bool is_thumb;
} tap;
struct {
@ -234,6 +237,7 @@ struct tp_dispatch {
double prev_scale;
double angle;
struct device_float_coords center;
uint32_t thumb_mask;
} gesture;
struct {
@ -317,6 +321,11 @@ struct tp_dispatch {
uint64_t keyboard_last_press_time;
} dwt;
struct {
bool detect_thumbs;
int threshold;
} thumb;
};
#define tp_for_each_touch(_tp, _t) \

View file

@ -1552,19 +1552,35 @@ evdev_read_model(struct evdev_device *device)
return m->model;
}
/* Return 1 if the given resolutions have been set, or 0 otherwise */
static inline int
evdev_read_attr_size_prop(struct evdev_device *device,
size_t *size_x,
size_t *size_y)
{
struct udev_device *udev;
const char *size_prop;
udev = device->udev_device;
size_prop = udev_device_get_property_value(udev,
"LIBINPUT_ATTR_SIZE_HINT");
if (!size_prop)
return false;
return parse_dimension_property(size_prop, size_x, size_y);
}
/* Return 1 if the device is set to the fake resolution or 0 otherwise */
static inline int
evdev_fix_abs_resolution(struct evdev_device *device,
unsigned int xcode,
unsigned int ycode,
int xresolution,
int yresolution)
unsigned int ycode)
{
struct libinput *libinput = device->base.seat->libinput;
struct libevdev *evdev = device->evdev;
const struct input_absinfo *absx, *absy;
struct input_absinfo fixed;
int rc = 0;
size_t widthmm = 0, heightmm = 0;
int xres = EVDEV_FAKE_RESOLUTION,
yres = EVDEV_FAKE_RESOLUTION;
if (!(xcode == ABS_X && ycode == ABS_Y) &&
!(xcode == ABS_MT_POSITION_X && ycode == ABS_MT_POSITION_Y)) {
@ -1574,37 +1590,28 @@ evdev_fix_abs_resolution(struct evdev_device *device,
return 0;
}
if (xresolution == 0 || yresolution == 0 ||
(xresolution == EVDEV_FAKE_RESOLUTION && xresolution != yresolution) ||
(yresolution == EVDEV_FAKE_RESOLUTION && xresolution != yresolution)) {
log_bug_libinput(libinput,
"Invalid x/y resolutions %d/%d\n",
xresolution, yresolution);
return 0;
}
absx = libevdev_get_abs_info(evdev, xcode);
absy = libevdev_get_abs_info(evdev, ycode);
if (absx->resolution == 0 || absx->resolution == EVDEV_FAKE_RESOLUTION) {
fixed = *absx;
fixed.resolution = xresolution;
/* libevdev_set_abs_info() changes the absinfo we already
have a pointer to, no need to fetch it again */
libevdev_set_abs_info(evdev, xcode, &fixed);
rc = 1;
if (absx->resolution != 0 || absy->resolution != 0)
return 0;
/* Note: we *do not* override resolutions if provided by the kernel.
* If a device needs this, add it to 60-evdev.hwdb. The libinput
* property is only for general size hints where we can make
* educated guesses but don't know better.
*/
if (evdev_read_attr_size_prop(device, &widthmm, &heightmm)) {
xres = (absx->maximum - absx->minimum)/widthmm;
yres = (absy->maximum - absy->minimum)/heightmm;
}
if (absy->resolution == 0 || absy->resolution == EVDEV_FAKE_RESOLUTION) {
fixed = *absy;
fixed.resolution = yresolution;
/* libevdev_set_abs_info() changes the absinfo we already
have a pointer to, no need to fetch it again */
libevdev_set_abs_info(evdev, ycode, &fixed);
rc = 1;
}
/* libevdev_set_abs_resolution() changes the absinfo we already
have a pointer to, no need to fetch it again */
libevdev_set_abs_resolution(evdev, xcode, xres);
libevdev_set_abs_resolution(evdev, ycode, yres);
return rc;
return xres == EVDEV_FAKE_RESOLUTION;
}
static enum evdev_device_udev_tags
@ -1775,9 +1782,7 @@ evdev_configure_mt_device(struct evdev_device *device)
if (evdev_fix_abs_resolution(device,
ABS_MT_POSITION_X,
ABS_MT_POSITION_Y,
EVDEV_FAKE_RESOLUTION,
EVDEV_FAKE_RESOLUTION))
ABS_MT_POSITION_Y))
device->abs.fake_resolution = 1;
device->abs.absinfo_x = libevdev_get_abs_info(evdev, ABS_MT_POSITION_X);
@ -1902,11 +1907,7 @@ evdev_configure_device(struct evdev_device *device)
evdev_fix_android_mt(device);
if (libevdev_has_event_code(evdev, EV_ABS, ABS_X)) {
if (evdev_fix_abs_resolution(device,
ABS_X,
ABS_Y,
EVDEV_FAKE_RESOLUTION,
EVDEV_FAKE_RESOLUTION))
if (evdev_fix_abs_resolution(device, ABS_X, ABS_Y))
device->abs.fake_resolution = 1;
device->abs.absinfo_x = libevdev_get_abs_info(evdev, ABS_X);
device->abs.absinfo_y = libevdev_get_abs_info(evdev, ABS_Y);

View file

@ -234,3 +234,33 @@ parse_trackpoint_accel_property(const char *prop)
return accel;
}
/**
* Parses a simple dimension string in the form of "10x40". The two
* numbers must be positive integers in decimal notation.
* On success, the two numbers are stored in w and h. On failure, w and h
* are unmodified.
*
* @param prop The value of the property
* @param w Returns the first component of the dimension
* @param h Returns the second component of the dimension
* @return true on success, false otherwise
*/
bool
parse_dimension_property(const char *prop, size_t *w, size_t *h)
{
int x, y;
if (!prop)
return false;
if (sscanf(prop, "%dx%d", &x, &y) != 2)
return false;
if (x < 0 || y < 0)
return false;
*w = (size_t)x;
*h = (size_t)y;
return true;
}

View file

@ -28,6 +28,7 @@
#include <unistd.h>
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
@ -321,6 +322,7 @@ enum ratelimit_state ratelimit_test(struct ratelimit *r);
int parse_mouse_dpi_property(const char *prop);
int parse_mouse_wheel_click_angle_property(const char *prop);
double parse_trackpoint_accel_property(const char *prop);
bool parse_dimension_property(const char *prop, size_t *width, size_t *height);
static inline double
vector_length(double x, double y)

View file

@ -13,42 +13,44 @@ noinst_LTLIBRARIES = liblitest.la
liblitest_la_SOURCES = \
litest.h \
litest-int.h \
litest-alps-semi-mt.c \
litest-alps-dualpoint.c \
litest-atmel-hover.c \
litest-bcm5974.c \
litest-generic-singletouch.c \
litest-keyboard.c \
litest-keyboard-razer-blackwidow.c \
litest-logitech-trackball.c \
litest-nexus4-touch-screen.c \
litest-mouse.c \
litest-mouse-roccat.c \
litest-mouse-low-dpi.c \
litest-ms-surface-cover.c \
litest-protocol-a-touch-screen.c \
litest-qemu-usb-tablet.c \
litest-synaptics.c \
litest-synaptics-hover.c \
litest-synaptics-st.c \
litest-synaptics-t440.c \
litest-synaptics-x1-carbon-3rd.c \
litest-trackpoint.c \
litest-touch-screen.c \
litest-wacom-bamboo-tablet.c \
litest-wacom-cintiq-tablet.c \
litest-wacom-intuos-tablet.c \
litest-wacom-isdv4-tablet.c \
litest-wacom-touch.c \
litest-wacom-intuos-finger.c \
litest-waltop-tablet.c \
litest-wheel-only.c \
litest-xen-virtual-pointer.c \
litest-vmware-virtual-usb-mouse.c \
litest-device-alps-semi-mt.c \
litest-device-alps-dualpoint.c \
litest-device-atmel-hover.c \
litest-device-bcm5974.c \
litest-device-elantech-touchpad.c \
litest-device-generic-singletouch.c \
litest-device-keyboard.c \
litest-device-keyboard-razer-blackwidow.c \
litest-device-logitech-trackball.c \
litest-device-nexus4-touch-screen.c \
litest-device-magic-trackpad.c \
litest-device-mouse.c \
litest-device-mouse-roccat.c \
litest-device-mouse-low-dpi.c \
litest-device-ms-surface-cover.c \
litest-device-protocol-a-touch-screen.c \
litest-device-qemu-usb-tablet.c \
litest-device-synaptics.c \
litest-device-synaptics-hover.c \
litest-device-synaptics-st.c \
litest-device-synaptics-t440.c \
litest-device-synaptics-x1-carbon-3rd.c \
litest-device-trackpoint.c \
litest-device-touch-screen.c \
litest-device-wacom-bamboo-tablet.c \
litest-device-wacom-cintiq-tablet.c \
litest-device-wacom-intuos-tablet.c \
litest-device-wacom-isdv4-tablet.c \
litest-device-wacom-touch.c \
litest-device-wacom-intuos-finger.c \
litest-device-waltop-tablet.c \
litest-device-wheel-only.c \
litest-device-xen-virtual-pointer.c \
litest-device-vmware-virtual-usb-mouse.c \
litest.c
liblitest_la_LIBADD = $(top_builddir)/src/libinput-util.la
liblitest_la_CFLAGS = $(AM_CFLAGS) \
-DLIBINPUT_UDEV_RULES_FILE="\"$(abs_top_srcdir)/udev/90-libinput-model-quirks.rules\"" \
-DLIBINPUT_UDEV_RULES_FILE="\"$(abs_top_srcdir)/udev/90-libinput-model-quirks-litest.rules\"" \
-DLIBINPUT_UDEV_HWDB_FILE="\"$(abs_top_srcdir)/udev/90-libinput-model-quirks.hwdb\""
if HAVE_LIBUNWIND
liblitest_la_LIBADD += $(LIBUNWIND_LIBS) -ldl

View file

@ -70,7 +70,8 @@ START_TEST(device_sendevents_config_touchpad)
expected = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
/* The wacom devices in the test suite are external */
if (libevdev_get_id_vendor(dev->evdev) != VENDOR_ID_WACOM)
if (libevdev_get_id_vendor(dev->evdev) != VENDOR_ID_WACOM &&
libevdev_get_id_bustype(dev->evdev) != BUS_BLUETOOTH)
expected |=
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
@ -89,7 +90,8 @@ START_TEST(device_sendevents_config_touchpad_superset)
uint32_t modes;
/* The wacom devices in the test suite are external */
if (libevdev_get_id_vendor(dev->evdev) == 0x56a) /* wacom */
if (libevdev_get_id_vendor(dev->evdev) == VENDOR_ID_WACOM ||
libevdev_get_id_bustype(dev->evdev) == BUS_BLUETOOTH)
return;
device = dev->libinput_device;

View file

@ -37,11 +37,12 @@ static void litest_bcm5974_setup(void)
static struct input_event down[] = {
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = 30 },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
@ -50,15 +51,31 @@ static struct input_event move[] = {
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static int
get_axis_default(struct litest_device *d, unsigned int evcode, int32_t *value)
{
switch (evcode) {
case ABS_PRESSURE:
case ABS_MT_PRESSURE:
*value = 30;
return 0;
}
return 1;
}
static struct litest_device_interface interface = {
.touch_down_events = down,
.touch_move_events = move,
.get_axis_default = get_axis_default,
};
static struct input_absinfo absinfo[] = {

View file

@ -0,0 +1,121 @@
/*
* Copyright © 2015 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "litest.h"
#include "litest-int.h"
static void
litest_elantech_touchpad_setup(void)
{
struct litest_device *d = litest_create_device(LITEST_ELANTECH_TOUCHPAD);
litest_set_current_device(d);
}
static struct input_event down[] = {
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static struct input_event move[] = {
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static int
get_axis_default(struct litest_device *d, unsigned int evcode, int32_t *value)
{
switch (evcode) {
case ABS_PRESSURE:
case ABS_MT_PRESSURE:
*value = 30;
return 0;
}
return 1;
}
static struct litest_device_interface interface = {
.touch_down_events = down,
.touch_move_events = move,
.get_axis_default = get_axis_default,
};
static struct input_id input_id = {
.bustype = 0x11,
.vendor = 0x2,
.product = 0xe,
};
static int events[] = {
EV_KEY, BTN_LEFT,
EV_KEY, BTN_RIGHT,
EV_KEY, BTN_TOOL_FINGER,
EV_KEY, BTN_TOUCH,
EV_KEY, BTN_TOOL_DOUBLETAP,
EV_KEY, BTN_TOOL_TRIPLETAP,
EV_KEY, BTN_TOOL_QUADTAP,
INPUT_PROP_MAX, INPUT_PROP_POINTER,
-1, -1,
};
static struct input_absinfo absinfo[] = {
{ ABS_X, 0, 1280, 0, 0, 0 },
{ ABS_Y, 0, 704, 0, 0, 0 },
{ ABS_PRESSURE, 0, 255, 0, 0, 0 },
{ ABS_TOOL_WIDTH, 0, 15, 0, 0, 0 },
{ ABS_MT_SLOT, 0, 1, 0, 0, 0 },
{ ABS_MT_POSITION_X, 0, 1280, 0, 0, 0 },
{ ABS_MT_POSITION_Y, 0, 704, 0, 0, 0 },
{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
{ .value = -1 }
};
struct litest_test_device litest_elantech_touchpad_device = {
.type = LITEST_ELANTECH_TOUCHPAD,
.features = LITEST_TOUCHPAD | LITEST_BUTTON,
.shortname = "elantech",
.setup = litest_elantech_touchpad_setup,
.interface = &interface,
.name = "ETPS/2 Elantech Touchpad",
.id = &input_id,
.events = events,
.absinfo = absinfo,
};

View file

@ -0,0 +1,110 @@
/*
* Copyright © 2015 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "litest.h"
#include "litest-int.h"
static void litest_magicpad_setup(void)
{
struct litest_device *d = litest_create_device(LITEST_MAGIC_TRACKPAD);
litest_set_current_device(d);
}
static struct input_event down[] = {
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TOUCH_MAJOR, .value = 272 },
{ .type = EV_ABS, .code = ABS_MT_TOUCH_MINOR, .value = 400 },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static struct input_event move[] = {
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TOUCH_MAJOR, .value = 272 },
{ .type = EV_ABS, .code = ABS_MT_TOUCH_MINOR, .value = 400 },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static struct litest_device_interface interface = {
.touch_down_events = down,
.touch_move_events = move,
};
static struct input_absinfo absinfo[] = {
{ ABS_X, -2909, 3167, 4, 0, 46 },
{ ABS_Y, -2456, 2565, 4, 0, 45 },
{ ABS_MT_SLOT, 0, 15, 0, 0, 0 },
{ ABS_MT_POSITION_X, -2909, 3167, 4, 0, 46 },
{ ABS_MT_POSITION_Y, -2456, 2565, 4, 0, 45 },
{ ABS_MT_ORIENTATION, -31, 32, 1, 0, 0 },
{ ABS_MT_TOUCH_MAJOR, 0, 1020, 4, 0, 0 },
{ ABS_MT_TOUCH_MINOR, 0, 1020, 4, 0, 0 },
{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
{ .value = -1 },
};
static struct input_id input_id = {
.bustype = 0x5,
.vendor = 0x5ac,
.product = 0x30e,
};
static int events[] = {
EV_KEY, BTN_LEFT,
EV_KEY, BTN_TOOL_FINGER,
EV_KEY, BTN_TOOL_QUINTTAP,
EV_KEY, BTN_TOUCH,
EV_KEY, BTN_TOOL_DOUBLETAP,
EV_KEY, BTN_TOOL_TRIPLETAP,
EV_KEY, BTN_TOOL_QUADTAP,
INPUT_PROP_MAX, INPUT_PROP_BUTTONPAD,
-1, -1
};
struct litest_test_device litest_magicpad_device = {
.type = LITEST_MAGIC_TRACKPAD,
.features = LITEST_TOUCHPAD | LITEST_CLICKPAD |
LITEST_BUTTON | LITEST_APPLE_CLICKPAD,
.shortname = "magic trackpad",
.setup = litest_magicpad_setup,
.interface = &interface,
.name = "Apple Wireless Trackpad",
.id = &input_id,
.events = events,
.absinfo = absinfo,
};

View file

@ -38,11 +38,12 @@ litest_synaptics_t440_setup(void)
static struct input_event down[] = {
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = 30 },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
@ -51,15 +52,31 @@ static struct input_event move[] = {
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static int
get_axis_default(struct litest_device *d, unsigned int evcode, int32_t *value)
{
switch (evcode) {
case ABS_PRESSURE:
case ABS_MT_PRESSURE:
*value = 30;
return 0;
}
return 1;
}
static struct litest_device_interface interface = {
.touch_down_events = down,
.touch_move_events = move,
.get_axis_default = get_axis_default,
};
static struct input_id input_id = {

View file

@ -38,11 +38,12 @@ litest_synaptics_carbon3rd_setup(void)
static struct input_event down[] = {
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = 30 },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
@ -51,15 +52,31 @@ static struct input_event move[] = {
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static int
get_axis_default(struct litest_device *d, unsigned int evcode, int32_t *value)
{
switch (evcode) {
case ABS_PRESSURE:
case ABS_MT_PRESSURE:
*value = 30;
return 0;
}
return 1;
}
static struct litest_device_interface interface = {
.touch_down_events = down,
.touch_move_events = move,
.get_axis_default = get_axis_default,
};
static struct input_id input_id = {

View file

@ -38,11 +38,12 @@ litest_synaptics_clickpad_setup(void)
static struct input_event down[] = {
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = 30 },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
@ -51,15 +52,31 @@ static struct input_event move[] = {
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static int
get_axis_default(struct litest_device *d, unsigned int evcode, int32_t *value)
{
switch (evcode) {
case ABS_PRESSURE:
case ABS_MT_PRESSURE:
*value = 30;
return 0;
}
return 1;
}
static struct litest_device_interface interface = {
.touch_down_events = down,
.touch_move_events = move,
.get_axis_default = get_axis_default,
};
static struct input_id input_id = {

View file

@ -79,6 +79,12 @@ struct litest_device_interface {
void (*touch_move)(struct litest_device *d, unsigned int slot, double x, double y);
void (*touch_up)(struct litest_device *d, unsigned int slot);
/**
* Default value for the given EV_ABS axis.
* @return 0 on success, nonzero otherwise
*/
int (*get_axis_default)(struct litest_device *d, unsigned int code, int32_t *value);
/**
* Set of of events to execute on touch down, terminated by a .type
* and .code value of -1. If the event value is LITEST_AUTO_ASSIGN,

View file

@ -360,6 +360,8 @@ extern struct litest_test_device litest_alps_dualpoint_device;
extern struct litest_test_device litest_mouse_low_dpi_device;
extern struct litest_test_device litest_generic_multitouch_screen_device;
extern struct litest_test_device litest_nexus4_device;
extern struct litest_test_device litest_magicpad_device;
extern struct litest_test_device litest_elantech_touchpad_device;
extern struct litest_test_device litest_waltop_tablet_device;
struct litest_test_device* devices[] = {
@ -394,6 +396,8 @@ struct litest_test_device* devices[] = {
&litest_mouse_low_dpi_device,
&litest_generic_multitouch_screen_device,
&litest_nexus4_device,
&litest_magicpad_device,
&litest_elantech_touchpad_device,
&litest_waltop_tablet_device,
NULL,
};
@ -1222,19 +1226,22 @@ litest_event(struct litest_device *d, unsigned int type,
litest_assert_int_eq(ret, 0);
}
static int32_t
static bool
axis_replacement_value(struct axis_replacement *axes,
int32_t evcode)
int32_t evcode,
int32_t *value)
{
struct axis_replacement *axis = axes;
while (axis->evcode != -1) {
if (axis->evcode == evcode)
return axis->value;
if (axis->evcode == evcode) {
*value = axis->value;
return true;
}
axis++;
}
return -1;
return false;
}
int
@ -1269,8 +1276,13 @@ litest_auto_assign_value(struct litest_device *d,
value = touching ? 0 : 1;
break;
default:
if (axes)
value = axis_replacement_value(axes, ev->code);
value = -1;
if (!axes)
break;
if (!axis_replacement_value(axes, ev->code, &value) &&
d->interface->get_axis_default)
d->interface->get_axis_default(d, ev->code, &value);
break;
}
@ -1466,7 +1478,7 @@ auto_assign_tablet_value(struct litest_device *d,
value = litest_scale(d, ABS_Y, y);
break;
default:
value = axis_replacement_value(axes, ev->code);
axis_replacement_value(axes, ev->code, &value);
break;
}

View file

@ -140,11 +140,13 @@ enum litest_device_type {
LITEST_MOUSE_LOW_DPI = -26,
LITEST_GENERIC_MULTITOUCH_SCREEN = -27,
LITEST_NEXUS4_TOUCH_SCREEN = -28,
LITEST_WACOM_BAMBOO = -29,
LITEST_WACOM_CINTIQ = -30,
LITEST_WACOM_INTUOS = -31,
LITEST_WACOM_ISDV4 = -32,
LITEST_WALTOP = -33,
LITEST_MAGIC_TRACKPAD = -29,
LITEST_ELANTECH_TOUCHPAD = -30,
LITEST_WACOM_BAMBOO = -31,
LITEST_WACOM_CINTIQ = -32,
LITEST_WACOM_INTUOS = -33,
LITEST_WACOM_ISDV4 = -34,
LITEST_WALTOP = -35,
};
enum litest_device_feature {

View file

@ -673,6 +673,52 @@ START_TEST(trackpoint_accel_parser)
}
END_TEST
struct parser_test_dimension {
char *tag;
bool success;
int x, y;
};
START_TEST(dimension_prop_parser)
{
struct parser_test_dimension tests[] = {
{ "10x10", true, 10, 10 },
{ "1x20", true, 1, 20 },
{ "1x8000", true, 1, 8000 },
{ "238492x428210", true, 238492, 428210 },
{ "0x0", true, 0, 0 },
{ "-10x10", false, 0, 0 },
{ "-1", false, 0, 0 },
{ "1x-99", false, 0, 0 },
{ "0", false, 0, 0 },
{ "100", false, 0, 0 },
{ "", false, 0, 0 },
{ "abd", false, 0, 0 },
{ "xabd", false, 0, 0 },
{ "0xaf", false, 0, 0 },
{ "0x0x", true, 0, 0 },
{ "x10", false, 0, 0 },
{ NULL, false, 0, 0 }
};
int i;
size_t x, y;
bool success;
for (i = 0; tests[i].tag != NULL; i++) {
x = y = 0xad;
success = parse_dimension_property(tests[i].tag, &x, &y);
ck_assert(success == tests[i].success);
if (success) {
ck_assert_int_eq(x, tests[i].x);
ck_assert_int_eq(y, tests[i].y);
} else {
ck_assert_int_eq(x, 0xad);
ck_assert_int_eq(y, 0xad);
}
}
}
END_TEST
void
litest_setup_tests(void)
{
@ -693,4 +739,5 @@ litest_setup_tests(void)
litest_add_no_device("misc:parser", dpi_parser);
litest_add_no_device("misc:parser", wheel_click_parser);
litest_add_no_device("misc:parser", trackpoint_accel_parser);
litest_add_no_device("misc:parser", dimension_prop_parser);
}

File diff suppressed because it is too large Load diff

View file

@ -413,8 +413,11 @@ START_TEST(udev_device_sysname)
libinput_dispatch(li);
while ((ev = libinput_get_event(li))) {
if (libinput_event_get_type(ev) != LIBINPUT_EVENT_DEVICE_ADDED)
if (libinput_event_get_type(ev) !=
LIBINPUT_EVENT_DEVICE_ADDED) {
libinput_event_destroy(ev);
continue;
}
device = libinput_event_get_device(ev);
sysname = libinput_device_get_sysname(device);

2
udev/.gitignore vendored
View file

@ -1 +1,3 @@
libinput-device-group
libinput-model-quirks
*.rules

View file

@ -2,7 +2,7 @@ ACTION!="add|change", GOTO="libinput_device_group_end"
KERNEL!="event[0-9]*", GOTO="libinput_device_group_end"
ATTRS{phys}=="?*", \
PROGRAM="libinput-device-group %S%p", \
PROGRAM="@UDEV_TEST_PATH@libinput-device-group %S%p", \
ENV{LIBINPUT_DEVICE_GROUP}="%c"
LABEL="libinput_device_group_end"

View file

@ -11,7 +11,7 @@
# Match string formats:
# libinput:<modalias>
# libinput:name:<name>:dmi:<dmi string>
# libinput:name:<name>:fwversion:<version>
#
# Sort by brand, model
@ -22,12 +22,17 @@ libinput:name:*AlpsPS/2 ALPS DualPoint TouchPad:dmi:*
libinput:name:*AlpsPS/2 ALPS GlidePoint:dmi:*
LIBINPUT_MODEL_ALPS_TOUCHPAD=1
libinput:name:*AlpsPS/2 ALPS DualPoint TouchPad:fwversion:8
libinput:name:*AlpsPS/2 ALPS GlidePoint:fwversion:8
LIBINPUT_ATTR_SIZE_HINT=100x55
##########################################
# Apple
##########################################
libinput:touchpad:input:b0003v05ACp*
libinput:touchpad:input:b0005v05ACp*
LIBINPUT_MODEL_APPLE_TOUCHPAD=1
LIBINPUT_ATTR_SIZE_HINT=104x75
##########################################
# Google

View file

@ -11,6 +11,20 @@
ACTION!="add|change", GOTO="libinput_model_quirks_end"
KERNEL!="event*", GOTO="libinput_model_quirks_end"
# Touchpad firmware detection, two-stage process.
# First, run the program and import the LIBINPUT_MODEL_FIRMWARE_VERSION
# environment (if any)
KERNELS=="*input*", \
ENV{ID_INPUT_TOUCHPAD}=="1", \
IMPORT{program}="@UDEV_TEST_PATH@libinput-model-quirks %S%p"
# Second, match on anything with that env set and import from the hwdb
KERNELS=="*input*", \
ENV{ID_INPUT_TOUCHPAD}=="1", \
ENV{LIBINPUT_MODEL_FIRMWARE_VERSION}!="", \
IMPORT{builtin}="hwdb 'libinput:name:$attr{name}:fwversion:$env{LIBINPUT_MODEL_FIRMWARE_VERSION}'"
# End of touchpad firmware detection
# Matches below are exclusive, if one matches we skip the rest
# hwdb matches:
#

View file

@ -1,10 +1,22 @@
udevdir=$(UDEV_DIR)
udev_PROGRAMS = libinput-device-group
udev_PROGRAMS = libinput-device-group \
libinput-model-quirks
litest_rules = 80-libinput-device-groups-litest.rules \
90-libinput-model-quirks-litest.rules
udev_SCRIPTS = $(litest_rules)
libinput_device_group_SOURCES = libinput-device-group.c
libinput_device_group_CFLAGS = $(LIBUDEV_CFLAGS) $(GCC_CFLAGS)
libinput_device_group_LDADD = $(LIBUDEV_LIBS)
libinput_model_quirks_SOURCES = libinput-model-quirks.c
libinput_model_quirks_CFLAGS = \
-I$(top_srcdir)/src \
$(LIBUDEV_CFLAGS) \
$(GCC_CFLAGS)
libinput_model_quirks_LDADD = $(LIBUDEV_LIBS)
udev_rulesdir=$(UDEV_DIR)/rules.d
dist_udev_rules_DATA = \
80-libinput-device-groups.rules \
@ -13,3 +25,11 @@ dist_udev_rules_DATA = \
udev_hwdbdir=$(UDEV_DIR)/hwdb.d
dist_udev_hwdb_DATA = \
90-libinput-model-quirks.hwdb
%-litest.rules: %.rules.in
$(SED) -e "s|\@UDEV_TEST_PATH\@|$(abs_builddir)/|" < $^ > $@
CLEANFILES = $(litest_rules)
DISTCLEANFILES = \
80-libinput-device-groups.rules \
90-libinput-model-quirks.rules

View file

@ -0,0 +1,116 @@
/*
* Copyright © 2015 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libudev.h>
#include <linux/input.h>
#include "libinput-util.h"
static inline const char *
prop_value(struct udev_device *device,
const char *prop_name)
{
struct udev_device *parent;
const char *prop_value = NULL;
parent = device;
while (parent && !prop_value) {
prop_value = udev_device_get_property_value(parent, prop_name);
parent = udev_device_get_parent(parent);
}
return prop_value;
}
static void
handle_touchpad_alps(struct udev_device *device)
{
const char *product;
int bus, vid, pid, version;
product = prop_value(device, "PRODUCT");
if (!product)
return;
if (sscanf(product, "%x/%x/%x/%x", &bus, &vid, &pid, &version) != 4)
return;
/* ALPS' firmware version is the PID */
if (pid)
printf("LIBINPUT_MODEL_FIRMWARE_VERSION=%d\n", pid);
}
static void
handle_touchpad(struct udev_device *device)
{
const char *name = NULL;
name = prop_value(device, "NAME");
if (!name)
return;
if (strstr(name, "AlpsPS/2 ALPS") != NULL)
handle_touchpad_alps(device);
}
int main(int argc, char **argv)
{
int rc = 1;
struct udev *udev = NULL;
struct udev_device *device = NULL;
const char *syspath;
if (argc != 2)
return 1;
syspath = argv[1];
udev = udev_new();
if (!udev)
goto out;
device = udev_device_new_from_syspath(udev, syspath);
if (!device)
goto out;
if (udev_device_get_property_value(device, "ID_INPUT_TOUCHPAD"))
handle_touchpad(device);
rc = 0;
out:
if (device)
udev_device_unref(device);
if (udev)
udev_unref(udev);
return rc;
}