Compare commits

...

186 commits

Author SHA1 Message Date
Peter Hutterer
0412fb3d44 pad: fix missing newline for mode group error messages
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1380>
2025-12-17 14:37:59 +10:00
David Santamaría Rogado
a376fe366c quirks: HP 14-fh0xxx rely on bus, ids and type
Signed-off-by: David Santamaría Rogado <howl.nsp@gmail.com>
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1378>
2025-12-16 21:52:31 +01:00
David Santamaría Rogado
9a07784451 quirks: Sort HP entries alphabetically
Also remove generic HP Haptics Touchpad CFD2 moved to Synaptics.

Signed-off-by: David Santamaría Rogado <howl.nsp@gmail.com>
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1378>
2025-12-16 21:51:53 +01:00
David Santamaría Rogado
3cf723451f quirks: Add generic Synaptics quirk found in HP ones
This quirk is a generic one for all the HP laptops with haptic touchpad
so makes more sense here because we are applying it dmi independent
being more difficult to track this change if the touchpad became used in
other vendors.

Signed-off-by: David Santamaría Rogado <howl.nsp@gmail.com>
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1378>
2025-12-16 16:35:22 +01:00
Peter Hutterer
2d3e47290c quirks: add a quirk for the RazerBlade182025 keyboard
Generated by tools/razer-quirks-lister.py

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1379>
2025-12-16 16:54:18 +10:00
Peter Hutterer
5bd7f93c16 doc/user: add a graph of how different contexts work
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1376>
2025-12-12 04:29:13 +00:00
Peter Hutterer
a202ed6115 Use a newtype usec_t for timestamps for better type-safety
This avoids mixing up milliseconds and usec, both by failing if
we're providing just a number somewhere we expect usecs and also
by making the API blindingly obvious that we're in usecs now.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1373>
2025-12-12 04:15:15 +00:00
Peter Hutterer
c0c809aaa1 util: fix documentation issues for newtypes
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1373>
2025-12-12 04:15:15 +00:00
Peter Hutterer
e8dfc3bfd6 util: make the newtype comparision functions return bool
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1373>
2025-12-12 04:15:15 +00:00
Sicelo A. Mhlongo
b3f7b4b1ea evdev: add support for SW_KEYPAD_SLIDE
A few devices have a keyboard/keypad which can be slid under the device,
leaving the device with only touch-based interaction. The corresponding kernel
event is reported as SW_KEYPAD_SLIDE [0]. Implement support in libinput.

Since the position of the switch varies across devices, it cannot always be
certain whether the keypad is usable when the switch is in the set position.
Therefore, do not automatically disable the keyboard.

[0] e68d80b13b/include/linux/linux/input-event-codes.h (L885)

Closes: #1069
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1242>
2025-12-11 13:28:41 +02:00
Peter Hutterer
0285001272 touchpad: make the dwt/dwtp timeout inclusive min/max
This makes it easier in callers that don't really differ between
inclusive and exclusive and makes the visualization in UIs a bit
nicer to look at too.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1377>
2025-12-10 03:07:36 +00:00
Peter Hutterer
bde6d07d57 quirks: replace ModelPressurepad with setting INPUT_PROP_PRESSUREPAD
This is the more generic approach and doesn't require us to have any
specific implementation in libinput itself.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1374>
2025-12-09 06:07:01 +00:00
Peter Hutterer
1b4dbb9087 doc/user: minor update to the forcepad/pressurepad documentation
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1374>
2025-12-09 06:07:01 +00:00
Peter Hutterer
d5d38b2ed1 doc/user: fix two typos in ref anchors
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1374>
2025-12-09 06:07:01 +00:00
Peter Hutterer
38b5c2e0cc touchpad: add configurable timeouts to disable-while-trackpointing
Same motivation as in commit
"touchpad: add configurable timeouts to disable-while-typing"

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1372>
2025-12-02 09:24:15 +10:00
Peter Hutterer
20851b5020 touchpad: add configurable timeouts to disable-while-typing
Touchpads that don't give us useful palm detection data are getting more
common (see e.g. our ABS_MT_TOOL_TYPE quirks). On those touchpads we can
only rely on dwt and palm edge detection which means those two must be
more spot on than ever before.

DWT in particular is more prone to user-specific requirements, the
current timeouts have been insufficient for a number of users. So let's
make them more configurable.

Currently limited to >100ms and <5 seconds to avoid DWT being used in
the xkcd workflow style.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1372>
2025-12-02 09:24:15 +10:00
Peter Hutterer
2003ab3aef test: rename a dwt test for better clarity
This is the "short-timeout after a single key press" dwt test so let's
rename it that way

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1372>
2025-12-02 09:20:08 +10:00
Peter Hutterer
4bb6a31894 doc: fix a few miss-hyphenated disable-while-typing comments
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1372>
2025-12-02 09:20:08 +10:00
Peter Hutterer
db6a04665c touchpad: use INPUT_PROP_PRESSUREPAD as signal it's a pressurepad
Because, well, it says so on the box now. No more quirks, hopefully!

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1359>
2025-12-01 21:39:11 +00:00
Peter Hutterer
7621edab05 include: sync headers with kernel 6.18
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1359>
2025-12-01 21:39:11 +00:00
Peter Hutterer
39aea2a8d6 test: silence a valgrind warning
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1370>
2025-11-27 09:32:13 +10:00
Peter Hutterer
7e8298e9ec tablet: when destroying a tablet, unlink the tablet tools
If a caller holds a ref to a tablet tool when the device is
destroyed, the tool didn't get removed from the tablet->tool_list.

Later on tool unref the list_remove() would try to reset the pointers
but the list head was long since freed, causing an invalid write.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1370>
2025-11-27 09:26:54 +10:00
Peter Hutterer
49d9528bdf tablet: after three valid prox out events, unload the forced prox plugin
Once we receive three valid proximity out events for the pen, unload the
plugin that generates forced proximity in events for tablets that
don't send BTN_TOOL_PEN.

Closes #1222

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1370>
2025-11-26 10:55:03 +10:00
Peter Hutterer
1e8901d009 tablet: only apply eraser button settings if we have a device
If the device is unplugged, our tool's last_device is NULL. If a caller
then tries to the toggle the eraser button setting libinput would crash.
Fix this by simply skipping the configuration until the tool goes back
into proximity over some other device (if any).

Closes #1223

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1370>
2025-11-26 10:54:47 +10:00
Peter Hutterer
a6ad084280 tablet: return the wanted eraser button mode/button from the config
This matches our behavior for other settings - always return the
user-configured setting from the configuration API, not the current
setting (which may be delayed until the device is in a netural state).

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1370>
2025-11-26 10:32:18 +10:00
Peter Hutterer
11b97edcdc libinput 1.30.0
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2025-11-25 13:40:43 +10:00
Peter Hutterer
c2f9ffe17e doc/user: drop references to the touchpad-edge-detector
We've had libinput measure touchpad-size for 5 years now, let's refer
only to that.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1368>
2025-11-24 14:29:56 +10:00
Peter Hutterer
21e3a76bf5 libinput 1.29.902 2025-11-18 12:29:32 +10:00
Peter Hutterer
9461d1a9a1 Revert "lua: drop compatibility to 5.1 to allow for luajit"
While luajit seems to be the most popular (and fastest) lua
implementation for higher-level implementations, at the system level
it is relatively unused. Lua 5.4 on the other hand is used by other
system-level components like wireplumber and RPM. In the latter case
this means that lua is already available on every rpm-based distro
without further dependencies.

The performance of 5.4 seems to be acceptable and while luajit may be
faster the extra dependency requires more maintenance. Let's only expose
ourselves to that if absolutely needed.

This is not a strict revert because the code has changed a bit since
with several bugfixes deployed on top.

This reverts commit 2723cadaeb.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1366>
2025-11-18 01:46:53 +00:00
Peter Hutterer
a595f0bd48 lua: export _VERSION so plugins can check the lua version
Since we only support one version this is not very informative but let's
include this now in case we ever need it in the future.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1365>
2025-11-17 16:48:35 +10:00
David Senoner
32ed3060f9 meson.build: use absolute path for callouts in udev rules
Udev rules can be put in many different directories to be found by udev
(see link #1). The callouts can be put only in one specific directory
if you want them to be found by using a relative path (only in
UDEV_LIBEXECDIR, see link #2). By passing the absolute path to the rule
you can make sure the callout will always be found.

Link 1: https://man7.org/linux/man-pages/man7/udev.7.html
Link 2: https://github.com/systemd/systemd/blob/main/src/udev/udev-spawn.c#L289

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1364>
2025-11-12 07:49:18 +01:00
Yinon Burgansky
a12dc6eba3 filter: differentiate scroll wheel from button scrolling to fix wheel speed
Commit 94b7836456 ("filter: support accelerating high-resolution
scroll wheel events") introduced a regression where high-res scroll
wheel events were incorrectly normalized by DPI. Mice with non-default
DPI (e.g., Logitech G502 at 2400 DPI) had their scroll wheel speed
reduced by the DPI ratio (1000/2400), resulting in 2-3x slower
scrolling.

The "noop" filter functions were actually performing DPI normalization
or applying a constant acceleration factor, which is appropriate for
button scrolling but incorrect for scroll wheels that have their own
units.

Add a filter_scroll_type enum (CONTINOUS, WHEEL, FINGER to match the
public events) passed through the filter_scroll interface. Update all
filter implementations to skip acceleration and normalization for wheel
events while maintaining existing behavior for button scrolling and
touchpad scrolling.

The custom acceleration profile continues to accelerate high-res wheel
events as designed.

Fixes: 94b7836456 ("filter: support accelerating high-resolution scroll wheel events")

Closes: #1212

Signed-off-by: Yinon Burgansky <yinonburgansky@gmail.com>
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1363>
2025-11-11 14:43:41 +10:00
Marien Zwart
f6caae2289 quirks: add quirk for MX Master 4
The scrollwheels are similar to the MX Master 3, and need the same
quirks: the horizontal wheel events are inverted (it scrolls "naturally"
by default while the horizontal scrollwheel direction is "traditional"),
and middle-clicking without scrolling is very difficult with high-res
scroll events (from the hid_logitech_hidpp kernel module) enabled.

This adds the device ID seen through Bluetooth, which seems to be the
only one we can add a quirk for:

- When connected using the Bolt receiver, there is no separate device ID
  for the mouse (just the same 046d:c548 ID for the receiver already
  documented as supporting multiple mice).

- When connected through USB, the mouse charges but does not provide HID
  events through USB (it can be used while charging but only by using a
  separate Bluetooth or Bolt connection for HID).

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1362>
2025-11-09 00:09:47 +00:00
Jan Hendrik Farr
20a3131947 quirks: add quirk for Google Chromebook Rull
Signed-off-by: Jan Hendrik Farr <kernel@jfarr.cc>
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1360>
2025-11-08 23:53:49 +00:00
Marien Zwart
822e571272 doc: fix gitlab remote url
SSH to gitlab.freedesktop.org times out.
ssh.gitlab.freedesktop.org (copied from the Gitlab UI) works..

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1361>
2025-11-08 15:02:59 +11:00
Peter Hutterer
9c78f989fb CI: bump to Fedora 43 and Ubuntu 25.10
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1355>
2025-11-07 15:13:08 +00:00
Peter Hutterer
9b37ffd340 meson.build: fix indentation for the python files
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1357>
2025-11-06 23:46:42 +00:00
Peter Hutterer
b9a04e4d57 meson.build: export the plugin dir via pkgconfig
This is for the system-wide plugindir only - the only path where we'd
expect other packages to put plugins.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1357>
2025-11-06 23:46:42 +00:00
Peter Hutterer
4fd5fe9d30 Fix clang-tidy false positives
Array out of bounds complaints but it's a false positive where
clang-tidy makes up some event flow that cannot happen.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1358>
2025-11-06 23:31:27 +00:00
Peter Hutterer
941aa9f997 plugins: add an example for using a wheel to trigger button scrolling
See https://gitlab.freedesktop.org/libinput/libinput/-/issues/1206

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1356>
2025-11-06 23:18:43 +00:00
Peter Hutterer
b870abd2f3 libinput 1.29.901
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2025-11-04 11:58:29 +10:00
Peter Hutterer
ad6f580b6d lua: add select to the list of allowed functions
This function is safe (and was already documented) so let's make sure
it's available.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1354>
2025-11-04 00:27:15 +00:00
Peter Hutterer
4ffd8ab544 lua: fix the event type/code loops - EV_MAX/ABS_MAX is inclusive
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1354>
2025-11-04 00:27:15 +00:00
Peter Hutterer
eac44529d8 lua: return an empty table as device info after device removal
As the documentation already promises.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1354>
2025-11-04 00:27:14 +00:00
Peter Hutterer
4fe3225050 lua: remove the unpack global function
Looks like 5.1 and later has unpack as table.unpack().

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1354>
2025-11-04 00:27:14 +00:00
Peter Hutterer
45878c3aac lua: fix superfluous comma
Not sure how this compiled but whoah, definitely not supposed to be
there.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1354>
2025-11-04 00:27:14 +00:00
Peter Hutterer
f0d8002255 plugins: fix some minor issues with the example plugins
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1354>
2025-11-04 00:27:13 +00:00
Peter Hutterer
037c07d76f doc/user: fix a few issues with the Lua API documentation
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1354>
2025-11-04 00:27:13 +00:00
Peter Hutterer
1dcf630584 plugins: add an example plugin on how to reconfigure the Copilot key
As Microsoft has decreed, that key sends LEFTMETA + LEFTSHIFT + F23
(usually across multiple frames) and the then the same in reverse when
released.

xkeyboard-config 2.44 maps this sequence by default to XF86Assistant but
for the use-cases where this does not work reshuffling the whole event
sequence is the best approach and that can easily be done with a plugin.

Note that this is the minimum effort plugin - it works for one
keyboard at at time (duplicate the plugin if two keyboards are needed,
or remove the vid/pid check) and it does *not* intercept the meta/shift
key presses and delay them, it simply releases them on F23 and then
replays the new sequence. Good enough for an example plugin.

Example sequence produces shift + a on press and releases a + shift on
release. Holding the key will thus produce AAAAAAAAA which is an
excellent summary of how this key was designed.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1352>
2025-11-03 03:57:00 +00:00
Peter Hutterer
eb01a4e73f plugins: add meson option to autoload plugins
Add an option to enable autoloading plugins from the default paths.
This makes testing and adoption for new users easier as they can (if
necessary) rebuild libinput with that option enabled instead of having
to wait for the compositor stack to update.

Autoloading will only use the default paths (/etc and /usr/lib) and will
only happen if the client does not modify those paths since that implies
the client wants to load plugins themselves. A client that adds a plugin
path but doesn't load the plugins is considered buggy anyway.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1347>
2025-10-31 05:25:29 +00:00
Peter Hutterer
f27fbdfa53 meson.build: change set10 to set
Follow-up to cfec80582e ("meson.build: change from config.set10() and #if to config.set() and #ifdef")
which was parallel to 9e37bc0cfa and the latter didn't get updated.

Fixes: 9e37bc0cfa ("plugins: add support for lua plugins to change evdev event streams")
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1347>
2025-10-31 05:25:29 +00:00
Jan Hendrik Farr
07659db3d9 quirks: add quirk for Google Chromebook Roric
Signed-off-by: Jan Hendrik Farr <kernel@jfarr.cc>
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1353>
2025-10-30 13:40:59 +01:00
Peter Hutterer
f18bf988f7 lua: drop the log global in favor of libinput:log_*
The existence of the log global was in part due to early (pre-merge)
versions of the Lua plugins supporting multiple libinput plugin objects
per file. This is no longer the case and integrating the log functions
into the (single) libinput object makes the code more obvious (we're
calling libinput:log_debug() now, so it's obviously a libinput log
function) and we no longer mix dot with colon notations.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1350>
2025-10-30 13:12:50 +10:00
Peter Hutterer
6b9dbc2a25 lua: remove the inject_frame API
Injecting frame was the first implementation of adding event frames but
it has since effectively been replaced by append/prepend_frame which are
more predictable and easier to support.

In the Lua API injecting frames was only possible within the timer and
the only real use-case for this is to inject events that are then also
seen by other plugins. But that can be achieved by simply ordering the
plugin before the other plugins and using the append/prepend approach.

Until we have a real use-case for injecting events let's remove the API
so we don't lock ourselves into an API that may not do what it needs to
but needs to be supported for a long time.

Closes: #1210
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1351>
2025-10-30 02:08:13 +00:00
Peter Hutterer
2784973b4d doc/user: update the lua plugin documentation
Some clarifications, some fixes, some reshuffling, overall somewhat
better.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1349>
2025-10-30 01:46:33 +00:00
Peter Hutterer
22dda5b154 doc: some updates to the plugin system docs
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1349>
2025-10-30 01:46:33 +00:00
Peter Hutterer
07a9161ef2 tools: add support for NO_COLOR/FORCE_COLOR
Environment variables to control whether the output should not have
color or must have color, regardless of the tty-ness of the output
stream.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1348>
2025-10-27 12:35:44 +10:00
Peter Hutterer
141f571aae tools: don't set the tool pressure range if we're using the default
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1340>
2025-10-24 01:55:25 +00:00
Peter Hutterer
48a3391f33 tablet: don't consider a 0.0-1.0 pressure range as configured range
A caller is likely to unconditionally call
libinput_tablet_tool_config_pressure_range_set() with whatever
values it has in its config storage. Those values will be 0 and 1 by
default, we should not take this as a sign that the tool has a pressure
range.

Setting a pressure range resets the automatic offset handling which we
definitely don't want to do for the default range.

Fixes: #1177
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1340>
2025-10-24 01:55:25 +00:00
Peter Hutterer
bda0c7478e tablet: when configuring a user-defined pressure range, zero the offset
When a user configures a pressure range, the tool would get locked into
the current offset even if that offset was still pending. For example, a
sequence of

- tool in-prox with pressure 50%
- libinput_tablet_tool_config_pressure_range_set(tool, 0.0, 0.9)
- tool out-of-prox, tool-in-prox
- libinput applies the tool config, tool now has a configured range,
  has_offset = true
- tool out-of-prox, tool-in-prox
- update_pressure_range() sees has_offset = true, scales the last offset
  (50%) to the actual offset.

Fix this by resetting the detected offset to zero when we shortcut the
heuristics. A user-defined pressure range should include the tool's
pressure offset anyway, the user knows this much better than our
heuristics.

Closes: 1177
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1340>
2025-10-24 01:55:25 +00:00
Peter Hutterer
66f2d121ee tablet: log the pressure offset's percentage once we detected it
Saying the offset is e.g. 15% is more informative to the user than a
vauge "we detected something".

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1340>
2025-10-24 01:55:25 +00:00
Peter Hutterer
60c5fdbc2f doc: fix wrong reference to the plugin system
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1345>
2025-10-24 01:34:52 +00:00
Yinon Burgansky
cc7dfccd22 filter: avoid division-by-zero when delta_time is zero in custom filter
delta_time can be zero when:
- the fallback acceleration function is used for multiple movement types
  (for example, pointer motion and wheel scrolling simultaneously)
- two different methods produce the same movement at the same time
  (for example, button-scrolling and wheel-scrolling)

Reusing the last delta_time is a graceful fallback even if there are
duplicate events or event-ordering bugs.

Signed-off-by: Yinon Burgansky <yinonburgansky@gmail.com>
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1336>
2025-10-24 01:18:40 +00:00
Yinon Burgansky
94b7836456 filter: support accelerating high-resolution scroll wheel events
Dispatch high-resolution scroll wheel events through filter_dispatch_scroll
so they can be accelerated using the custom acceleration profile.

Low-resolution scroll wheel events are not accelerated to avoid zero
delta-time in the filter.

Signed-off-by: Yinon Burgansky <yinonburgansky@gmail.com>
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1316>
2025-10-24 01:05:54 +00:00
Zephyr Lykos
2d4482e03d meson.build: remove useless meson version checks
meson version requirement is bumped to 0.64.0 since
9e37bc0cfa ("plugins: add support for lua plugins to change evdev event streams")

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1344>
2025-10-24 00:21:12 +00:00
Zephyr Lykos
607852e66d meson.build: mark executables with install_tag
install_tag is available since meson 0.60.0:
https://mesonbuild.com/Reference-manual_functions.html#executable_install_tag

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1344>
2025-10-24 00:21:12 +00:00
wangyafei
a60976c1d4 quirks: add quirks for Dell laptop with Sensel Touchpad.
This touchpad is a pressure pad and needs the pressure
handling disable.

Signed-off-by: Charles Wang <charles.goodix@gmail.com>
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1342>
2025-10-24 00:01:00 +00:00
José Expósito
aac0d3c2f8 quirks: Avoid accidental scroll for Logitech MX Master 2S
Similar to the MX Master 3, this mouse can send scroll events when the
wheel is pressed.

Add the "ModelScrollOnMiddleClick" quirk to avoid it.

Closes: https://gitlab.freedesktop.org/libinput/libinput/-/issues/1181
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1343>
2025-10-23 21:51:36 +00:00
José Expósito
327db95a7c doc/user: Document ModelScrollOnMiddleClick quirk
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1343>
2025-10-23 21:51:35 +00:00
José Expósito
4a705ba8d7 quirks: Rename QUIRK_MODEL_LOGITECH_MX_MASTER_3
The Logitech MX Master 2S also triggers accidental scroll events when
the mouse wheel is pressed [1].

Rename the "ModelLogitechMXMaster3" quirk to "ModelScrollOnMiddleClick"
to make it more generic.

[1] https://gitlab.freedesktop.org/libinput/libinput/-/issues/1181

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1343>
2025-10-23 21:51:35 +00:00
Peter Hutterer
d6e1f93812 plugins: update plugins for the current API
A few changes to the Lua API didn't get reflected in the example
plugins, let's update them.

Also included here is naming of all arguments, instead of _ use the
argument name even where unused. These are examples so being expressive
is more important than making any lua static checkers happy.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1339>
2025-10-23 13:28:16 +00:00
Peter Hutterer
dd3f931481 plugins: rename the flags to libinput_plugin_system_flags
"libinput_plugins_flags" is bound to be annoying to approximately
everyone who'll ever have to use them so let's rename this while we
still can. Renamed to libinput_plugin_system_flags to leave the
namespace open for a possible future libinput_plugin_flags that works
on individual plugins.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1337>
2025-10-23 13:13:01 +00:00
Peter Hutterer
d1720d351d meson.build: bump to 1.29.900
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1338>
2025-10-23 00:06:09 +00:00
Marge Yang
fcb5dfe515 quirks: add Dell laptop touchpad quirks
This touchpad is a pressure pad.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1335>
2025-10-21 17:25:13 +08:00
Peter Hutterer
43c7644f01 quirks: add a quirk for the RazerBlade142025 keyboard
Generated by tools/razer-quirks-lister.py

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1334>
2025-10-21 04:56:12 +00:00
Peter Hutterer
3307341ebf CI: drop the ci-fairy check-mr job
The only thing this checked was the checkbox for allowing maintainers to
edit the MR. Changed permissions checks now fail this job but luckily
the setting it checked has been the default for years anyway so we can
drop it.

https://gitlab.freedesktop.org/freedesktop/ci-templates/-/issues/81

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1332>
2025-10-20 12:35:51 +10:00
Peter Hutterer
4b1bae3f8c tools/replay: rmdir, not unlink for directories
And if the directory happens to be nonempty, well, maybe anyother
libinput reply is running so let's ignore that.

Fixes: cdfe34f62a ("replay: use the runtime quirks for our replay quirks")
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1330>
2025-10-20 02:01:23 +00:00
Peter Hutterer
f9977dba9c tools/replay: set the multiprocessing start method to fork
Python 3.14 has changed from fork to forkserver[1] which causes
libinput replay to fail with the following error when starting the
subprocesses:
   ValueError: ctypes objects containing pointers cannot be pickled

This is caused by the libevdev device being passed to the sub process.
A more proper fix may be to only initialize the device in the subprocess
and then signal the process to start replaying. But meanwhile, switching
back to fork will do.

[1] https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods

Closes #1204

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1330>
2025-10-20 02:01:23 +00:00
Marge Yang
3725bb0b93 quirks: add Dell laptop touchpad quirks
This touchpad is a pressure pad.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1329>
2025-10-16 06:13:34 +00:00
Cyrus Lien
d19f95ec21 quirks: add quirks for Dell Pro Rugged tablets for volume keys.
Close #1205

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1331>
2025-10-16 13:42:37 +08:00
Peter Hutterer
533d5f6ee1 lua: ignore unsupported event codes in modified frames
If a plugin adds events to an event frame that are not supported by the
target device we may eventually dereference a null pointer (for ABS_*
events) or, possibly, use an OOB index access (for buttons or keys).
Let's filter out any events that the device doesn't support immediately.

Fixes #1202

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1324>
2025-10-14 06:08:37 +00:00
Peter Hutterer
3250686e70 lua: add missing lua_pop for the evdev frame
Where a plugin returns a table to be processed we never popped that
table from the stack. Eventually this would lead to the plugin failing
as the stack size is exceeded.

Fixes #1195

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1323>
2025-10-14 05:53:06 +00:00
Sertonix
aa7d58005e Fix format strings on 32-bit with 64-bit time_t
This fixes a segfault when running the test suite on ppc musl

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1318>
2025-10-14 05:40:19 +00:00
José Expósito
5df3eb8527 quirks: add Dell 14 Premium touchpad quirks
This touchpad is a pressure pad.

Closes: https://gitlab.freedesktop.org/libinput/libinput/-/issues/1185
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1320>
2025-10-13 06:53:31 +00:00
José Expósito
185e1bd824 quirks: add Dell 16 Premium touchpad quirks
This touchpad is a pressure pad.

Closes: https://gitlab.freedesktop.org/libinput/libinput/-/issues/1198
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1320>
2025-10-13 06:53:31 +00:00
Daniel Brackenbury
195c39b21a quirks: revert Nulea USB quirk due to ID conflict
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1325>
2025-10-13 06:35:42 +00:00
Peter Hutterer
6dcb47185e triage-policies: add a bugbot hook for re-closing a bug
Having the bugbot comment on re-closing the same bug is friendlier than
just closing it, even if we explained it before already.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1321>
2025-10-13 06:21:26 +00:00
Daniel Brackenbury
e07e138809 quirks: add vendor quirks for Nulea M501 thumb trackball
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1322>
2025-10-09 18:39:49 -04:00
José Expósito
a4ea1e2d97 libinput: Add libinput_event_gesture forward declaration
Closes: https://gitlab.freedesktop.org/libinput/libinput/-/issues/1189
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1319>
2025-10-07 00:31:38 +00:00
Dan Forest
772c1f5ebb quirks: add quirk for Asus ROG Flow Z13 2025 (GZ302EA) keyboard
Keyboard must be detected as internal in order for "disable touchpad while typing" to work as expected.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1317>
2025-10-03 15:04:37 +00:00
Mingcong Bai
95281de7e4 quirks: lower AttrTrackpointMultiplier for Lenovo ThinkPad E16 Gen 3
Lower TrackPoint multiplier for Lenovo ThinkPad E16 Gen 3 to match closer
to Windows defaults. The default multiplier was way too quick.

Signed-off-by: Mingcong Bai <jeffbai@aosc.io>
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1315>
2025-09-06 17:17:34 +08:00
José Expósito
32a9863507 quirks: Add quirks for the GPD MicroPC 2 touchpad
This laptop's touchpad has physical left, middle and right button, but
advertises itself as a clickpad.

Drop the incorrectly set INPUT_PROP_BUTTONPAD property.

Closes: https://gitlab.freedesktop.org/libinput/libinput/-/issues/1182

Signed-off-by: José Expósito <jose.exposito89@gmail.com>
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1314>
2025-09-04 09:57:26 +02:00
José Expósito
a0a6ff2777 plugin/wheel: Use libinput_device_is_virtual()
Use this function instead of evdev_device_is_virtual().

Signed-off-by: José Expósito <jose.exposito89@gmail.com>
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1313>
2025-09-02 10:59:31 +02:00
Peter Hutterer
60bcbb6c88 quirks: change VM devices to AttrIsVirtual
These devices had the debouncing disabled via a model quirk but
really they are virtual devices and should have all hw-specific
processing disabled - on the assumption that this will be handled
in the host. See also e.g. commit 5d23794d53 ("tablet: disable
smoothing for uinput devices").

Closes #1175

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1308>
2025-09-02 07:44:11 +00:00
David Santamaría Rogado
4f6c741570 quirks: HP OmniBook Ultra Flip Laptop 14-fh0xxx
HP OmniBook Ultra Flip Laptop 14-fh0xxx manages itself keyboard and
touchpad deactivation when HP's custom Intel ISH firmware is installed
in the system. Without the custom firmware tablet-mode switch isn't
exposed so there is no way we don't need this.
More detailed information in the file comment.

Signed-off-by: David Santamaría Rogado <howl.nsp@gmail.com>
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1311>
2025-09-02 07:23:22 +00:00
José Expósito
69ed729e80 quirks: don't disable the keyboard on the Dell Latitude 7285
Closes: https://gitlab.freedesktop.org/libinput/libinput/-/issues/1180
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1312>
2025-09-02 09:05:30 +02:00
Peter Hutterer
f3f8e8ef6c plugin: ensure prox out for a forced proximity tool if the tool changes
A device may send axis events while the tool is out of proximity,
causing our plugin to force a proximity in for the pen. If the tool then
sends a proximity event for a different tool we ended up with two tools
in proximity.

The sequence in #1171 shows this:

  - evdev:
    - [  1, 499608,   3,  27,       0] # EV_ABS / ABS_TILT_Y                0 (+30)
    - [  1, 499608,   0,   0,       0] # ------------ SYN_REPORT (0) ---------- +0ms
  - evdev:
    - [  2, 199637,   1, 321,       1] # EV_KEY / BTN_TOOL_RUBBER           1
    - [  2, 199637,   4,   4,      30] # EV_MSC / MSC_SCAN                 30 (obfuscated)
    - [  2, 199637,   1, 330,       1] # EV_KEY / BTN_TOUCH                 1
    - [  2, 199637,   3,   0,     910] # EV_ABS / ABS_X                   910 (+246)
    - [  2, 199637,   3,   1,    8736] # EV_ABS / ABS_Y                  8736 (-105)
    - [  2, 199637,   3,  27,     -25] # EV_ABS / ABS_TILT_Y              -25 (-25)
    - [  2, 199637,   0,   0,       0] # ------------ SYN_REPORT (0) ---------- +700ms

Fix this by remembering that we forced the tool out of proximity so if
we see tool events for another tool we force the pen out of proximity
again.

This will have some interplay with the other tablet plugins but
hopefully none that affect real-world devices, e.g. forcing a proximity
out means the proximity out timer plugin gets disabled. Since devices
behave in unexpected manners anyway let's see if it affects a real-world
device.

Closes #1171

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1306>
2025-08-31 00:18:59 +00:00
Peter Hutterer
ce1112c263 plugin: remove the proximity timer callback after prox out events
One of the code paths was addressed in b2cd9c69a0 but this path was
missing.

Fixes: b2cd9c69a0 ("plugin: remove the event frame callbacks when disabling a plugin")
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1306>
2025-08-31 00:18:59 +00:00
Peter Hutterer
428a3299a8 plugin: don't initialize the debounce plugin on a virtual device
Effectively the same motivation as commit 5d23794d53 ("tablet: disable
smoothing for uinput devices") - virtual devices should not need
debouncing and if they do let's do the debouncing on the other end that
creates those devices.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1307>
2025-08-28 01:56:12 +00:00
Peter Hutterer
bdb51593fe quirks: add quirk for the RazerBlade162025 keyboard
Generated by tools/razer-quirks-lister.py

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1305>
2025-08-26 20:51:55 +00:00
Alexander Bruy
d9c9e0ff25 add quirk for TongFang GX4 (X4SP4NAL) touchpad
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1302>
2025-08-20 08:07:36 +00:00
Peter Hutterer
4d317eae17 plugin: reset the filtered frame after sending it with buttons
Our filtered frame (frame without any button events) that gets passed
down to here may include relative motion or other events. Once we use
that frame to prepend the button event we need to reset it so we don't
cause duplicate motion and/or events with zero delta timestamps.

With the the previous code we got two identical frames:

  42: event25: plugin button-debounce        - 0.360 EV_MSC           MSC_SCAN               30
  ... event25: plugin button-debounce        - 0.360 EV_KEY           BTN_RIGHT               1
  ... event25: plugin button-debounce        - 0.360 EV_REL           REL_X                  -7
  ... event25: plugin button-debounce        - 0.360 EV_REL           REL_Y                  -7
  ... event25: plugin button-debounce        - 0.360 ----------------- EV_SYN ----------------- +8ms
  ... Plugin:button-debounce - debounce state: DEBOUNCE_STATE_IS_UP → DEBOUNCE_EVENT_OTHERBUTTON → DEBOUNCE_STATE_IS_UP
  ... Plugin:button-debounce - debounce state: DEBOUNCE_STATE_IS_UP → DEBOUNCE_EVENT_PRESS → DEBOUNCE_STATE_IS_DOWN_WAITING
  ... event25: plugin evdev                  - 0.360 EV_MSC           MSC_SCAN               30
  ... event25: plugin evdev                  - 0.360 EV_REL           REL_X                  -7
  ... event25: plugin evdev                  - 0.360 EV_REL           REL_Y                  -7
  ... event25: plugin evdev                  - 0.360 EV_KEY           BTN_RIGHT               1
  ... event25: plugin evdev                  - 0.360 ----------------- EV_SYN ----------------- +0ms
  ... Queuing  event25  POINTER_MOTION               +0.000s	-7.00/ -7.00 ( -7.00/ -7.00)
  ... event25 - middlebutton state: MIDDLEBUTTON_IDLE → MIDDLEBUTTON_EVENT_R_DOWN → MIDDLEBUTTON_RIGHT_DOWN, rc 1
  ... event25: plugin evdev                  - 0.360 EV_MSC           MSC_SCAN               30
  ... event25: plugin evdev                  - 0.360 EV_REL           REL_X                  -7
  ... event25: plugin evdev                  - 0.360 EV_REL           REL_Y                  -7
  ... event25: plugin evdev                  - 0.360 EV_KEY           BTN_RIGHT               1
  ... event25: plugin evdev                  - 0.360 ----------------- EV_SYN ----------------- +0ms
  ... Queuing  event25  POINTER_MOTION               +0.000s	 -nan/  -nan ( -7.00/ -7.00)
 event25  POINTER_MOTION               +0.360s	-7.00/ -7.00 ( -7.00/ -7.00)
 event25  POINTER_MOTION            2  +0.360s	 -nan/  -nan ( -7.00/ -7.00)

The 0ms delta caused a -nan in the custom pointer accel but even without
that bug the pointer would've jumped more than it should.

Closes #1172

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1301>
2025-08-20 07:49:01 +00:00
Peter Hutterer
2f2bd357bc plugin: don't register the mouse-wheel plugin for passthrough devices
If a device is immediately set to PASSTHROUGH let's skip registering
this plugin for this device. We're not doing anything with the events
anyway.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1300>
2025-08-19 15:22:49 +10:00
Peter Hutterer
b2cd9c69a0 plugin: remove the event frame callbacks when disabling a plugin
In all cases we remove the device's handling from the plugin so let's
remove the event frame callback for this device.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1300>
2025-08-19 15:22:49 +10:00
Peter Hutterer
3ff1a2e24d plugin: when disabling wheel-debouncing set to the correct enum value
This isn't a boolean. Let's change the first enum value to something
truthy so any use as boolean is more likely to trigger test failures.

Fixes: 18992b2ec0 ("plugin: allow disabling the wheel debouncing feature")
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1300>
2025-08-19 15:22:49 +10:00
Peter Hutterer
e45cd2bc13 util: print nonchanged axis with a space instead of a *
When printing tablet events always print a '*' or ' ' suffix to ensure
the alignment of the next field matches. We're using a tab to align
after each field so if the string length doesn't match, our events may
print at different tab stops.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1299>
2025-08-18 23:11:03 +00:00
Peter Hutterer
537552480d plugin: expand the plugin name prefix to 22 chars
That's the longest name we have for our internal plugins so let's make
sure those align nicely for easier debugging.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1299>
2025-08-18 23:11:03 +00:00
Peter Hutterer
5abe051a9c doc/user: fixes and a TOC for the Configuration Options page
A typo fix, more links and a local TOC since we now have quite a few
options.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1296>
2025-08-12 15:20:12 +00:00
Peter Hutterer
9b58177a7e doc/user: add eraser buttons to the "Configuration Options" page
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1296>
2025-08-12 15:20:12 +00:00
Peter Hutterer
cdfe34f62a replay: use the runtime quirks for our replay quirks
Closes #1166

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1297>
2025-08-12 07:58:07 +00:00
Peter Hutterer
ce87da63ba quirks: add XDG_RUNTIME_DIR/libinput to the quirks directories
This allows tools like libinput-replay and the test suite to install
temporary quirks without having to mess with actual system-installed
files or directories.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1297>
2025-08-12 07:58:07 +00:00
Peter Hutterer
4e002383cf clang-tidy: fix WarningAsErrors option to actually work
Not a boolean, despite what one would immediately assume.

Closes #1168

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1298>
2025-08-11 15:27:35 +10:00
Peter Hutterer
17617a75c4 pad: add some extra asserts to shut up clang-tidy
For some reason clang-tidy believes that in the second iteration group
is set to NULL and causes a NULL-pointer dereference.

../src/evdev-tablet-pad.c:305:2: error: Access to field 'next' results in a dereference of a null pointer [clang-analyzer-core.NullDereference,-warnings-as-errors]
  305 |         list_for_each(group, &pad->modes.mode_group_list, link) {
      |         ^
../src/util-list.h:265:13: note: expanded from macro 'list_for_each'
  265 |              pos = list_first_entry_by_type(&pos->member, __typeof__(*pos), member))
      |                    ^
../src/util-list.h:202:2: note: expanded from macro 'list_first_entry_by_type'
  202 |         container_of((head)->next, container_type, member)
      |         ^

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1298>
2025-08-11 15:27:31 +10:00
Peter Hutterer
48a26f91c3 CI: change the wayland-web job to use rules
"only: ..." is deprecated and we should be using rules

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1294>
2025-08-08 09:18:22 +00:00
Peter Hutterer
c0519c3b5e Correct the @since tags for the new plugin functions
Fixes: d557a649fd ("Add a public plugin system to libinput")
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1295>
2025-08-08 05:06:04 +00:00
Nat Karmios
960df4d8b8 quirks: Ignore BTN_0 for Microsoft Surface Keyboard
Signed-off-by: Nat Karmios <nat@karmios.com>
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1293>
2025-08-08 00:39:06 +01:00
Peter Hutterer
347ff90871 lua: implement support for disabling of features
Because our lua hooks don't expose the device-added callback we need to
cache any calls to disable a feature and then apply it quietly when the
device is actually added. Any other call can go straight through.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1249>
2025-08-07 10:23:30 +10:00
Peter Hutterer
5b2a723a02 touchpad: allow disabling palm detection altogether
Also a long-requested configuration option but it's difficult to expose
- depending on the touchpad we utilize different palm detection methods
and in theory may add to those at any time as we see fit.

Disabling it completely via a configuration option is only going to get
us more bug reports because *some* palm detection is likely desired on
most setups. So let's allow disabling it in a plugin and thus leave any
further palm detection code up to the plugin.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1249>
2025-08-07 10:23:30 +10:00
Peter Hutterer
afd3be9a99 touchpad: allow disabling the touchpad hysteresis
Over the years we had a few devices that required some special
hysteresis handling - all of it very customized to the device and not
upstreamable (or even implementable by upstream without the device).

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1249>
2025-08-07 10:23:30 +10:00
Peter Hutterer
2bb9c66cd7 touchpad: allow disabling the touchpad jumping cursor detection
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1249>
2025-08-07 10:23:30 +10:00
Peter Hutterer
18992b2ec0 plugin: allow disabling the wheel debouncing feature
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1249>
2025-08-07 10:23:30 +10:00
Peter Hutterer
cf52552eef plugin: allow disabling the button debouncing feature
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1249>
2025-08-07 10:21:59 +10:00
Peter Hutterer
7ac051ab41 plugin: add hooks to disable internal features
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1249>
2025-08-07 10:21:36 +10:00
Peter Hutterer
4f0b82800a plugins: remove two unused cleanup functions
Let's make clang tidy happy so at least one of us is.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1292>
2025-08-06 07:34:55 +00:00
Peter Hutterer
aa9d5bf630 util: silence two clang-tidy false positives
Both about the same complaint, somehow it is of the impression that
masks->mask can be an uninitialized value. The only place where we
allocate those is in _infmask_ensure_size() and they're set to zero
there.

libinput/src/util-bits.h:166:22: warning: The left operand of '&' is a garbage value [clang-analyzer-core.UndefinedBinaryOperatorResult]
  166 |         return !!(mask.mask & bit(bit));
      |                             ^
libinput/src/util-bits.h:444:2: note: Calling 'infmask_set_bit'
  444 |         infmask_set_bit(&m, mask);
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~
libinput/src/util-bits.h:394:15: note: Calling 'infmask_bit_is_set'
  394 |         bool isset = infmask_bit_is_set(mask, bit);
      |                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
libinput/src/util-bits.h:382:13: note: Field 'mask' is non-null
  382 |         if (!mask->mask || bit / bitmask_size() >= mask->nmasks)
      |                    ^
libinput/src/util-bits.h:382:6: note: Left side of '||' is false
  382 |         if (!mask->mask || bit / bitmask_size() >= mask->nmasks)
      |             ^
libinput/src/util-bits.h:382:2: note: Taking false branch
  382 |         if (!mask->mask || bit / bitmask_size() >= mask->nmasks)
      |         ^
libinput/src/util-bits.h:385:9: note: Uninitialized value stored to 'mask.mask'
  385 |         return bitmask_bit_is_set(mask->mask[bit / bitmask_size()], // NOLINT(core.UndefinedBinaryOperatorResult)
      |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  386 |                                   bit % bitmask_size());
      |                                   ~~~~~~~~~~~~~~~~~~~~~
libinput/src/util-bits.h:385:9: note: Calling 'bitmask_bit_is_set'
  385 |         return bitmask_bit_is_set(mask->mask[bit / bitmask_size()], // NOLINT(core.UndefinedBinaryOperatorResult)
      |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  386 |                                   bit % bitmask_size());
      |                                   ~~~~~~~~~~~~~~~~~~~~~
libinput/src/util-bits.h:166:22: note: The left operand of '&' is a garbage value
  166 |         return !!(mask.mask & bit(bit));
      |                   ~~~~~~~~~ ^

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1292>
2025-08-06 07:34:55 +00:00
Peter Hutterer
b831068fbb util: annotate that our mask cannot be NULL
Poor clang-tidy thinks that there's a code-path here that lets us
return with a NULL mask.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1292>
2025-08-06 07:34:55 +00:00
Peter Hutterer
a0d286741c touchpad: fix a clang-tidy warning
../src/evdev-mt-touchpad-buttons.c:1233:16: warning: Value stored to 'button' during its initialization is never read [clang-analyzer-deadcode.DeadStores]
 1233 |         evdev_usage_t button = evdev_usage_from_uint32_t(0);
      |                       ^~~~~~   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1292>
2025-08-06 07:34:54 +00:00
Peter Hutterer
5495511485 tools: move a clang-tidy silence back to where it needs to be
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1292>
2025-08-06 07:34:54 +00:00
Peter Hutterer
4ef50ee946 test: fix two clang-tidy dead store complaints
udev_device isn't used but we assign it for being auto-freed.

And color is overwritten immediately but it's better to have a known
good value for it anyway.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1292>
2025-08-06 07:34:54 +00:00
Peter Hutterer
a102269364 util: mark the various bitmask functions as nonnull
Might as well get a bit of compiler help here.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1292>
2025-08-06 07:34:54 +00:00
Peter Hutterer
2562c24f95 pad: don't assert when unable to find the mode group, just discard
Instead of a hard assert if we fail to find the mode group for the given
ring/dial/strip let's just log an error and discard the event.

I'm not sure this assert can be triggered in the current code base but
if it can an error message is going to be more useful to the user than
an assert.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1291>
2025-08-04 12:53:10 +10:00
Adam Sampson
47d4c563f4 evdev: remove duplicate sizeof
This looks like a copy-and-paste error. In practice it was harmless on
64-bit systems because evdev_event happens to be 64 bits long, but on
32-bit systems it would allocate too little memory.

Found by GCC 15 with _FORTIFY_SOURCE=3 on ia32.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1288>
2025-08-01 22:34:07 +00:00
Adam Sampson
931dad76a9 test: correct value type in atou64_test
This needs to be an unsigned 64-bit value, given the constants that are
stored in this field below; unsigned long is 32 bits on some platforms
(e.g. ia32).

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1288>
2025-08-01 22:34:07 +00:00
Matt Turner
7f3aa8058a test: Accept mkdir_p("/proc/foo") might return EACCES
... as it does under Gentoo's sandbox.

Fixes: 6770131e ("util: fix a memleak in mkdir_p")
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1289>
2025-08-01 11:11:31 -04:00
Peter Hutterer
c1d8d92b57 Drop evdev_frame_new_on_stack()
Was unused anwyay but also cannot work, returning an address to a frame
allocated on the stack is not a great idea...

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1192>
2025-08-01 16:49:00 +10:00
Peter Hutterer
2723cadaeb lua: drop compatibility to 5.1 to allow for luajit
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1192>
2025-08-01 16:49:00 +10:00
Peter Hutterer
9e37bc0cfa plugins: add support for lua plugins to change evdev event streams
This patch adds support for Lua scripts to modify evdev devices and
event frames before libinput sees those events.

Plugins in Lua are sandboxed and restricted in what they can do: no IO,
no network, not much of anything else.

A plugin is notified about a new device before libinput handles it and
it can thus modify that device (changes are passed through to our libevdev
context). A plugin can then also connect an evdev frame handler which
gives it access to the evdev frames before libinput processes them. The
example plugin included shows how to swap left/right mouse buttons:

    libinput:register({1})

    function frame(device, frame)
        for _, v in ipairs(frame.events) do
            if v.usage == evdev.BTN_RIGHT then
                v.usage = evdev.BTN_LEFT
            elseif v.usage == evdev.BTN_LEFT then
                v.usage = evdev.BTN_RIGHT
            end
        end
        return frame
    end

    function device_new(plugin, device)
        local usages = device:usages()
        if usages[evdev.BTN_LEFT] and usages[evdev.BTN_RIGHT] then
            device:connect("evdev-frame", frame)
        end
    end

    libinput:connect("new-evdev-device", device_new)

A few other example plugins are included in this patch

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1192>
2025-08-01 16:04:09 +10:00
Peter Hutterer
0c65b1069b test: allow creating a context with a custom plugindir
For runtime creation of plugins for testing

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1192>
2025-08-01 15:38:39 +10:00
Peter Hutterer
d557a649fd Add a public plugin system to libinput
This patch adds a new API for enabling public "plugins" in libinput, in
addition to the exisitng internal ones. The API is currently limited to
specifying which paths should be loaded and whether to load them.
Public plugins are static, they are loaded before the context is initialized
and do not update after that.

If plugins are to be loaded, libinput will then run through those paths,
look up files and pass them to (future) plugins to load the actual
files.

Our debugging tools have an --enable-plugins and
--disable-plugins option that load from the default data paths
(/etc/libinput/plugins and /usr/lib{64}/libinput/plugins) and from
the $builddir/plugins directory.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1192>
2025-08-01 15:38:39 +10:00
Peter Hutterer
d1dbbb7328 plugin: register plugins for the plugin-specific usages
The debounce, wheel/wheel-low-res, double-tool and eraser-button plugins can limit
themselves to a specific usage.

The mtdev plugin needs to pass each each event to mtdev and the proximity
timer plugin needs to get each event's timestamp so they cannot be
restricted.

The forced-tool could be restricted but effectively any event on the
devices it works on will have one of those usages set so there's no
point.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1271>
2025-08-01 14:29:44 +10:00
Peter Hutterer
3a86b6ea58 plugin: add evdev usage masks for plugins
This adds the ability for a plugin to announce the evdev usages it may
possibly care about. Only event frames that contain one or more of those
usages will be passed to the plugin. Ideally this is an optimization
over calling every plugin for every frame but that largely also depends
on what exactly the plugin does with each frame.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1271>
2025-08-01 14:29:44 +10:00
Peter Hutterer
f04b276ac1 util: add a helper struct for an evdev mask
Allows to keep track of a set of evdev masks, or at least the subset we
care about.

The EV_KEY range is split into two masks to save some memory. The future
use of this is for the plugins to use those masks and some of those will
set BTN_TOOL_PEN and friends. This would immediately create an 81 byte
mask of zeroes just to keep that one bit.

Splitting it into a key/btn mask with the latter starting at BTN_MISC
means we duplicate the infmask struct (2x16 bytes) but instead only use
8 bytes for the mask itself to keep the BTN_TOOL_PEN bits.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1271>
2025-08-01 14:29:44 +10:00
Peter Hutterer
d45f4493f1 util: add an infmask_t type for an infinitely-sized bitmask
Using the bitmask-size underneath but this mask can grow to any number
of bits requested. Notably, the sized of the mask is the nearest 4-byte
multiple for the highest bit ever set in the mask.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1271>
2025-08-01 14:29:44 +10:00
Peter Hutterer
4c4d5f33ee util: add bitmask_size() to return the size of a bitmask
This way we can theoretically change the bitmask to use a longer type in
the future.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1271>
2025-08-01 14:29:44 +10:00
Peter Hutterer
8336721dc0 test: fix the keycode obfuscation test
An earlier version of 73103a5c38 only obfuscated the plugin key codes,
not the "Queuing ..." message libinput itself uses. The test didn't get
updated when the queuing message was updated to obfuscate.

Fixes: 73103a5c38 ("plugin: always obfuscate keycodes")
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1284>
2025-08-01 04:02:57 +00:00
Peter Hutterer
6dfd72dc03 test: add litest_assert_strv_no_substring
To verify a strv does *not* include a given substring

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1284>
2025-08-01 04:02:57 +00:00
Peter Hutterer
ce85ee7d35 test: switch another test to use logcapture
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1284>
2025-08-01 04:02:57 +00:00
Peter Hutterer
d9a4667a14 test: track bug messages separately in log captures
Notably, this also tracks kernel bugs now as opposed to just other bugs.
It is up to tests checking for the expected message.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1284>
2025-08-01 04:02:57 +00:00
Peter Hutterer
465fce9128 test: don't run the check for lowres events if we don't have hires events
This test explicitly checks for devices only sending REL_WHEEL but *not*
REL_WHEEL_HI_RES. But that check only needs to run if the device has
the HI_RES axis.

The test device with REL_WHEEL_HI_RES disabled via quirk needs to be
special-cased since we cannot detect this in the test suite otherwise.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1284>
2025-08-01 04:02:57 +00:00
Peter Hutterer
68dbb98f04 clang-format: add litest_with_logcapture to foreach macros
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1284>
2025-08-01 04:02:56 +00:00
Peter Hutterer
a22883f7e2 CI: don't run the wayland-web job for marge
marge doesn't have sufficient permissions to trigger the pipeline, so
all we get is every trigger job failing. Let's disable this until it can
be figured out properly.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1287>
2025-08-01 03:46:22 +00:00
Peter Hutterer
7d59252643 quirks: minor cleanup to use attribute(cleanup)
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1285>
2025-08-01 02:19:15 +00:00
Peter Hutterer
59f0d8f647 tools/replay: improve the verbose output a bit
Make sure our SYN_REPORT line is indented correctly for multi-device
replay and match the output a bit closer to the one from libinput
record.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1286>
2025-08-01 11:24:06 +10:00
Peter Hutterer
936cee2242 libinput 1.29.0
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2025-07-31 15:26:54 +10:00
Peter Hutterer
3ca34aa88a evdev: move the SYN_REPORT 1 filtering to the touchpad backend
In commit 9a9466b6a9 ("evdev: discard any frame with EV_SYN SYN_REPORT 1")
all frames with a SYN_REPORT 1 were discarded on the assumption of those
being key repeat frames. Unfortunately the kernel uses the same sequence
to simply mark *any* injected/emulated event, regardless of the cause. Key repeat
events are merely the most numerous ones but as shown in commit
7140f13d82 ("evdev: track KEY_SYSRQ frames and pass them even as repeat frames")
Alt+PrintScreen is also an emulated event.

Issue #1165 details another case: keyboards with n-key rollover can
exceed the kernel-internal event buffer, typically 8 events for devices
without EV_REL/EV_ABS. Those events will be broken up by the kernel into
multiple frames - once nevents == buffer_size the current state is
flushed as SYN_REPORT 1 frame. Then, if any more events are pending
those are flushed as SYN_REPORT 0 frame. In the case of exactly 8
events, the second frame is never present, so we cannot easily detect if
another one is coming.

Issue #1145 only affects us in the touchpad code, the rest of the
backends seem to (so far) be fine. So let's move the discarding of
SYN_REPORT 1 to the touchpad backend and leave the rest of the code
as-is.

This effectively
Reverts: 7140f13d82 ("evdev: track KEY_SYSRQ frames and pass them even as repeat frames")
Reverts: 9a9466b6a9 ("evdev: discard any frame with EV_SYN SYN_REPORT 1")

Closes #1165

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1282>
2025-07-31 00:50:30 +00:00
Peter Hutterer
96d1954dce tools: add missing AttrIsVirtual handling for listing quirks
If a device has AttrIsVirtual set in the quirks we'd abort() when trying
to list those quirks.

Fixes: efb4b6a3be ("evdev: detect virtual devices")
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1281>
2025-07-30 05:43:03 +00:00
Peter Hutterer
b8651d798c tools: add a debug-tablet-pad tool
A simple tool to check the evdev and libinput events from a tablet pad.
This is near-identical to the existing debug-tablet tool but adjusted
for tablet pad events.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1280>
2025-07-28 23:40:33 +00:00
Peter Hutterer
74705ee94c util: add a clamp macro
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1280>
2025-07-28 23:40:33 +00:00
Peter Hutterer
e8d24f818b util: add two macros for clearing a line
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1280>
2025-07-28 23:40:33 +00:00
Peter Hutterer
4a67ddc111 utils: allow strv_join on a null string
NULL in means NULL out, as the docs says, no need for an assert here.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1280>
2025-07-28 23:40:32 +00:00
Peter Hutterer
cfec80582e meson.build: change from config.set10() and #if to config.set() and #ifdef
config.set10 is much more convenient and nicer to read but can provide
false positive if the value is 0 and #ifdef is used instead of #if. So
let's switch everything to use #ifdef instead, that way we cannot get
false positives if the value is unset.

Closes #1162

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1277>
2025-07-28 12:04:09 +10:00
Peter Hutterer
36b2afae82 plugin: ignore high-resolution wheel events from disabled wheels
Make sure we drop any potential high-resolution wheel events from a
device that isn't supposed to have them.

Where the device's axes were disabled due to a quirk, re-enabling the
axes means the device's events won't be filtered anymore. Our wheel
emulation plugin thus emulates high-resolution wheel events in addition
to the hardware events.

Fix this by simply filtering out any high-resolution wheel events on any
device that uses this plugin.

Closes #1160

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1279>
2025-07-24 09:13:12 +10:00
Peter Hutterer
6aefc2f166 libinput 1.28.903
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2025-07-21 13:34:46 +10:00
Peter Hutterer
4326413238 libinput: obfuscate the keycodes in the "Queuing ..." debug log
These messages are behind the 'internal-event-debugging' meson options
but let's be extra safe. It's rarely if ever required to see the actual
keycode anyway.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1276>
2025-07-21 13:02:43 +10:00
Peter Hutterer
73103a5c38 plugin: always obfuscate keycodes
Same approach as chosen in libinput-record, this leaves the F1-F10 out
but otherwise prints every other "normal" key (including modifiers) as
KEY_A.

In the future we may need some more specific approach but for now this
will do. For the use-cases where we do need some specific approach,
libinput record and libinput debug-events will still show the full
keycode on request anyway.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1276>
2025-07-21 13:02:43 +10:00
Peter Hutterer
abd5989ee8 plugin: prevent potential keycode leakage to the logs
Mixup of #if vs #ifdef caused this condition to always be treated as
true, resulting in leakage of key codes to the logs if the libinput log
level was set to debug.

Fixes: 7137eb9702 ("plugin: add ability to queue more events in the evdev_frame callback")

Closes #1163

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1276>
2025-07-21 13:02:20 +10:00
Peter Hutterer
7a22eb8d08 plugin/evdev: drop the duplicate event frame printing
The plugin system prints all events before they're passed to the plugin
anyway and the special evdev plugin does not do anything but pass it on.
We can thus assume that the events passed to libinput are the same as
the ones passed to this plugin.

Let's do that and adjust the print format to be closer to what
evdev_log_debug() would print.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1276>
2025-07-21 11:53:57 +10:00
Peter Hutterer
80b45ff28e test: a skipped test does not count as failure
There's a blurry line between NOT_APPLICABLE and SKIP but the latter
has a stronger "should run but can't right now". But where it happens
the skip shouldn't count as a failure.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1276>
2025-07-21 11:53:57 +10:00
Peter Hutterer
35838e9b2c test: fix litest_assert_str_not_in
Looks like an inadvertent paste or possibly a search regex gone wrong.
And the strstr condition was wrong too.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1276>
2025-07-21 10:12:29 +10:00
Peter Hutterer
013ed167a4 libinput 1.28.902
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2025-07-17 11:29:06 +10:00
Peter Hutterer
eed4f84508 test: add a typical PS/2 wheel mouse
Notably without a high-resoulution wheel to ensure we run the tests on
those devices too.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1275>
2025-07-17 00:36:52 +00:00
Peter Hutterer
18b34d0bc0 test: split some tablet tests into new groups
Our CI pipeline fails 9 times out of 10 on the valgrind tests. The tests
seem to finish in either 35 min or exceed the 60 min timeout limit, with
nothing in between. To avoid this let's split into more groups so we can
a) run those more in parallel and b) are less likely to hit the
timeout when run slowly.

Analysis of recent logs shows the eraser button tests to be the worst
offender, taking 752s (due to the combinatorial explosion) alone. The
various tip and proximity tests together also take some time so let's
group those out.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1274>
2025-07-17 00:16:02 +00:00
Peter Hutterer
c24ad64d18 meson.build: drop the timeouts to fit into the CI's 60 min limit
The vm tests had a 20 min timeout and a multiplier of 100. Our CI will
kill us (without logs) after 60 minutes.

Let's drop this to ~18min and a multiplier of 3 which gives us a few
minutes for setup before the CI terminates us. Ideally this means
we get some meson logs on timeout failures.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1274>
2025-07-17 00:16:02 +00:00
Peter Hutterer
7140f13d82 evdev: track KEY_SYSRQ frames and pass them even as repeat frames
Alt+Printscreen aka KEY_LEFTALT + KEY_SYSRQ is emulated by the kernel
and always posted with SYN_REPORT 1, see
drivers/tty/sysrq.c:sysrq_reinject_alt_sysrq()

The actual sequence when pressing Alt + Printscreen is to release Alt
first, then press it again:
- evdev:
    - [ 10, 674010,   1,  56,       0] # EV_KEY / KEY_LEFTALT               0
    - [ 10, 674010,   0,   0,       0] # ------------ SYN_REPORT (0) ---------- +2861ms
  - evdev:
    - [ 10, 674030,   1,  56,       1] # EV_KEY / KEY_LEFTALT               1
    - [ 10, 674030,   1,  99,       1] # EV_KEY / KEY_SYSRQ                 1
    - [ 10, 674030,   0,   0,       1] # ------------ SYN_REPORT (1) ---------- +0ms
  - evdev:
    - [ 10, 674031,   1,  99,       0] # EV_KEY / KEY_SYSRQ                 0
    - [ 10, 674031,   1,  56,       0] # EV_KEY / KEY_LEFTALT               0
    - [ 10, 674031,   0,   0,       1] # ------------ SYN_REPORT (1) ---------- +0ms

Handle that special case so we get our printscreen key to work as
expected anymore.

Fixes: 9a9466b6a9 ("evdev: discard any frame with EV_SYN SYN_REPORT 1")
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1273>
2025-07-16 09:36:10 +10:00
Peter Hutterer
ccc798808a ci: don't paper over pre-commit failures
The ruff-format check only checks, it doesn't modify the files so our
CI doesn't catch formatting errors.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1272>
2025-07-15 16:26:59 +10:00
Peter Hutterer
79ea713aa3 tools: fix ruff format for libinput-replay
Fixes: 89c2f29d2c ("tools/libinput-replay: Warn if writing to local-overrides fails")
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1272>
2025-07-15 16:26:59 +10:00
Peter Hutterer
7e98f2f2d2 pre-commit: update the name for the ruff check
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1272>
2025-07-15 16:24:27 +10:00
Peter Hutterer
778caa41af Add evdev_usage_name as helper for debugging
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1270>
2025-07-15 01:14:37 +00:00
Peter Hutterer
f98db521ee plugin: remove a leftover function
This one is no longer called, so no need to keep it around.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1269>
2025-07-15 10:51:42 +10:00
Peter Hutterer
0b07e77c3e plugin: remove a confusing FIXME
This is a leftover from the Lua plugin branch where the question of
whether to have public plugins before or after internal plugins is a
valid one. We don't have public plugins here, so let's remove this
fixme.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1269>
2025-07-15 10:51:42 +10:00
Peter Hutterer
c7ec39f51e test: tighten a helper function
Make sure we can pass only the right axis in

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1268>
2025-07-14 11:14:15 +10:00
Peter Hutterer
09acce4ce8 test: make the wheel tests stricter and always expect hires events
This would've caught #1156

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1268>
2025-07-14 11:14:15 +10:00
Peter Hutterer
c75d158bc0 test: swap a condition for easier readability
We have an else, so let's not do an `if not foo` condition.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1268>
2025-07-14 11:14:15 +10:00
Peter Hutterer
8fcacca7da util: print legacy axis events for debugging
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1268>
2025-07-14 11:14:15 +10:00
Peter Hutterer
1dec6f98b0 plugins: add a plugin to emulate high-resolution wheel events
Fixes a regression causing missing scroll events on devices where the
kernel only sets REL_WHEEL/REL_HWHEEL but not the corresponding
hires events. On those devices we would get the legacy axis events but
no longer the new ones.

The mouse wheel plugin will correctly emulate missing hires events
but it doesn't attach to devices where the hires bit is never set.

This plugin can be very simple - since we know we enabled the code on
this device we don't need to keep any extra state around. If our frame
handler is called for this device we want to add the hi-res events.

Theoretically this breaks if the device has only one hi-res axis enabled
but not the other one (i.e. REL_WHEEL_HI_RES but only REL_HWHEEL) but
that's too theoretical to worry about.

Closes #1156

Fixes: 31854a829a ("plugin: only register the wheel plugin on devices that have a wheel")
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1268>
2025-07-14 11:14:15 +10:00
gudvinr
975ce6dbf5 plugin/wheel: reduce ACC_V120_THRESHOLD
For mice with multipliers 30/120 and 40/120 nothing would change
as both will still cross threshold only after 2nd event.

But for MX Master 3 (and maybe others) that makes scroll beginning
a bit responsive, without jumping straight to 64/120 or 72/120.

Now events being emitted at 16+16+16 or 24+24 without significant
side-effects ("twitching" when resting finger on the wheel,
sudden scroll events when pressing middle button, etc). See !1262 for
some background.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1267>
2025-07-12 10:28:17 +03:00
158 changed files with 9864 additions and 1953 deletions

View file

@ -74,6 +74,7 @@ ForEachMacros:
- tp_for_each_touch
- range_for_each
- litest_log_group
- litest_with_logcapture
- litest_with_parameters
- litest_with_event_frame
- udev_list_entry_foreach

View file

@ -4,4 +4,4 @@
Checks: >
-clang-analyzer-unix.Malloc,
-clang-analyzer-optin.core.EnumCastOutOfRange
WarningsAsErrors: true
WarningsAsErrors: '*'

View file

@ -98,11 +98,11 @@ variables:
# See the documentation here: #
# https://wayland.freedesktop.org/libinput/doc/latest/building.html #
###############################################################################
FEDORA_PACKAGES: 'git-core gcc gcc-c++ pkgconf-pkg-config meson check-devel libudev-devel libevdev-devel doxygen graphviz python3-sphinx python3-recommonmark python3-sphinx_rtd_theme python3-pytest-xdist libwacom-devel cairo-devel gtk4-devel glib2-devel mtdev-devel diffutils wayland-protocols-devel black clang clang-tools-extra jq rpmdevtools valgrind systemd-udev qemu-img qemu-system-x86-core qemu-system-aarch64-core jq python3-click python3-rich virtme-ng'
DEBIAN_PACKAGES: 'git gcc g++ pkg-config meson check libudev-dev libevdev-dev doxygen graphviz python3-sphinx python3-recommonmark python3-sphinx-rtd-theme python3-pytest-xdist libwacom-dev libcairo2-dev libgtk-3-dev libglib2.0-dev libmtdev-dev curl'
UBUNTU_PACKAGES: 'git gcc g++ pkg-config meson check libudev-dev libevdev-dev doxygen graphviz python3-sphinx python3-recommonmark python3-sphinx-rtd-theme python3-pytest-xdist libwacom-dev libcairo2-dev libgtk-3-dev libglib2.0-dev libmtdev-dev'
ARCH_PACKAGES: 'git gcc pkgconfig meson check libsystemd libevdev python-pytest-xdist libwacom gtk4 mtdev diffutils'
ALPINE_PACKAGES: 'git gcc build-base pkgconfig meson check-dev eudev-dev libevdev-dev libwacom-dev cairo-dev gtk4.0-dev mtdev-dev bash'
FEDORA_PACKAGES: 'git-core gcc gcc-c++ pkgconf-pkg-config meson check-devel libudev-devel libevdev-devel doxygen graphviz python3-sphinx python3-recommonmark python3-sphinx_rtd_theme python3-pytest-xdist libwacom-devel cairo-devel gtk4-devel glib2-devel mtdev-devel diffutils wayland-protocols-devel black clang clang-tools-extra jq rpmdevtools valgrind systemd-udev qemu-img qemu-system-x86-core qemu-system-aarch64-core jq python3-click python3-rich virtme-ng lua-devel'
DEBIAN_PACKAGES: 'git gcc g++ pkg-config meson check libudev-dev libevdev-dev doxygen graphviz python3-sphinx python3-recommonmark python3-sphinx-rtd-theme python3-pytest-xdist libwacom-dev libcairo2-dev libgtk-3-dev libglib2.0-dev libmtdev-dev curl lua5.4-dev'
UBUNTU_PACKAGES: 'git gcc g++ pkg-config meson check libudev-dev libevdev-dev doxygen graphviz python3-sphinx python3-recommonmark python3-sphinx-rtd-theme python3-pytest-xdist libwacom-dev libcairo2-dev libgtk-3-dev libglib2.0-dev libmtdev-dev lua5.4-dev'
ARCH_PACKAGES: 'git gcc pkgconfig meson check libsystemd libevdev python-pytest-xdist libwacom gtk4 mtdev diffutils lua'
ALPINE_PACKAGES: 'git gcc build-base pkgconfig meson check-dev eudev-dev libevdev-dev libwacom-dev cairo-dev gtk4.0-dev mtdev-dev bash lua5.4-dev'
FREEBSD_PACKAGES: 'git pkgconf meson libepoll-shim libudev-devd libevdev libwacom gtk3 libmtdev bash wayland'
############################ end of package lists #############################
@ -110,12 +110,12 @@ variables:
# changing these will force rebuilding the associated image
# Note: these tags have no meaning and are not tied to a particular
# libinput version
FEDORA_TAG: '2025-05-19.0'
DEBIAN_TAG: '2025-05-19.0'
UBUNTU_TAG: '2025-05-19.0'
ARCH_TAG: '2025-05-19.0'
ALPINE_TAG: '2025-05-19.0'
FREEBSD_TAG: '2025-05-19.0'
FEDORA_TAG: '2025-11-17.0'
DEBIAN_TAG: '2025-11-17.0'
UBUNTU_TAG: '2025-11-17.0'
ARCH_TAG: '2025-11-17.0'
ALPINE_TAG: '2025-11-17.0'
FREEBSD_TAG: '2025-11-17.0'
FDO_UPSTREAM_REPO: libinput/libinput
@ -258,7 +258,7 @@ pre-commit-hooks:
- python3 -m venv venv
- source venv/bin/activate
- pip3 install pre-commit
- pre-commit run --all-files || true
- pre-commit run --all-files
- git diff --exit-code || (echo "ERROR - Code style errors found, please fix" && false)
#################################################################
@ -267,18 +267,6 @@ pre-commit-hooks:
# #
#################################################################
fedora:41@container-prep:
extends:
- .fdo.container-build@fedora
- .policy
- .fdo-runner-tags
stage: prep
variables:
GIT_STRATEGY: none
FDO_DISTRIBUTION_VERSION: '41'
FDO_DISTRIBUTION_PACKAGES: $FEDORA_PACKAGES
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
fedora:42@container-prep:
extends:
- .fdo.container-build@fedora
@ -291,6 +279,18 @@ fedora:42@container-prep:
FDO_DISTRIBUTION_PACKAGES: $FEDORA_PACKAGES
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
fedora:43@container-prep:
extends:
- .fdo.container-build@fedora
- .policy
- .fdo-runner-tags
stage: prep
variables:
GIT_STRATEGY: none
FDO_DISTRIBUTION_VERSION: '43'
FDO_DISTRIBUTION_PACKAGES: $FEDORA_PACKAGES
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
debian:stable@container-prep:
extends:
- .fdo.container-build@debian
@ -303,7 +303,7 @@ debian:stable@container-prep:
FDO_DISTRIBUTION_PACKAGES: $DEBIAN_PACKAGES
FDO_DISTRIBUTION_TAG: $DEBIAN_TAG
ubuntu:25.04@container-prep:
ubuntu:25.10@container-prep:
extends:
- .fdo.container-build@ubuntu
- .policy
@ -311,7 +311,7 @@ ubuntu:25.04@container-prep:
stage: prep
variables:
GIT_STRATEGY: none
FDO_DISTRIBUTION_VERSION: '25.04'
FDO_DISTRIBUTION_VERSION: '25.10'
FDO_DISTRIBUTION_PACKAGES: $UBUNTU_PACKAGES
FDO_DISTRIBUTION_TAG: $UBUNTU_TAG
@ -386,16 +386,6 @@ freebsd:14.2@container-prep:
only:
- schedules
fedora:41@container-clean:
extends:
- .policy
- .container-clean
variables:
GIT_STRATEGY: none
CURRENT_CONTAINER_IMAGE: $CI_REGISTRY_IMAGE/fedora/$FDO_DISTRIBUTION_VERSION:$FDO_DISTRIBUTION_TAG
FDO_DISTRIBUTION_VERSION: '41'
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
fedora:42@container-clean:
extends:
- .policy
@ -406,6 +396,16 @@ fedora:42@container-clean:
FDO_DISTRIBUTION_VERSION: '42'
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
fedora:43@container-clean:
extends:
- .policy
- .container-clean
variables:
GIT_STRATEGY: none
CURRENT_CONTAINER_IMAGE: $CI_REGISTRY_IMAGE/fedora/$FDO_DISTRIBUTION_VERSION:$FDO_DISTRIBUTION_TAG
FDO_DISTRIBUTION_VERSION: '43'
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
debian:stable@container-clean:
extends:
- .policy
@ -416,14 +416,14 @@ debian:stable@container-clean:
FDO_DISTRIBUTION_VERSION: 'stable'
FDO_DISTRIBUTION_TAG: $DEBIAN_TAG
ubuntu:25.04@container-clean:
ubuntu:25.10@container-clean:
extends:
- .policy
- .container-clean
variables:
GIT_STRATEGY: none
CURRENT_CONTAINER_IMAGE: $CI_REGISTRY_IMAGE/ubuntu/$FDO_DISTRIBUTION_VERSION:$FDO_DISTRIBUTION_TAG
FDO_DISTRIBUTION_VERSION: '25.04'
FDO_DISTRIBUTION_VERSION: '25.10'
FDO_DISTRIBUTION_TAG: $UBUNTU_TAG
arch:rolling@container-clean:
@ -577,20 +577,20 @@ freebsd:14.2@container-clean:
- export MESON_TEST_ARGS="$MESON_TEST_ARGS $SUITES"
.fedora:42@test-suite-vm:
.fedora:43@test-suite-vm:
extends:
- .fdo.distribution-image@fedora
- .test-suite-vm
variables:
FDO_DISTRIBUTION_VERSION: 42
FDO_DISTRIBUTION_VERSION: 43
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
needs:
- "fedora:42@container-prep"
- "fedora:43@container-prep"
vm-touchpad:
extends:
- .fedora:42@test-suite-vm
- .fedora:43@test-suite-vm
variables:
SUITE_NAMES: 'touchpad'
@ -603,7 +603,7 @@ vm-touchpad-no-libwacom:
vm-touchpad_palm:
extends:
- .fedora:42@test-suite-vm
- .fedora:43@test-suite-vm
variables:
SUITE_NAMES: 'touchpad_palm'
@ -616,7 +616,7 @@ vm-touchpad_palm-no-libwacom:
vm-touchpad_dwt:
extends:
- .fedora:42@test-suite-vm
- .fedora:43@test-suite-vm
variables:
SUITE_NAMES: 'touchpad_dwt'
@ -629,7 +629,7 @@ vm-touchpad_dwt-no-libwacom:
vm-tap:
extends:
- .fedora:42@test-suite-vm
- .fedora:43@test-suite-vm
variables:
SUITE_NAMES: 'touchpad_tap'
@ -642,7 +642,7 @@ vm-tap-no-libwacom:
vm-tap-drag:
extends:
- .fedora:42@test-suite-vm
- .fedora:43@test-suite-vm
variables:
SUITE_NAMES: 'touchpad_tap_drag'
@ -655,7 +655,7 @@ vm-tap-drag-no-libwacom:
vm-tap-palm:
extends:
- .fedora:42@test-suite-vm
- .fedora:43@test-suite-vm
variables:
SUITE_NAMES: 'touchpad_tap_palm'
@ -668,7 +668,7 @@ vm-tap-palm-no-libwacom:
vm-touchpad-buttons:
extends:
- .fedora:42@test-suite-vm
- .fedora:43@test-suite-vm
variables:
SUITE_NAMES: 'touchpad_buttons'
@ -681,7 +681,7 @@ vm-touchpad-buttons-no-libwacom:
vm-tablet:
extends:
- .fedora:42@test-suite-vm
- .fedora:43@test-suite-vm
variables:
SUITE_NAMES: 'tablet'
@ -694,7 +694,7 @@ vm-tablet-no-libwacom:
vm-tablet_left_handed:
extends:
- .fedora:42@test-suite-vm
- .fedora:43@test-suite-vm
variables:
SUITE_NAMES: 'tablet_left_handed'
@ -705,9 +705,35 @@ vm-tablet_left_handed-no-libwacom:
variables:
MESON_ARGS: '-Dlibwacom=false'
vm-tablet_proximity_tip:
extends:
- .fedora:43@test-suite-vm
variables:
SUITE_NAMES: 'tablet_proximity tablet_tip'
vm-tablet_proximity_tip-no-libwacom:
extends:
- vm-tablet_proximity_tip
stage: test-suite-no-libwacom
variables:
MESON_ARGS: '-Dlibwacom=false'
vm-tablet_eraser:
extends:
- .fedora:43@test-suite-vm
variables:
SUITE_NAMES: 'tablet_eraser'
vm-tablet_eraser-no-libwacom:
extends:
- vm-tablet_eraser
stage: test-suite-no-libwacom
variables:
MESON_ARGS: '-Dlibwacom=false'
vm-gestures:
extends:
- .fedora:42@test-suite-vm
- .fedora:43@test-suite-vm
variables:
SUITE_NAMES: 'gestures'
@ -720,7 +746,7 @@ vm-gestures-no-libwacom:
vm-backends:
extends:
- .fedora:42@test-suite-vm
- .fedora:43@test-suite-vm
variables:
SUITE_NAMES: 'path udev'
@ -733,7 +759,7 @@ vm-backends-no-libwacom:
vm-misc:
extends:
- .fedora:42@test-suite-vm
- .fedora:43@test-suite-vm
variables:
SUITE_NAMES: 'log misc quirks device'
@ -746,7 +772,7 @@ vm-misc-no-libwacom:
vm-other devices:
extends:
- .fedora:42@test-suite-vm
- .fedora:43@test-suite-vm
variables:
SUITE_NAMES: 'keyboard pad switch trackball trackpoint totem touch'
@ -759,7 +785,7 @@ vm-other devices-no-libwacom:
vm-pointer:
extends:
- .fedora:42@test-suite-vm
- .fedora:43@test-suite-vm
variables:
SUITE_NAMES: 'pointer'
@ -770,6 +796,19 @@ vm-pointer-no-libwacom:
variables:
MESON_ARGS: '-Dlibwacom=false'
vm-lua:
extends:
- .fedora:43@test-suite-vm
variables:
SUITE_NAMES: 'lua'
vm-lua-no-libwacom:
extends:
- vm-lua
stage: test-suite-no-libwacom
variables:
MESON_ARGS: '-Dlibwacom=false'
vm-valgrind-touchpad:
stage: valgrind
@ -888,6 +927,32 @@ vm-valgrind-tablet_left_handed:
rules:
- if: $GITLAB_USER_LOGIN != "marge-bot"
vm-valgrind-tablet_proximity_tip:
stage: valgrind
extends:
- vm-tablet_proximity_tip
- .policy-retry-on-failure
variables:
MESON_TEST_ARGS: '--setup=valgrind'
LITEST_JOBS: 0
retry:
max: 2
rules:
- if: $GITLAB_USER_LOGIN != "marge-bot"
vm-valgrind-tablet_eraser:
stage: valgrind
extends:
- vm-tablet_eraser
- .policy-retry-on-failure
variables:
MESON_TEST_ARGS: '--setup=valgrind'
LITEST_JOBS: 0
retry:
max: 2
rules:
- if: $GITLAB_USER_LOGIN != "marge-bot"
vm-valgrind-gestures:
stage: valgrind
extends:
@ -953,18 +1018,32 @@ vm-valgrind-pointer:
rules:
- if: $GITLAB_USER_LOGIN != "marge-bot"
vm-valgrind-lua:
stage: valgrind
extends:
- vm-lua
- .policy-retry-on-failure
variables:
MESON_TEST_ARGS: '--setup=valgrind'
LITEST_JOBS: 0
retry:
max: 2
rules:
- if: $GITLAB_USER_LOGIN != "marge-bot"
.fedora-build@template:
extends:
- .fdo.distribution-image@fedora
- .build@template
variables:
FDO_DISTRIBUTION_VERSION: '42'
FDO_DISTRIBUTION_VERSION: '43'
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
needs:
- "fedora:42@container-prep"
- "fedora:43@container-prep"
default-build-release@fedora:42:
default-build-release@fedora:43:
stage: distro
extends:
- .fedora-build@template
@ -972,7 +1051,7 @@ default-build-release@fedora:42:
MESON_ARGS: "-Dbuildtype=release"
CFLAGS: "-Werror"
clang-tidy@fedora:42:
clang-tidy@fedora:43:
extends:
- .fedora-build@template
variables:
@ -987,13 +1066,13 @@ clang-tidy@fedora:42:
# run them on one image, they shouldn't fail on one distro
# when they succeed on another.
build-no-libwacom@fedora:42:
build-no-libwacom@fedora:43:
extends:
- .fedora-build@template
variables:
MESON_ARGS: "-Dlibwacom=false"
build-no-libwacom-nodeps@fedora:42:
build-no-libwacom-nodeps@fedora:43:
extends:
- .fedora-build@template
variables:
@ -1001,13 +1080,13 @@ build-no-libwacom-nodeps@fedora:42:
before_script:
- dnf remove -y libwacom libwacom-devel
build-no-mtdev@fedora:42:
build-no-mtdev@fedora:43:
extends:
- .fedora-build@template
variables:
MESON_ARGS: "-Dmtdev=false"
build-no-mtdev-nodeps@fedora:42:
build-no-mtdev-nodeps@fedora:43:
extends:
- .fedora-build@template
variables:
@ -1015,13 +1094,27 @@ build-no-mtdev-nodeps@fedora:42:
before_script:
- dnf remove -y mtdev mtdev-devel
build-docs@fedora:42:
build-no-lua@fedora:43:
extends:
- .fedora-build@template
variables:
MESON_ARGS: "-Dlua-plugins=disabled"
build-no-lua-nodeps@fedora:43:
extends:
- .fedora-build@template
variables:
MESON_ARGS: "-Dlua-plugins=disabled"
before_script:
- dnf remove -y lua lua-devel
build-docs@fedora:43:
extends:
- .fedora-build@template
variables:
MESON_ARGS: "-Ddocumentation=true"
build-no-docs-nodeps@fedora:42:
build-no-docs-nodeps@fedora:43:
extends:
- .fedora-build@template
variables:
@ -1029,13 +1122,13 @@ build-no-docs-nodeps@fedora:42:
before_script:
- dnf remove -y doxygen graphviz
build-no-debuggui@fedora:42:
build-no-debuggui@fedora:43:
extends:
- .fedora-build@template
variables:
MESON_ARGS: "-Ddebug-gui=false"
build-no-debuggui-nodeps@fedora:42:
build-no-debuggui-nodeps@fedora:43:
extends:
- .fedora-build@template
variables:
@ -1043,13 +1136,13 @@ build-no-debuggui-nodeps@fedora:42:
before_script:
- dnf remove -y gtk3-devel gtk4-devel
build-no-tests@fedora:42:
build-no-tests@fedora:43:
extends:
- .fedora-build@template
variables:
MESON_ARGS: "-Dtests=false"
build-no-tests-nodeps@fedora:42:
build-no-tests-nodeps@fedora:43:
extends:
- .fedora-build@template
variables:
@ -1057,7 +1150,7 @@ build-no-tests-nodeps@fedora:42:
before_script:
- dnf remove -y check-devel
valgrind@fedora:42:
valgrind@fedora:43:
extends:
- .fedora-build@template
variables:
@ -1065,7 +1158,7 @@ valgrind@fedora:42:
# Python checks, only run on Fedora
usr-bin-env-python@fedora:42:
usr-bin-env-python@fedora:43:
extends:
- .fedora-build@template
script:
@ -1093,6 +1186,9 @@ check-test-suites:
libinput-test-suite-touchpad_buttons
libinput-test-suite-tablet
libinput-test-suite-tablet_left_handed
libinput-test-suite-tablet_proximity
libinput-test-suite-tablet_tip
libinput-test-suite-tablet_eraser
libinput-test-suite-gestures
libinput-test-suite-path
libinput-test-suite-udev
@ -1108,6 +1204,7 @@ check-test-suites:
libinput-test-suite-totem
libinput-test-suite-touch
libinput-test-suite-pointer
libinput-test-suite-lua
EOF
- sort -o ci-testsuites ci-testsuites
- diff -u8 -w ci-testsuites meson-testsuites || (echo "Some test suites are not run in the CI" && false)
@ -1172,18 +1269,6 @@ coverity:
# #
#################################################################
fedora:41@default-build:
stage: distro
extends:
- .build@template
- .fdo.distribution-image@fedora
variables:
FDO_DISTRIBUTION_VERSION: '41'
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
needs:
- "fedora:41@container-prep"
fedora:42@default-build:
stage: distro
extends:
@ -1196,6 +1281,18 @@ fedora:42@default-build:
- "fedora:42@container-prep"
fedora:43@default-build:
stage: distro
extends:
- .build@template
- .fdo.distribution-image@fedora
variables:
FDO_DISTRIBUTION_VERSION: '43'
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
needs:
- "fedora:43@container-prep"
debian:stable@default-build:
stage: distro
extends:
@ -1208,16 +1305,16 @@ debian:stable@default-build:
- "debian:stable@container-prep"
ubuntu:25.04@default-build:
ubuntu:25.10@default-build:
stage: distro
extends:
- .build@template
- .fdo.distribution-image@ubuntu
variables:
FDO_DISTRIBUTION_VERSION: '25.04'
FDO_DISTRIBUTION_VERSION: '25.10'
FDO_DISTRIBUTION_TAG: $UBUNTU_TAG
needs:
- "ubuntu:25.04@container-prep"
- "ubuntu:25.10@container-prep"
arch:rolling@default-build:
@ -1267,27 +1364,6 @@ freebsd:14.2@default-build:
# #
#################################################################
#
# Verify that the merge request has the allow-collaboration checkbox ticked
#
check-merge-request:
extends:
- .fdo.ci-fairy
- .policy
- .fdo-runner-tags
stage: deploy
script:
- ci-fairy check-merge-request --require-allow-collaboration --junit-xml=results.xml
artifacts:
when: on_failure
reports:
junit: results.xml
allow_failure: true
rules:
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
build rpm:
extends:
- .fdo.distribution-image@fedora
@ -1295,10 +1371,10 @@ build rpm:
- .fdo-runner-tags
stage: deploy
variables:
FDO_DISTRIBUTION_VERSION: '42'
FDO_DISTRIBUTION_VERSION: '43'
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
needs:
- "fedora:42@container-prep"
- "fedora:43@container-prep"
script:
- meson "$MESON_BUILDDIR"
- VERSION=$(meson introspect "$MESON_BUILDDIR" --projectinfo | jq -r .version)
@ -1316,14 +1392,12 @@ build rpm:
wayland-web:
stage: deploy
trigger: wayland/wayland.freedesktop.org
except:
refs:
- schedules
variables:
MESON_ARGS: '-Ddocumentation=true -Ddebug-gui=false -Dlibwacom=false -Dtests=false'
MESON_BUILDDIR: 'builddir'
only:
refs:
- main
variables:
- $CI_PROJECT_PATH == "libinput/libinput"
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
when: never
- if: '$CI_COMMIT_BRANCH == "main" && $GITLAB_USER_LOGIN != "marge-bot" && $CI_PROJECT_PATH == $FDO_UPSTREAM_REPO'
when: on_success
- when: never

View file

@ -246,7 +246,7 @@ pre-commit-hooks:
- python3 -m venv venv
- source venv/bin/activate
- pip3 install pre-commit
- pre-commit run --all-files || true
- pre-commit run --all-files
- git diff --exit-code || (echo "ERROR - Code style errors found, please fix" && false)
#################################################################
@ -492,6 +492,7 @@ vm-valgrind-{{suite.name}}:
- if: $GITLAB_USER_LOGIN != "marge-bot"
{% endfor %}
{% endfor %}{# for if distro.use_for_qemu_tests #}
{% for distro in distributions if distro.use_for_custom_build_tests %}
@ -557,6 +558,20 @@ build-no-mtdev-nodeps@{{distro.name}}:{{version}}:
before_script:
- dnf remove -y mtdev mtdev-devel
build-no-lua@{{distro.name}}:{{version}}:
extends:
- .{{distro.name}}-build@template
variables:
MESON_ARGS: "-Dlua-plugins=disabled"
build-no-lua-nodeps@{{distro.name}}:{{version}}:
extends:
- .{{distro.name}}-build@template
variables:
MESON_ARGS: "-Dlua-plugins=disabled"
before_script:
- dnf remove -y lua lua-devel
build-docs@{{distro.name}}:{{version}}:
extends:
- .{{distro.name}}-build@template
@ -746,27 +761,6 @@ coverity:
# #
#################################################################
#
# Verify that the merge request has the allow-collaboration checkbox ticked
#
check-merge-request:
extends:
- .fdo.ci-fairy
- .policy
- .fdo-runner-tags
stage: deploy
script:
- ci-fairy check-merge-request --require-allow-collaboration --junit-xml=results.xml
artifacts:
when: on_failure
reports:
junit: results.xml
allow_failure: true
rules:
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
{% for distro in distributions if distro.name == "fedora" %}
{% set version = "{}".format(distro.versions|last()) %}
build rpm:
@ -798,14 +792,12 @@ build rpm:
wayland-web:
stage: deploy
trigger: wayland/wayland.freedesktop.org
except:
refs:
- schedules
variables:
MESON_ARGS: '-Ddocumentation=true -Ddebug-gui=false -Dlibwacom=false -Dtests=false'
MESON_BUILDDIR: 'builddir'
only:
refs:
- main
variables:
- $CI_PROJECT_PATH == "libinput/libinput"
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
when: never
- if: '$CI_COMMIT_BRANCH == "main" && $GITLAB_USER_LOGIN != "marge-bot" && $CI_PROJECT_PATH == $FDO_UPSTREAM_REPO'
when: on_success
- when: never

View file

@ -3,14 +3,14 @@
#
# We're happy to rebuild all containers when one changes.
.default_tag: &default_tag '2025-05-19.0'
.default_tag: &default_tag '2025-11-17.0'
distributions:
- name: fedora
tag: *default_tag
versions:
- '41'
- '42'
- '43'
use_for_custom_build_tests: true
use_for_qemu_tests: true
packages:
@ -50,6 +50,7 @@ distributions:
- python3-click
- python3-rich
- virtme-ng
- lua-devel
- name: debian
tag: *default_tag
versions:
@ -75,10 +76,11 @@ distributions:
- libglib2.0-dev
- libmtdev-dev
- curl # for the coverity job
- lua5.4-dev
- name: ubuntu
tag: *default_tag
versions:
- '25.04'
- '25.10'
packages:
- git
- gcc
@ -99,6 +101,7 @@ distributions:
- libgtk-3-dev
- libglib2.0-dev
- libmtdev-dev
- lua5.4-dev
- name: arch
tag: *default_tag
versions:
@ -116,6 +119,7 @@ distributions:
- gtk4
- mtdev
- diffutils
- lua
build:
extra_variables:
- "MESON_ARGS: '-Ddocumentation=false'" # python-recommonmark is no longer in the repos
@ -136,6 +140,7 @@ distributions:
- gtk4.0-dev
- mtdev-dev
- bash
- lua5.4-dev
build:
extra_variables:
- "MESON_ARGS: '-Ddocumentation=false' # alpine does not have python-recommonmark"
@ -195,6 +200,13 @@ test_suites:
- name: tablet_left_handed
suites:
- tablet_left_handed
- name: tablet_proximity_tip
suites:
- tablet_proximity
- tablet_tip
- name: tablet_eraser
suites:
- tablet_eraser
- name: gestures
suites:
- gestures
@ -220,6 +232,9 @@ test_suites:
- name: pointer
suites:
- pointer
- name: lua
suites:
- lua
vng:
kernel: https://gitlab.freedesktop.org/api/v4/projects/libevdev%2Fhid-tools/packages/generic/kernel-x86_64/v6.14/bzImage

View file

@ -100,6 +100,7 @@ intended to be run by users.
%files utils
%{_libexecdir}/libinput/libinput-debug-gui
%{_libexecdir}/libinput/libinput-debug-tablet
%{_libexecdir}/libinput/libinput-debug-tablet-pad
%{_libexecdir}/libinput/libinput-list-kernel-devices
%{_libexecdir}/libinput/libinput-measure
%{_libexecdir}/libinput/libinput-measure-fuzz
@ -117,6 +118,7 @@ intended to be run by users.
%{_libexecdir}/libinput/libinput-analyze-touch-down-state
%{_mandir}/man1/libinput-debug-gui.1*
%{_mandir}/man1/libinput-debug-tablet.1*
%{_mandir}/man1/libinput-debug-tablet-pad.1*
%{_mandir}/man1/libinput-list-kernel-devices.1*
%{_mandir}/man1/libinput-measure.1*
%{_mandir}/man1/libinput-measure-fuzz.1*

View file

@ -9,7 +9,7 @@ repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.2
hooks:
- id: ruff
- id: ruff-check
args: ['--ignore=E741,E501', '--extend-exclude=subprojects', '.']
- id: ruff-format
args: ['--check', '--diff']

View file

@ -183,6 +183,16 @@ resource_rules:
For a detailed explanation on the how and why of this process please see
the [Closed Issues wiki page](https://gitlab.freedesktop.org/libinput/libinput/-/wikis/Closed-Issues).
status: "close"
- name: "Re-close bug for reopening"
conditions:
labels:
- "bugbot::re-close"
actions:
remove_labels:
- "bugbot::re-close"
comment: |
I'm temporarily closing this bug again. This is not a final close, see my comments above for the open/close process.
status: "close"
- *udev_hid_bpf
- *libinput_record
- *hid_recorder

View file

@ -93,6 +93,9 @@ __all_seats()
+ '(natural-scrolling)' \
'--enable-natural-scrolling[Enable natural scrolling]' \
'--disable-natural-scrolling[Disable natural scrolling]' \
+ '(plugins)' \
'--enable-plugins[Enable plugins]' \
'--disable-plugins[Disable plugins]' \
+ '(tap-to-click)' \
'--enable-tap[Enable tap-to-click]' \
'--disable-tap[Disable tap-to-click]'

View file

@ -40,11 +40,6 @@ To fix the touchpad you need to:
Detailed explanations are below.
.. note:: ``libinput measure touchpad-size`` was introduced in libinput
1.16. For earlier versions, use `libevdev <http://freedesktop.org/wiki/Software/libevdev/>`_'s
``touchpad-edge-detector`` tool.
The ``libinput measure touchpad-size`` tool is an interactive tool. It must
be called with the physical dimensions of the touchpad in mm. In the example
below, we use 100mm wide and 55mm high. The tool will find the touchpad device

View file

@ -6,15 +6,21 @@ Configuration options
Below is a list of configurable options exposed to the users.
.. contents::
:depth: 1
:local:
.. hint:: Not all configuration options are available on all devices. Use
:ref:`libinput list-devices <libinput-list-devices>` to show the
configuration options for local devices.
libinput's configuration interface is available to the caller only, not
directly to the user. Thus is is the responsibility of the caller to expose
directly to the user. Thus it is the responsibility of the caller to expose
the various options and how these options are exposed. For example, the
xf86-input-libinput driver exposes the options through X Input device
properties and xorg.conf.d options. See the `libinput(4)
`xf86-input-libinput driver <https://gitlab.freedesktop.org/xorg/driver/xf86-input-libinput/>`_
exposes the options through X Input device properties and `xorg.conf.d
<https://linux.die.net/man/5/xorg.conf.d>`_ options. See the `libinput(4)
<https://www.mankier.com/4/libinput>`_ man page for more details.
@ -205,6 +211,18 @@ before the maximum hardware-supported pressure is reached.
See :ref:`tablet-pressure-range` for more info.
.. _config-tablet-eraser-buttons:
------------------------------------------------------------------------------
Tablet tool eraser buttons
------------------------------------------------------------------------------
On many contemporary :ref:`Tablet tools <tablet-tools>` one button is hardcoded
in firmware to emulate an eraser. This button can be remapped to provide
a normal stylus button instead.
See :ref:`tablet-eraser-button` for more info.
------------------------------------------------------------------------------
Area configuration
------------------------------------------------------------------------------

View file

@ -181,7 +181,7 @@ sufficient to make some of the more confusing steps obvious.
with your username). git will call this repository ``gitlab``. ::
cd /path/to/libinput.git
git remote add gitlab git@gitlab.freedesktop.org:USERNAME/libinput.git
git remote add gitlab git@ssh.gitlab.freedesktop.org:USERNAME/libinput.git
git fetch gitlab
- Create a new branch and commit your changes to that branch. ::

View file

@ -148,6 +148,9 @@ ModelBouncingKeys
ModelSynapticsSerialTouchpad
Reserved for touchpads made by Synaptics on the serial bus
ModelPressurePad
.. warning:: This quirk is no longer in use. Use
``AttrInputProp=+INPUT_PROP_PRESSUREPAD`` instead.
Unlike in traditional touchpads, whose pressure value equals contact size,
on pressure pads pressure is a real physical axis.
Indicates that the device is a pressure pad.
@ -155,6 +158,10 @@ ModelTouchpadPhantomClicks
Some laptops are prone to registering touchpad clicks when the case is
bent. Indicates that clicks should be ignored if no fingers are on the
touchpad.
ModelScrollOnMiddleClick
Some mice can generate unwanted high-resolution scroll events when the wheel
is pressed. Increases the scroll threshold required to start scrolling to
avoid accidentally scrolling when middle clicking.
AttrSizeHint=NxM, AttrResolutionHint=N
Hints at the width x height of the device in mm, or the resolution
of the x/y axis in units/mm. These may only be used where they apply to
@ -195,6 +202,9 @@ AttrInputProp=+INPUT_PROP_BUTTONPAD;-INPUT_PROP_POINTER;
Enables or disables the evdev input property on the device. The prefix
for each entry is either '+' (enable) or '-' (disable). Entries may be
a named input property or the hexadecimal value of that property.
The most common use of this is ``AttrInputProp=+INPUT_PROP_PRESSUREPAD``
which marks a touchpad as a :ref:`forcepad or pressurepad <touchpads_buttons_forcepads>`.
AttrPointingStickIntegration=internal|external
Indicates the integration of the pointing stick. This is a string enum.
Only needed for external pointing sticks. These are rare.

View file

@ -0,0 +1,36 @@
digraph stack
{
compound=true;
splines=line;
rankdir="LR";
node [
shape="box";
]
subgraph cluster_2 {
label="Kernel";
event0 [label="/dev/input/event0"];
event1 [label="/dev/input/event1"];
}
subgraph cluster_0 {
label="Compositor process";
libinput [label="libinput context 1"];
}
subgraph cluster_1 {
label="libinput debug-events";
libinput2 [label="libinput context 2"];
}
stdout;
client [label="Wayland client"];
event0:e -> libinput:w;
event1:e -> libinput:w;
event0:e -> libinput2:w;
event1:e -> libinput2:w;
libinput -> client [ltail=cluster_0 label="Wayland protocol"];
libinput2 -> stdout [ltail=cluster_1];
}

View file

@ -0,0 +1,31 @@
digraph stack
{
compound=true;
rankdir="LR";
node [
shape="box";
]
subgraph cluster_2 {
label="Kernel";
event0 [label="/dev/input/event0"]
}
subgraph cluster_1 {
label="libinput";
subgraph cluster_0 {
label="Plugin pipeline";
p1 [label="00-foo.lua"];
p2 [label="10-bar.lua"];
}
libinput [label="libinput core"];
}
compositor [label="Compositor"];
event0 -> p1;
p1 -> p2;
p2 -> libinput;
libinput -> compositor [ltail=cluster_1 label="libinput API"];
}

View file

@ -348,10 +348,14 @@ compositor. This indicates a misunderstanding of how libinput works:
libinput is a library that converts kernel events into libinput events, much
like ``sed`` reads data in, modifies it, and provides it to stdout.
If ``sed`` is used by a shell-script, that script has full control over how
``sed`` processes data. In this analogy, ``sed`` is libinput and the
shell script is the compositor. It is not possible to write a program
to modify the behavior of the ``sed`` instance used inside that shell script
.. graphviz:: libinput-contexts.gv
A libinput context is private to the process and cannot be modified from the
outside. To use the ``sed`` analogy again: if ``sed`` is used by a
shell-script, that script has full control over how ``sed`` processes data. In
this analogy, ``sed`` is libinput and the shell script is the compositor. It is
not possible to write a program to modify the behavior of the ``sed`` instance
used inside that shell script.
Writing a program that uses libinput is akin to writing a new script that
invoke ``sed``. It will not have any effect on the original ``sed`` instance.
@ -360,6 +364,14 @@ The only way to modify libinput's behavior is to use the configuration options
exposed by the respective compositor. Those affect the libinput context inside
the compositor and thus have an effect on the input device behavior.
.. _faq_debug_events_not_showing_configuration:
------------------------------------------------------------------------------
Why doesn't libinput debug-events show my configuration
------------------------------------------------------------------------------
See :ref:`faq_separate_contexts`.
------------------------------------------------------------------------------
Can I configure scroll speed?
------------------------------------------------------------------------------

View file

@ -12,6 +12,7 @@
troubleshooting
contributing
development
lua-plugins
API documentation <@HTTP_DOC_LINK@/api/>

661
doc/user/lua-plugins.rst Normal file
View file

@ -0,0 +1,661 @@
.. _lua_plugins:
==============================================================================
Lua Plugins
==============================================================================
libinput provides a plugin system that allows users to modify the behavior
of devices. For example, a plugin may add or remove axes and/or buttons on a
device and/or modify the event stream seen by this device before it is passed
to libinput.
Plugins are implemented in `Lua <https://www.lua.org/>`_ (version 5.4)
and are typically loaded from the following paths:
- ``/etc/libinput/plugins/*.lua``, and
- ``/usr/lib{64}/libinput/plugins/*.lua``
Plugins are loaded in alphabetical order and where
multiple plugins share the same file name, the one in the highest precedence
directory is used. Plugins in ``/etc`` take precedence over
plugins in ``/usr``.
.. note:: Plugins lookup paths and their order are decided by the compositor.
Some compositors may support more/fewer/other lookup paths than the
above defaults.
Plugins are run sequentially in ascending sort-order (i.e. ``00-foo.lua`` runs
before ``10-bar.lua``) and each plugin sees the state left by any previous
plugins. For example if ``00-foo.lua`` changes all left button events to right
button events, ``10-bar.lua`` only ever sees right button events.
See the `Lua Reference manual <https://www.lua.org/manual/5.4/manual.html>`_ for
details on the Lua language.
.. note:: Plugins are **not** loaded by default, it is up to the compositor
whether to allow plugins. An explicit call to
``libinput_plugin_system_load_plugins()`` is required.
------------------------------------------------------------------------------
Limitations
------------------------------------------------------------------------------
Each script runs in its own sandbox and cannot communicate or share state with
other scripts.
Tables that hold API methods are not writable, i.e. it is not possible
to overwrite the default functionality of those APIs.
The Lua API available to plugins is limited to the following calls::
assert error ipairs next pairs tonumber
pcall select print tostring type xpcall
table string math _VERSION
It is not possible to e.g. use the ``io`` module from a script.
To use methods on instantiated objects, the ``object:method`` method call
syntax must be used. For example:
.. code-block:: lua
libinput:register()
libinput.register() -- this will fail
------------------------------------------------------------------------------
When to use plugins
------------------------------------------------------------------------------
libinput plugins are a relatively niche use-case that typically need to
address either once-off issues (e.g. those caused by worn-out hardware) or
user preferences that libinput does not and will not cater for.
Plugins should not be used for issues that can be fixed generically, for
example via :ref:`device-quirks`.
As a rule of thumb: a plugin should be a once-off that only works for one
user's hardware. If a plugin can be shared with many users then the plugin
implements functionality that should be integrated into libinput proper.
------------------------------------------------------------------------------
Testing plugins
------------------------------------------------------------------------------
Our :ref:`tools` support plugins if passed the ``--enable-plugins`` commandline
option. For implementing and testing plugins the easiest commands to test are
- ``libinput debug-events --enable-plugins`` (see :ref:`libinput-debug-events` docs)
- ``libinput debug-gui --enable-plugins`` (see :ref:`libinput-debug-gui` docs)
Where libinput is built and run from git, the tools will also look for plugins
in the meson build directory. See the ``plugins/meson.build`` file for details.
.. _plugins_api_lua:
--------------------------------------------------------------------------------
Lua Plugin API
--------------------------------------------------------------------------------
Lua plugins sit effectively below libinput and the API is not a
representation of the libinput API. Plugins modify the evdev event stream
received from the kernel.
.. graphviz:: plugin-stack.gv
The API revolves around two types: ``libinput`` and ``EvdevDevice``. The
``libinput`` type is used to register a plugin from a script, the
``EvdevDevice`` represents one device that is present in the system (but may
not have yet been added by libinput).
Typically a script does the following steps:
- register with libinput via ``libinput:register({versions})``
- connect to the ``"new-evdev-device"`` event
- receive an ``EvdevDevice`` object in the ``"new-evdev-device"`` callback
- check and/or modify the evdev event codes on the device
- connect to the device's ``"evdev-frame"`` event
- receive an :ref:`evdev frame <plugins_api_evdev_frame>` in the device's
``"evdev-frame"`` callback
- check and/or modify the events in that frame
Where multiple plugins are active, the evdev frame passed to the callback is
the combined frame as processed by all previous plugins in ascending sort order.
For example, if one plugin discards all button events subsequent plugins will
never see those button events in the frame.
.. _plugins_api_version_stability:
..............................................................................
Plugin version stability
..............................................................................
Plugin API version stability is provided on a best effort basis. We aim to provide
stable plugin versions for as long as feasible but may need to retire some older
versions over time. For this reason a plugin can select multiple versions it
implements, libinput will pick one supported version and adjust the plugin
behavior to match that version. See the ``libinput:register()`` call for details.
--------------------------------------------------------------------------------
Lua Plugin API Reference
--------------------------------------------------------------------------------
libinput provides the following globals and types:
.. _plugins_api_evdev_usage:
................................................................................
Evdev Usages
................................................................................
Evdev usages are a libinput-specific wrapper around the ``linux/input-event-codes.h``
evdev types and codes. They are used by libinput internally and are a 32-bit
combination of ``type << 16 | code``. Each usage carries the type and code and
is thus simpler to pass around and less prone to type confusion.
The :ref:`evdev global <plugins_api_evdev_global>` attempts to provide all
available usages but for the niche cases where it does not provide a named constant
the value can be crafted manually:
.. code-block:: lua
evdev_type = 0x3 -- EV_REL
evdev_code = 0x1 -- REL_Y
evdev_usage = (evdev_type << 16) | evdev_code
assert(usage == evdev.REL_Y)
.. _plugins_api_evdev_global:
................................................................................
The ``evdev`` global
................................................................................
The ``evdev`` global represents all known :ref:`plugins_api_evdev_usage`,
effectively in the form:
.. code-block:: lua
evdev = {
ABS_X = (3 << 16) | 0,
ABS_Y = (3 << 16) | 1,
...
REL_X = (2 << 16) | 0,
REL_Y = (2 << 16) | 1,
...
}
This global is provided for convenience to improve readability in the code.
Note that the name uses the event code name only (e.g. ``evdev.ABS_Y``) but the
value is an :ref:`Evdev Usage <plugins_api_evdev_usage>` (type and code).
See the ``linux/input-event-codes.h`` header file provided by your kernel
for a list of all evdev types and codes.
The evdev global also provides the bus type constants, e.g. ``evdev.BUS_USB``.
See the ``linux/input.h`` header file provided by your kernel
for a list of bus types.
.. _plugins_api_evdev_frame:
................................................................................
Evdev frames
................................................................................
Evdev frames represent a single frame of evdev events for a device. A frame
is a group of events that occurred at the same time. The frame usually only
contains state that has changed compared to the previous frame.
In our API a frame is exposed as a nested table with the following structure:
.. code-block:: lua
frame1 = {
{ usage = evdev.ABS_X, value = 123 },
{ usage = evdev.ABS_Y, value = 456 },
{ usage = evdev.BTN_LEFT, value = 1 },
}
frame2 = {
{ usage = evdev.ABS_Y, value = 457 },
}
frame3 = {
{ usage = evdev.ABS_X, value = 124 },
{ usage = evdev.BTN_LEFT, value = 0 },
}
.. note:: This API does not use ``SYN_REPORT`` events, it is implied at the
end of the table. Where a plugin writes a ``SYN_REPORT`` into the
list of events, that ``SYN_REPORT`` terminates the event frame
(similar to writing a ``\0`` into the middle of a C string).
A frame containing only a ``SYN_REPORT`` is functionally equivalent
to an empty frame.
Events or frames do not have a timestamp. Where a timestamp is required, that
timestamp is passed as additional argument to the function or return value.
See :ref:`plugins_api_evdev_global` for a list of known usages.
.. warning:: Evdev frames have an implementation-defined size limit of how many
events can be added to a single frame. This limit should never be
hit by valid plugins.
.. _plugins_api_libinputglobal:
................................................................................
The ``libinput`` global object
................................................................................
The core of our plugin API is the ``libinput`` global object. A script must
immediately ``register()`` to be active, otherwise it is unloaded immediately.
All libinput-specific APIs can be accessed through the ``libinput`` object.
.. function:: libinput:register({1, 2, ...})
Register this plugin with the given table of supported version numbers and
returns the version number selected by libinput for this plugin. See
:ref:`plugins_api_version_stability` for details.
.. code-block:: lua
-- this plugin can support versions 1, 4 and 5
version = libinput:register({1, 4, 5})
if version == 1 then
...
This function must be the first function called.
If the plugin calls any other functions before ``register()``, those functions
return the default zero value for the return type (``nil``, ``0``, an empty
table, etc.).
If the plugin does not call ``register()`` it will be removed immediately.
Once registered, any connected callbacks will be invoked whenever libinput
detects new devices, removes devices, etc.
This function must only be called once.
.. function:: libinput:unregister()
Unregister this plugin. This removes the plugin from libinput and releases
any resources associated with this plugin. This call must be the last call
in your plugin, it is effectively equivalent to Lua's
`os.exit() <https://www.lua.org/manual/5.4/manual.html#pdf-os.exit>`_.
.. function:: libinput:log_debug(message)
Log a message at the libinput debug log priority. See
``libinput:log_error()`` for details.
.. function:: libinput:log_info(message)
Log a message at the libinput info log priority. See
``libinput:log_error()`` for details.
.. function:: libinput:log_error(message)
Log a message at the libinput error log priority. Whether a message is
displayed in the log depends on libinput's log priority, set by the caller.
A compositor may disable stdout and stderr. Log messages should be preferred
over Lua's ``print()`` function to ensure the messages end up in the same
location as other libinput log messages and are not discarded.
.. function:: libinput:now()
Returns the current time in microseconds in ``CLOCK_MONOTONIC``. This is
the timestamp libinput uses internally. This timestamp cannot be mapped
to any particular time of day, see the
`clock_gettime() man page <https://man7.org/linux/man-pages/man3/clock_gettime.3.html>`_
for details.
.. function:: libinput:version()
Returns the agreed-on version of the plugin, see ``libinput:register()``.
If called before ``libinput:register()`` this function returns ``0``.
.. function:: libinput:connect(name, function)
Set the callback to the given event name. Only one callback
may be set for an event name at any time, subsequent callbacks
will replace any earlier callbacks for the same name.
Version 1 of the plugin API supports the following events and callback arguments:
- ``"new-evdev-device"``: A new :ref:`EvdevDevice <plugins_api_evdevdevice>`
has been seen by libinput but not yet added.
.. code-block:: lua
libinput:connect("new-evdev-device", function (device) ... end)
- ``"timer-expired"``: The timer for this plugin has expired. This event is
only sent if the plugin has set a timer with ``timer_set()``.
.. code-block:: lua
libinput:connect("timer-expired", function (now) ... end)
The ``now`` argument is the current time in microseconds in
``CLOCK_MONOTONIC`` (see ``libinput:now()``).
.. function:: libinput:timer_cancel()
Cancel the timer for this plugin. This is a no-op if the timer
has not been set or has already expired.
.. function:: libinput:timer_set_absolute(time)
Set a timer for this plugin, with the given time in microseconds.
The timeout specifies an absolute time in microseconds (see
``libinput:now()``) The timer will expire once and then call the
``"timer-expired"`` event handler (if any).
See ``libinput:timer_set_relative()`` for a relative timer.
The following two lines of code are equivalent:
.. code-block:: lua
libinput:timer_set_relative(1000000) -- 1 second from now
libinput:timer_set_absolute(libinput:now() + 1000000) -- 1 second from now
Calling this function will cancel any existing (relative or absolute) timer.
.. function:: libinput:timer_set_relative(timeout)
Set a timer for this plugin, with the given timeout in microseconds from
the current time. The timer will expire once and then call the
``"timer-expired"`` event handler (if any).
See ``libinput:timer_set_absolute()`` for an absolute timer.
The following two lines of code are equivalent:
.. code-block:: lua
libinput:timer_set_relative(1000000) -- 1 second from now
libinput:timer_set_absolute(libinput:now() + 1000000) -- 1 second from now
Calling this function will cancel any existing (relative or absolute) timer.
.. _plugins_api_evdevdevice:
................................................................................
The ``EvdevDevice`` type
................................................................................
The ``EvdevDevice`` type represents a device available in the system
but not (yet) added by libinput. This device may be used to modify
a device's capabilities before the device is processed by libinput.
A plugin should always ``connect()`` to the ``"device-removed"`` callback
to be notified when a device is removed. If the plugin keeps a reference
to this device but the device is discarded by libinput, the device's query
methods will return zero values (e.g. ``nil``, ``0``, an empty table) and
methods will be noops.
.. function:: EvdevDevice:info()
A table containing static information about the device, e.g.
.. code-block:: lua
{
bustype = evdev.BUS_USB,
vid = 0x1234,
pid = 0x5678,
}
A plugin must ignore keys it does not know about.
Version 1 of the plugin API supports the following keys and values:
- ``bustype``: The numeric bustype of the device. See the
``BUS_*`` defines in ``linux/input.h`` for the list of possible values.
- ``vid``: The 16-bit vendor ID of the device
- ``pid``: The 16-bit product ID of the device
If the device has since been discarded by libinput, this function returns an
empty table.
.. function:: EvdevDevice:name()
The device name as set by the kernel
.. function:: EvdevDevice:usages()
Returns a table of all usages that are currently enabled for this
device. Any type that exists on the device has a table assigned and in this
table any code that exists on the device is a boolean true.
For example:
.. code-block:: lua
{
evdev.REL_X = true,
evdev.REL_Y = true,
evdev.BTN_LEFT = true,
}
All other usages are ``nil``, so that the following code is possible:
.. code-block:: lua
local usages = device:usages()
if usages[evdev.REL_X] then
-- do something
end
If the device has since been discarded by libinput, this function returns an
empty table.
.. function:: EvdevDevice:absinfos()
Returns a table of all ``EV_ABS`` codes that are currently enabled for this device.
The event code is the key, each value is a table containing the following keys:
``minimum``, ``maximum``, ``fuzz``, ``flat``, ``resolution``.
.. code-block:: lua
{
evdev.ABS_X = {
minimum = 0,
maximum = 1234,
fuzz = 0,
flat = 0,
resolution = 45,
},
}
If the device has since been discarded by libinput, this function returns an
empty table.
.. function:: EvdevDevice:udev_properties()
Returns a table containing a filtered list of udev properties available on this device
in the form ``{ property_name = property_value, ... }``.
udev properties used as a boolean (e.g. ``ID_INPUT``) are only present if their
value is a logical true.
Version 1 of the plugin API supports the following udev properties:
- ``ID_INPUT`` and all of ``ID_INPUT_*`` that denote the device type as assigned
by udev. This information is usually used by libinput to determine a
device type. Note that for historical reasons these properties have
varying rules - some properties may be mutually exclusive, others are
independent, others may only be set if another property is set. Refer to
the udev documentation (if any) for details. ``ID_INPUT_WIDTH_MM`` and
``ID_INPUT_HEIGHT_MM`` are excluded from this set.
If the device has since been discarded by libinput, this function returns an
empty table.
.. function:: EvdevDevice:enable_evdev_usage(usage)
Enable the given :ref:`evdev usage <plugins_api_evdev_usage>` for this device.
Use :ref:`plugins_api_evdev_global` for better readability,
e.g. ``device:enable_evdev_usage(evdev.REL_X)``.
This function must not be used for ``ABS_*`` events, use ``set_absinfo()``
instead.
Once a usage is enabled, events for that usage may be added to a device's
frame.
If the device has since been discarded by libinput, this function does nothing.
.. function:: EvdevDevice:disable_evdev_usage(usage)
Disable the given :ref:`evdev usage <plugins_api_evdev_usage>` for this device.
Use :ref:`plugins_api_evdev_global` for better readability,
e.g. ``device:disable_evdev_usage(evdev.REL_X)``.
Once a usage is disabled, events for that usage are discarded from any
device frame.
If the device has since been discarded by libinput, this function does nothing.
.. function:: EvdevDevice:set_absinfo(usage, absinfo)
Set the absolute axis information for the given :ref:`evdev usage <plugins_api_evdev_usage>`
and enable it if it does not yet exist on the device. The ``absinfo`` argument is a table
containing zero or more of the following keys: ``minimum``, ``maximum``, ``fuzz``,
``flat``, ``resolution``. Any missing key defaults the corresponding
value from the device if the device already has this event usage or zero otherwise.
For example, the following code changes the resolution but leaves everything
else as-is:
.. code-block:: lua
local absinfo = {
resolution = 40,
}
device:set_absinfo(evdev.ABS_X, absinfo)
device:set_absinfo(evdev.ABS_Y, absinfo)
Use :ref:`plugins_api_evdev_global` for better readability as shown in the
example above.
If the device has since been discarded by libinput, this function does nothing.
.. note:: Overriding the absinfo values often indicates buggy firmware. This should
typically be fixed with an entry in the
`60-evdev.hwdb <https://github.com/systemd/systemd/blob/main/hwdb.d/60-evdev.hwdb>`_
or :ref:`device-quirks` instead of a plugin so all users of that
device can benefit from the fix.
.. function:: EvdevDevice:connect(name, function)
Set the callback to the given event name. Only one callback
may be set for an event name at any time, subsequent callbacks
will overwrite any earlier callbacks for the same name.
If the device has since been discarded by libinput, this function does nothing.
Version 1 of the plugin API supports the following events and callback arguments:
- ``"evdev-frame"``: A new :ref:`evdev frame <plugins_api_evdev_frame>` has
started for this device. If the callback returns a value other than
``nil``, that value is the frame with any modified events.
An empty frame (``{}``) causes libinput to drop the current event frame.
.. code-block:: lua
device:connect("evdev-frame", function (device, frame, timestamp)
-- change any event into a movement left by 1 pixel
move_left = {
{ usage = evdev.REL_X, value = -1, },
}
return move_left
end
The timestamp of an event frame is in microseconds in ``CLOCK_MONOTONIC``, see
``libinput:now()`` for details.
For performance reasons plugins that do not modify the event frame should
return ``nil`` (or nothing) instead of the event frame that was passed
as argument.
- ``"device-removed"``: This device was removed by libinput. This may happen
without the device ever becoming a libinput device as seen by libinput's
public API (e.g. if the device does not meet the requirements to be
added). Once this callback is invoked, the plugin should remove any
references to this device and stop using it.
.. code-block:: lua
device:connect("device-removed", function (device) ... end)
Functions to query the device's capabilities (e.g. ``usages()``) will
return an empty table.
.. function:: EvdevDevice:disconnect(name)
Disconnect the existing callback (if any) for the given event name. See
``EvdevDevice:connect()`` for a list of supported names.
.. function:: EvdevDevice:prepend_frame(frame)
Prepend an :ref:`evdev frame <plugins_api_evdev_frame>` for this device
**before** the current frame (if any). The **next** plugin will see the
prepended frame first followed by the current frame.
This function can only be called from within a device's ``"evdev-frame"``
handler or from within the plugin's timer callback function.
For example, to change a single event into a drag, prepend a button
down and append a button up before each event:
.. code:: lua
function frame_handler(device, frame, timestamp)
device:prepend_frame({
{ usage = evdev.BTN_LEFT, value = 1}
})
device:append_frame({
{ usage = evdev.BTN_LEFT, value = 0}
})
return nil -- return the current frame unmodified
-- The next plugin sees the event sequence:
-- button down, frame, button up
end
If called from within the plugin's timer there is no current frame and this
function is identical to ``append_frame()``.
.. function:: EvdevDevice:append_frame(frame)
Appends an :ref:`evdev frame <plugins_api_evdev_frame>` for this device
**after** the current frame (if any). This function can only be called from
within a device's ``"evdev-frame"`` handler or from within the plugin's timer
callback function.
If called from within the plugin's timer there is no current frame and this
function is identical to ``prepend_frame()``.
See ``prepend_frame()`` for more details.
.. function:: EvdevDevice:disable_feature(feature_name)
Disable the given libinput-internal feature for this device. This should be used
by plugins that replace that feature with a custom implementation for this device.
libinput may have multiple internal implementations for any given feature, disabling
it via this API disables any and all of those implementations, causing the feature to
no longer work at all. It is up to the plugin implementation to re-implement that
feature to match the user's expectation.
Version 1 of the plugin API supports the following features:
- ``"button-debouncing"``: see :ref:`button_debouncing`
- ``"touchpad-hysteresis"``: see :ref:`touchpad_jitter`
- ``"touchpad-jump-detection"``: see :ref:`touchpad_jumping_cursor`
- ``"touchpad-palm-detection"``: see :ref:`palm_detection`
- ``"wheel-debouncing"``: some high-resolution mouse wheel movements inside libinput
are delayed and/or modified

View file

@ -95,11 +95,13 @@ src_rst = files(
# dot drawings
'dot/seats-sketch.gv',
'dot/seats-sketch-libinput.gv',
'dot/libinput-contexts.gv',
'dot/libinput-stack-wayland.gv',
'dot/libinput-stack-xorg.gv',
'dot/libinput-stack-gnome.gv',
'dot/evemu.gv',
'dot/libinput-record.gv',
'dot/plugin-stack.gv',
# svgs
'svg/button-debouncing-wave-diagram.svg',
'svg/button-scrolling.svg',
@ -155,6 +157,7 @@ src_rst = files(
'middle-button-emulation.rst',
'normalization-of-relative-motion.rst',
'palm-detection.rst',
'lua-plugins.rst',
'pointer-acceleration.rst',
'reporting-bugs.rst',
'scrolling.rst',

View file

@ -113,8 +113,8 @@ When you file a bug, please attach the following information:
- the output from udevadm info, see :ref:`udev_info`.
- the vendor model number of your laptop (e.g. "Lenovo Thinkpad T440s")
- and the content of ``/sys/class/dmi/id/modalias``.
- run the ``touchpad-edge-detector`` tool (provided by libevdev) and verify
that the ranges and sizes it prints match the touchpad (up to 5mm
- run ``libinput measure touchpad-size`` tool (see :ref:`absolute_coordinate_ranges_fix`)
and verify that the ranges and sizes it prints match the touchpad (up to 5mm
difference is ok)
If you are reporting a bug related to button event generation:

View file

@ -17,7 +17,7 @@ other properties.
Number of buttons
------------------------------------------------------------------------------
.. _touchapds_buttons_phys:
.. _touchpads_buttons_phys:
..............................................................................
Physically separate buttons
@ -57,7 +57,7 @@ property.
.. _touchpads_buttons_forcepads:
..............................................................................
Forcepads
Forcepads/Pressurepads
..............................................................................
Forcepads are Clickpads without a physical button underneath the hardware.
@ -65,6 +65,7 @@ They provide pressure and may have a vibration element that is
software-controlled. This element can simulate the feel of a physical
click or be co-opted for other tasks.
Forcepads are also called pressurepads or haptic touchpads.
.. _touchpads_touch:
@ -79,7 +80,7 @@ device can **track**, i.e. provide reliable positional information for.
In the kernel each finger is tracked in a so-called "slot", the number of
slots thus equals the number of simultaneous touches a device can track.
.. _touchapds_touch_st:
.. _touchpads_touch_st:
..............................................................................
Single-touch touchpads

View file

@ -27,6 +27,7 @@
#define INPUT_PROP_TOPBUTTONPAD 0x04 /* softbuttons at top of pad */
#define INPUT_PROP_POINTING_STICK 0x05 /* is a pointing stick */
#define INPUT_PROP_ACCELEROMETER 0x06 /* has accelerometer */
#define INPUT_PROP_PRESSUREPAD 0x07 /* pressure triggers clicks */
#define INPUT_PROP_MAX 0x1f
#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1)
@ -278,7 +279,8 @@
#define KEY_PAUSECD 201
#define KEY_PROG3 202
#define KEY_PROG4 203
#define KEY_DASHBOARD 204 /* AL Dashboard */
#define KEY_ALL_APPLICATIONS 204 /* AC Desktop Show All Applications */
#define KEY_DASHBOARD KEY_ALL_APPLICATIONS
#define KEY_SUSPEND 205
#define KEY_CLOSE 206 /* AC Close */
#define KEY_PLAY 207
@ -515,6 +517,10 @@
#define KEY_10CHANNELSUP 0x1b8 /* 10 channels up (10+) */
#define KEY_10CHANNELSDOWN 0x1b9 /* 10 channels down (10-) */
#define KEY_IMAGES 0x1ba /* AL Image Browser */
#define KEY_NOTIFICATION_CENTER 0x1bc /* Show/hide the notification center */
#define KEY_PICKUP_PHONE 0x1bd /* Answer incoming call */
#define KEY_HANGUP_PHONE 0x1be /* Decline incoming call */
#define KEY_LINK_PHONE 0x1bf /* AL Phone Syncing */
#define KEY_DEL_EOL 0x1c0
#define KEY_DEL_EOS 0x1c1
@ -542,6 +548,7 @@
#define KEY_FN_F 0x1e2
#define KEY_FN_S 0x1e3
#define KEY_FN_B 0x1e4
#define KEY_FN_RIGHT_SHIFT 0x1e5
#define KEY_BRL_DOT1 0x1f1
#define KEY_BRL_DOT2 0x1f2
@ -595,8 +602,14 @@
#define BTN_DPAD_LEFT 0x222
#define BTN_DPAD_RIGHT 0x223
#define BTN_GRIPL 0x224
#define BTN_GRIPR 0x225
#define BTN_GRIPL2 0x226
#define BTN_GRIPR2 0x227
#define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */
#define KEY_ROTATE_LOCK_TOGGLE 0x231 /* Display rotation lock */
#define KEY_REFRESH_RATE_TOGGLE 0x232 /* Display refresh rate toggle */
#define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */
#define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */
@ -607,10 +620,29 @@
#define KEY_VOICECOMMAND 0x246 /* Listening Voice Command */
#define KEY_ASSISTANT 0x247 /* AL Context-aware desktop assistant */
#define KEY_KBD_LAYOUT_NEXT 0x248 /* AC Next Keyboard Layout Select */
#define KEY_EMOJI_PICKER 0x249 /* Show/hide emoji picker (HUTRR101) */
#define KEY_DICTATE 0x24a /* Start or Stop Voice Dictation Session (HUTRR99) */
#define KEY_CAMERA_ACCESS_ENABLE 0x24b /* Enables programmatic access to camera devices. (HUTRR72) */
#define KEY_CAMERA_ACCESS_DISABLE 0x24c /* Disables programmatic access to camera devices. (HUTRR72) */
#define KEY_CAMERA_ACCESS_TOGGLE 0x24d /* Toggles the current state of the camera access control. (HUTRR72) */
#define KEY_ACCESSIBILITY 0x24e /* Toggles the system bound accessibility UI/command (HUTRR116) */
#define KEY_DO_NOT_DISTURB 0x24f /* Toggles the system-wide "Do Not Disturb" control (HUTRR94)*/
#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */
#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */
/*
* Keycodes for hotkeys toggling the electronic privacy screen found on some
* laptops on/off. Note when the embedded-controller turns on/off the eprivacy
* screen itself then the state should be reported through drm connecter props:
* https://www.kernel.org/doc/html/latest/gpu/drm-kms.html#standard-connector-properties
* Except when implementing the drm connecter properties API is not possible
* because e.g. the firmware does not allow querying the presence and/or status
* of the eprivacy screen at boot.
*/
#define KEY_EPRIVACY_SCREEN_ON 0x252
#define KEY_EPRIVACY_SCREEN_OFF 0x253
#define KEY_KBDINPUTASSIST_PREV 0x260
#define KEY_KBDINPUTASSIST_NEXT 0x261
#define KEY_KBDINPUTASSIST_PREVGROUP 0x262
@ -655,6 +687,27 @@
/* Select an area of screen to be copied */
#define KEY_SELECTIVE_SCREENSHOT 0x27a
/* Move the focus to the next or previous user controllable element within a UI container */
#define KEY_NEXT_ELEMENT 0x27b
#define KEY_PREVIOUS_ELEMENT 0x27c
/* Toggle Autopilot engagement */
#define KEY_AUTOPILOT_ENGAGE_TOGGLE 0x27d
/* Shortcut Keys */
#define KEY_MARK_WAYPOINT 0x27e
#define KEY_SOS 0x27f
#define KEY_NAV_CHART 0x280
#define KEY_FISHING_CHART 0x281
#define KEY_SINGLE_RANGE_RADAR 0x282
#define KEY_DUAL_RANGE_RADAR 0x283
#define KEY_RADAR_OVERLAY 0x284
#define KEY_TRADITIONAL_SONAR 0x285
#define KEY_CLEARVU_SONAR 0x286
#define KEY_SIDEVU_SONAR 0x287
#define KEY_NAV_INFO 0x288
#define KEY_BRIGHTNESS_MENU 0x289
/*
* Some keyboards have keys which do not have a defined meaning, these keys
* are intended to be programmed / bound to macros by the user. For most
@ -730,6 +783,9 @@
#define KEY_KBD_LCD_MENU4 0x2bb
#define KEY_KBD_LCD_MENU5 0x2bc
/* Performance Boost key (Alienware)/G-Mode key (Dell) */
#define KEY_PERFORMANCE 0x2bd
#define BTN_TRIGGER_HAPPY 0x2c0
#define BTN_TRIGGER_HAPPY1 0x2c0
#define BTN_TRIGGER_HAPPY2 0x2c1
@ -834,6 +890,7 @@
#define ABS_TOOL_WIDTH 0x1c
#define ABS_VOLUME 0x20
#define ABS_PROFILE 0x21
#define ABS_MISC 0x28
@ -889,7 +946,8 @@
#define SW_MUTE_DEVICE 0x0e /* set = device disabled */
#define SW_PEN_INSERTED 0x0f /* set = pen inserted */
#define SW_MACHINE_COVER 0x10 /* set = cover closed */
#define SW_MAX 0x10
#define SW_USB_INSERT 0x11 /* set = USB audio device connected */
#define SW_MAX 0x11
#define SW_CNT (SW_MAX+1)
/*

View file

@ -27,6 +27,7 @@
#define INPUT_PROP_TOPBUTTONPAD 0x04 /* softbuttons at top of pad */
#define INPUT_PROP_POINTING_STICK 0x05 /* is a pointing stick */
#define INPUT_PROP_ACCELEROMETER 0x06 /* has accelerometer */
#define INPUT_PROP_PRESSUREPAD 0x07 /* pressure triggers clicks */
#define INPUT_PROP_MAX 0x1f
#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1)
@ -278,7 +279,8 @@
#define KEY_PAUSECD 201
#define KEY_PROG3 202
#define KEY_PROG4 203
#define KEY_DASHBOARD 204 /* AL Dashboard */
#define KEY_ALL_APPLICATIONS 204 /* AC Desktop Show All Applications */
#define KEY_DASHBOARD KEY_ALL_APPLICATIONS
#define KEY_SUSPEND 205
#define KEY_CLOSE 206 /* AC Close */
#define KEY_PLAY 207
@ -515,6 +517,10 @@
#define KEY_10CHANNELSUP 0x1b8 /* 10 channels up (10+) */
#define KEY_10CHANNELSDOWN 0x1b9 /* 10 channels down (10-) */
#define KEY_IMAGES 0x1ba /* AL Image Browser */
#define KEY_NOTIFICATION_CENTER 0x1bc /* Show/hide the notification center */
#define KEY_PICKUP_PHONE 0x1bd /* Answer incoming call */
#define KEY_HANGUP_PHONE 0x1be /* Decline incoming call */
#define KEY_LINK_PHONE 0x1bf /* AL Phone Syncing */
#define KEY_DEL_EOL 0x1c0
#define KEY_DEL_EOS 0x1c1
@ -542,6 +548,7 @@
#define KEY_FN_F 0x1e2
#define KEY_FN_S 0x1e3
#define KEY_FN_B 0x1e4
#define KEY_FN_RIGHT_SHIFT 0x1e5
#define KEY_BRL_DOT1 0x1f1
#define KEY_BRL_DOT2 0x1f2
@ -595,8 +602,14 @@
#define BTN_DPAD_LEFT 0x222
#define BTN_DPAD_RIGHT 0x223
#define BTN_GRIPL 0x224
#define BTN_GRIPR 0x225
#define BTN_GRIPL2 0x226
#define BTN_GRIPR2 0x227
#define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */
#define KEY_ROTATE_LOCK_TOGGLE 0x231 /* Display rotation lock */
#define KEY_REFRESH_RATE_TOGGLE 0x232 /* Display refresh rate toggle */
#define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */
#define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */
@ -607,10 +620,29 @@
#define KEY_VOICECOMMAND 0x246 /* Listening Voice Command */
#define KEY_ASSISTANT 0x247 /* AL Context-aware desktop assistant */
#define KEY_KBD_LAYOUT_NEXT 0x248 /* AC Next Keyboard Layout Select */
#define KEY_EMOJI_PICKER 0x249 /* Show/hide emoji picker (HUTRR101) */
#define KEY_DICTATE 0x24a /* Start or Stop Voice Dictation Session (HUTRR99) */
#define KEY_CAMERA_ACCESS_ENABLE 0x24b /* Enables programmatic access to camera devices. (HUTRR72) */
#define KEY_CAMERA_ACCESS_DISABLE 0x24c /* Disables programmatic access to camera devices. (HUTRR72) */
#define KEY_CAMERA_ACCESS_TOGGLE 0x24d /* Toggles the current state of the camera access control. (HUTRR72) */
#define KEY_ACCESSIBILITY 0x24e /* Toggles the system bound accessibility UI/command (HUTRR116) */
#define KEY_DO_NOT_DISTURB 0x24f /* Toggles the system-wide "Do Not Disturb" control (HUTRR94)*/
#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */
#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */
/*
* Keycodes for hotkeys toggling the electronic privacy screen found on some
* laptops on/off. Note when the embedded-controller turns on/off the eprivacy
* screen itself then the state should be reported through drm connecter props:
* https://www.kernel.org/doc/html/latest/gpu/drm-kms.html#standard-connector-properties
* Except when implementing the drm connecter properties API is not possible
* because e.g. the firmware does not allow querying the presence and/or status
* of the eprivacy screen at boot.
*/
#define KEY_EPRIVACY_SCREEN_ON 0x252
#define KEY_EPRIVACY_SCREEN_OFF 0x253
#define KEY_KBDINPUTASSIST_PREV 0x260
#define KEY_KBDINPUTASSIST_NEXT 0x261
#define KEY_KBDINPUTASSIST_PREVGROUP 0x262
@ -655,6 +687,27 @@
/* Select an area of screen to be copied */
#define KEY_SELECTIVE_SCREENSHOT 0x27a
/* Move the focus to the next or previous user controllable element within a UI container */
#define KEY_NEXT_ELEMENT 0x27b
#define KEY_PREVIOUS_ELEMENT 0x27c
/* Toggle Autopilot engagement */
#define KEY_AUTOPILOT_ENGAGE_TOGGLE 0x27d
/* Shortcut Keys */
#define KEY_MARK_WAYPOINT 0x27e
#define KEY_SOS 0x27f
#define KEY_NAV_CHART 0x280
#define KEY_FISHING_CHART 0x281
#define KEY_SINGLE_RANGE_RADAR 0x282
#define KEY_DUAL_RANGE_RADAR 0x283
#define KEY_RADAR_OVERLAY 0x284
#define KEY_TRADITIONAL_SONAR 0x285
#define KEY_CLEARVU_SONAR 0x286
#define KEY_SIDEVU_SONAR 0x287
#define KEY_NAV_INFO 0x288
#define KEY_BRIGHTNESS_MENU 0x289
/*
* Some keyboards have keys which do not have a defined meaning, these keys
* are intended to be programmed / bound to macros by the user. For most
@ -730,6 +783,9 @@
#define KEY_KBD_LCD_MENU4 0x2bb
#define KEY_KBD_LCD_MENU5 0x2bc
/* Performance Boost key (Alienware)/G-Mode key (Dell) */
#define KEY_PERFORMANCE 0x2bd
#define BTN_TRIGGER_HAPPY 0x2c0
#define BTN_TRIGGER_HAPPY1 0x2c0
#define BTN_TRIGGER_HAPPY2 0x2c1
@ -834,6 +890,7 @@
#define ABS_TOOL_WIDTH 0x1c
#define ABS_VOLUME 0x20
#define ABS_PROFILE 0x21
#define ABS_MISC 0x28
@ -889,7 +946,8 @@
#define SW_MUTE_DEVICE 0x0e /* set = device disabled */
#define SW_PEN_INSERTED 0x0f /* set = pen inserted */
#define SW_MACHINE_COVER 0x10 /* set = cover closed */
#define SW_MAX 0x10
#define SW_USB_INSERT 0x11 /* set = USB audio device connected */
#define SW_MAX 0x11
#define SW_CNT (SW_MAX+1)
/*

View file

@ -1,8 +1,8 @@
project('libinput', 'c',
version : '1.28.901',
version : '1.30.0',
license : 'MIT/Expat',
default_options : [ 'c_std=gnu99', 'warning_level=2' ],
meson_version : '>= 0.56.0')
meson_version : '>= 0.64.0')
libinput_version = meson.project_version().split('.')
@ -156,8 +156,9 @@ if not cc.has_header_symbol('sys/ptrace.h', 'PTRACE_ATTACH', prefix : prefix)
config_h.set('PTRACE_CONT', 'PT_CONTINUE')
config_h.set('PTRACE_DETACH', 'PT_DETACH')
endif
config_h.set10('HAVE_INSTALLED_TESTS', get_option('install-tests'))
if get_option('install-tests')
config_h.set('HAVE_INSTALLED_TESTS', 1)
endif
# Dependencies
pkgconfig = import('pkgconfig')
@ -167,6 +168,32 @@ dep_libevdev = dependency('libevdev', version: '>= 1.10.0')
dep_lm = cc.find_library('m', required : false)
dep_rt = cc.find_library('rt', required : false)
dep_lua = dependency('lua-5.4', 'lua5.4', 'lua',
version : '>= 5.4',
required : get_option('lua-plugins'))
have_lua = dep_lua.found()
if have_lua
config_h.set('HAVE_LUA', 1)
endif
have_plugins = dep_lua.found()
if have_plugins
config_h.set('HAVE_PLUGINS', 1)
endif
autoload_plugins = get_option('autoload-plugins')
if autoload_plugins
config_h.set('AUTOLOAD_PLUGINS', 1)
endif
summary({
'Plugins enabled' : have_plugins,
'Autoload plugins' : autoload_plugins,
'Lua Plugin support' : have_lua,
},
section : 'Plugins',
bool_yn : true)
# Include directories
includes_include = include_directories('include')
includes_src = include_directories('src')
@ -174,8 +201,8 @@ includes_src = include_directories('src')
############ mtdev configuration ############
have_mtdev = get_option('mtdev')
config_h.set10('HAVE_MTDEV', have_mtdev)
if have_mtdev
config_h.set('HAVE_MTDEV', 1)
dep_mtdev = dependency('mtdev', version : '>= 1.1.0')
else
dep_mtdev = declare_dependency()
@ -184,8 +211,8 @@ endif
############ libwacom configuration ############
have_libwacom = get_option('libwacom')
config_h.set10('HAVE_LIBWACOM', have_libwacom)
if have_libwacom
config_h.set('HAVE_LIBWACOM', 1)
dep_libwacom = dependency('libwacom', version : '>= 0.27')
if cc.has_header_symbol('libwacom/libwacom.h', 'WACOM_BUTTON_DIAL_MODESWITCH',
dependencies : dep_libwacom)
@ -206,6 +233,7 @@ executable('libinput-device-group',
dependencies : [dep_udev, dep_libwacom],
include_directories : [includes_src, includes_include],
install : true,
install_tag : 'runtime',
install_dir : dir_udev_callouts)
executable('libinput-fuzz-extract',
'udev/libinput-fuzz-extract.c',
@ -214,16 +242,18 @@ executable('libinput-fuzz-extract',
dependencies : [dep_udev, dep_libevdev, dep_lm],
include_directories : [includes_src, includes_include],
install : true,
install_tag : 'runtime',
install_dir : dir_udev_callouts)
executable('libinput-fuzz-to-zero',
'udev/libinput-fuzz-to-zero.c',
dependencies : [dep_udev, dep_libevdev],
include_directories : [includes_src, includes_include],
install : true,
install_tag : 'runtime',
install_dir : dir_udev_callouts)
udev_rules_config = configuration_data()
udev_rules_config.set('UDEV_TEST_PATH', '')
udev_rules_config.set('UDEV_TEST_PATH', dir_udev_callouts + '/')
configure_file(input : 'udev/80-libinput-device-groups.rules.in',
output : '80-libinput-device-groups.rules',
install_dir : dir_udev_rules,
@ -369,14 +399,15 @@ libquirks = static_library('quirks', src_libquirks,
dep_libquirks = declare_dependency(link_with : libquirks)
# Create /etc/libinput
if meson.version().version_compare('>= 0.60')
install_emptydir(dir_etc / 'libinput')
else
install_subdir('libinput', install_dir : dir_etc)
endif
############ libinput.so ############
config_h.set10('EVENT_DEBUGGING', get_option('internal-event-debugging'))
if get_option('internal-event-debugging')
config_h.set('EVENT_DEBUGGING', 1)
endif
config_h.set_quoted('LIBINPUT_PLUGIN_LIBDIR', dir_lib / 'libinput' / 'plugins')
config_h.set_quoted('LIBINPUT_PLUGIN_ETCDIR', dir_etc / 'libinput' / 'plugins')
install_headers('src/libinput.h')
src_libinput = src_libfilter + [
@ -384,6 +415,7 @@ src_libinput = src_libfilter + [
'src/libinput-plugin.c',
'src/libinput-plugin-button-debounce.c',
'src/libinput-plugin-mouse-wheel.c',
'src/libinput-plugin-mouse-wheel-lowres.c',
'src/libinput-plugin-tablet-double-tool.c',
'src/libinput-plugin-tablet-eraser-button.c',
'src/libinput-plugin-tablet-forced-tool.c',
@ -408,10 +440,17 @@ src_libinput = src_libfilter + [
'src/timer.c',
'src/util-libinput.c',
]
if have_mtdev
src_libinput += ['src/libinput-plugin-mtdev.c']
endif
if dep_lua.found()
src_libinput += [
'src/libinput-plugin-lua.c',
]
endif
deps_libinput = [
dep_mtdev,
dep_udev,
@ -421,7 +460,8 @@ deps_libinput = [
dep_rt,
dep_libwacom,
dep_libinput_util,
dep_libquirks
dep_libquirks,
dep_lua,
]
libinput_version_h_config = configuration_data()
@ -452,9 +492,7 @@ dep_libinput = declare_dependency(
link_with : lib_libinput,
dependencies : deps_libinput)
if meson.version().version_compare('>= 0.54.0')
meson.override_dependency('libinput', dep_libinput)
endif
pkgconfig.generate(
filebase : 'libinput',
@ -463,6 +501,9 @@ pkgconfig.generate(
version : meson.project_version(),
libraries : lib_libinput,
requires_private : dep_udev,
variables : [
'plugindir=${libdir}/libinput/plugins'
]
)
git_version_h = vcs_tag(command : ['git', 'describe'],
@ -470,6 +511,8 @@ git_version_h = vcs_tag(command : ['git', 'describe'],
input : 'src/libinput-git-version.h.in',
output :'libinput-git-version.h')
subdir('plugins')
############ documentation ############
if get_option('documentation')
@ -515,6 +558,7 @@ executable('libinput-debug-events',
dependencies : deps_tools,
include_directories : [includes_src, includes_include],
install_dir : libinput_tool_path,
install_tag : 'bin',
install : true
)
@ -524,8 +568,17 @@ executable('libinput-debug-tablet',
dependencies : deps_tools,
include_directories : [includes_src, includes_include],
install_dir : libinput_tool_path,
install_tag : 'bin',
install : true)
libinput_debug_tablet_pad_sources = [ 'tools/libinput-debug-tablet-pad.c' ]
executable('libinput-debug-tablet-pad',
libinput_debug_tablet_pad_sources,
dependencies : deps_tools,
include_directories : [includes_src, includes_include],
install_dir : libinput_tool_path,
install_tag : 'bin',
install : true)
libinput_quirks_sources = [ 'tools/libinput-quirks.c' ]
libinput_quirks = executable('libinput-quirks',
@ -533,6 +586,7 @@ libinput_quirks = executable('libinput-quirks',
dependencies : [dep_libquirks, dep_tools_shared, dep_libinput],
include_directories : [includes_src, includes_include],
install_dir : libinput_tool_path,
install_tag : 'bin',
install : true
)
test('validate-quirks',
@ -554,6 +608,7 @@ libinput_list_devices = executable('libinput-list-devices',
dependencies : deps_tools,
include_directories : [includes_src, includes_include],
install_dir : libinput_tool_path,
install_tag : 'bin',
install : true,
)
test('list-devices',
@ -566,6 +621,7 @@ executable('libinput-measure',
dependencies : deps_tools,
include_directories : [includes_src, includes_include],
install_dir : libinput_tool_path,
install_tag : 'bin',
install : true,
)
@ -575,6 +631,7 @@ executable('libinput-analyze',
dependencies : deps_tools,
include_directories : [includes_src, includes_include],
install_dir : libinput_tool_path,
install_tag : 'bin',
install : true,
)
@ -606,23 +663,23 @@ executable('libinput-record',
dependencies : deps_tools + [dep_udev],
include_directories : [includes_src, includes_include],
install_dir : libinput_tool_path,
install_tag : 'bin',
install : true,
)
config_h.set10('HAVE_DEBUG_GUI', get_option('debug-gui'))
if get_option('debug-gui')
config_h.set('HAVE_DEBUG_GUI', 1)
dep_gtk = dependency('gtk4', version : '>= 4.0', required : false)
config_h.set10('HAVE_GTK4', dep_gtk.found())
if not dep_gtk.found()
if dep_gtk.found()
config_h.set('HAVE_GTK4', 1)
else
dep_gtk = dependency('gtk+-3.0', version : '>= 3.20')
config_h.set10('HAVE_GTK3', dep_gtk.found())
if dep_gtk.found()
config_h.set('HAVE_GTK3', 1)
endif
endif
if meson.version().version_compare('>= 0.58')
gtk_targets = dep_gtk.get_variable('targets')
else
gtk_targets = dep_gtk.get_pkgconfig_variable('targets')
endif
have_gtk_wayland = gtk_targets.contains('wayland')
have_gtk_x11 = gtk_targets.contains('x11')
@ -633,18 +690,15 @@ if get_option('debug-gui')
dep_wayland_client = dependency('wayland-client', required : false)
dep_wayland_protocols = dependency('wayland-protocols', required : false)
config_h.set10('HAVE_GTK_X11', have_gtk_x11 and dep_x11.found())
config_h.set10('HAVE_GTK_WAYLAND', false)
if have_gtk_x11 and dep_x11.found()
config_h.set('HAVE_GTK_X11', 1)
endif
debug_gui_sources = [ 'tools/libinput-debug-gui.c' ]
if have_gtk_wayland and dep_wayland_client.found() and dep_wayland_protocols.found()
wayland_scanner = find_program('wayland-scanner')
if meson.version().version_compare('>= 0.58')
wlproto_dir = dep_wayland_protocols.get_variable('pkgdatadir')
else
wlproto_dir = dep_wayland_protocols.get_pkgconfig_variable('pkgdatadir')
endif
proto_name = 'pointer-constraints-unstable-v1'
input = files(wlproto_dir / 'unstable' / 'pointer-constraints' / '@0@.xml'.format(proto_name))
@ -662,7 +716,7 @@ if get_option('debug-gui')
)
debug_gui_sources += [ wayland_headers, wayland_sources ]
config_h.set10('HAVE_GTK_WAYLAND', true)
config_h.set('HAVE_GTK_WAYLAND', 1)
endif
deps_debug_gui = [
@ -678,6 +732,7 @@ if get_option('debug-gui')
dependencies : deps_debug_gui,
include_directories : [includes_src, includes_include],
install_dir : libinput_tool_path,
install_tag : 'bin',
install : true
)
src_man += files('tools/libinput-debug-gui.man')
@ -689,6 +744,7 @@ libinput_tool = executable('libinput',
libinput_sources,
dependencies : deps_tools,
include_directories : [includes_src, includes_include],
install_tag : 'bin',
install : true
)
@ -779,6 +835,7 @@ executable('libinput-test',
dependencies : deps_tools,
include_directories : [includes_src, includes_include],
install_dir : libinput_tool_path,
install_tag : 'tests',
install : true,
)
@ -788,11 +845,15 @@ if get_option('tests')
dep_check = dependency('check', version : '>= 0.9.10', required: false)
gstack = find_program('gstack', required : false)
config_h.set10('HAVE_GSTACK', gstack.found())
if gstack.found()
config_h.set('HAVE_GSTACK', 1)
endif
# for inhibit support during test run
dep_libsystemd = dependency('libsystemd', version : '>= 221', required : false)
config_h.set10('HAVE_LIBSYSTEMD', dep_libsystemd.found())
if dep_libsystemd.found()
config_h.set('HAVE_LIBSYSTEMD', 1)
endif
litest_sources = [
'src/libinput-private-config.c',
@ -830,6 +891,7 @@ if get_option('tests')
'test/litest-device-keyboard-razer-blackwidow.c',
'test/litest-device-keyboard-razer-blade-stealth.c',
'test/litest-device-keyboard-razer-blade-stealth-videoswitch.c',
'test/litest-device-keypad-slide-switch.c',
'test/litest-device-lenovo-scrollpoint.c',
'test/litest-device-lid-switch.c',
'test/litest-device-lid-switch-surface3.c',
@ -839,6 +901,8 @@ if get_option('tests')
'test/litest-device-magic-trackpad.c',
'test/litest-device-mouse.c',
'test/litest-device-mouse-wheel-tilt.c',
'test/litest-device-mouse-wheel-hires-disabled.c',
'test/litest-device-mouse-ps2.c',
'test/litest-device-mouse-roccat.c',
'test/litest-device-mouse-low-dpi.c',
'test/litest-device-mouse-virtual.c',
@ -998,6 +1062,10 @@ if get_option('tests')
'test/test-switch.c',
'test/test-quirks.c',
]
if have_plugins and have_lua
tests_sources += ['test/test-plugins-lua.c']
endif
libinput_test_runner_sources = litest_sources + tests_sources
libinput_test_runner = executable('libinput-test-suite',
libinput_test_runner_sources,
@ -1022,7 +1090,10 @@ if get_option('tests')
'quirks',
'switch',
'tablet',
'tablet_eraser',
'tablet_left_handed',
'tablet_proximity',
'tablet_tip',
'totem',
'touch',
'touchpad',
@ -1036,6 +1107,9 @@ if get_option('tests')
'trackpoint',
'udev',
]
if have_plugins and have_lua
collections += ['lua']
endif
foreach group : collections
test('libinput-test-suite-@0@'.format(group),
@ -1043,7 +1117,7 @@ if get_option('tests')
suite : ['all', 'valgrind', 'root', 'hardware'],
args : ['--filter-group=@0@'.format(group)],
is_parallel : false,
timeout : 1200)
timeout : 1100)
endforeach
test('libinput-test-deviceless',
@ -1063,7 +1137,7 @@ if get_option('tests')
'--error-exitcode=3',
'--suppressions=' + valgrind_suppressions_file ],
env : valgrind_env,
timeout_multiplier : 100)
timeout_multiplier : 3)
else
message('valgrind not found, disabling valgrind test suite')
endif
@ -1096,6 +1170,7 @@ src_man += files(
'tools/libinput-analyze-touch-down-state.man',
'tools/libinput-debug-events.man',
'tools/libinput-debug-tablet.man',
'tools/libinput-debug-tablet-pad.man',
'tools/libinput-list-devices.man',
'tools/libinput-list-kernel-devices.man',
'tools/libinput-measure.man',

View file

@ -42,3 +42,11 @@ option('internal-event-debugging',
type: 'boolean',
value: false,
description: 'Enable additional internal event debug tracing. This will print key values to the logs and thus must never be enabled in a release build')
option('autoload-plugins',
type: 'boolean',
value: false,
description: 'Always load plugins from default plugin paths (only if the caller does not do so)')
option('lua-plugins',
type: 'feature',
value: 'auto',
description: 'Enable support for Lua plugins')

View file

@ -0,0 +1,71 @@
-- SPDX-License-Identifier: MIT
--
-- This is an example libinput plugin
--
-- This plugin detects the Copilot key on the keyboard with
-- the given VID/PID and replaces it with a different key (sequence).
-- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN
-- libinput:register({1})
-- Replace this with your keyboard's VID/PID
KEYBOARD_VID = 0x046d
KEYBOARD_PID = 0x4088
meta_is_down = false
shift_is_down = false
-- shift-A, because you can never have enough screaming
replacement_sequence = { evdev.KEY_LEFTSHIFT, evdev.KEY_A }
function frame(device, frame, _)
for _, v in ipairs(frame) do
if v.value ~= 2 then -- ignore key repeats
if v.usage == evdev.KEY_LEFTMETA then
meta_is_down = v.value == 1
elseif v.usage == evdev.KEY_LEFTSHIFT then
shift_is_down = v.value == 1
elseif v.usage == evdev.KEY_F23 and meta_is_down and shift_is_down then
-- We know from the MS requirements that F23 for copilot is
-- either last key (on press) or the first key (on release)
-- of the three-key sequence, and no other keys are
-- within this frame.
if v.value == 1 then
-- Release our modifiers first
device:prepend_frame({
{ usage = evdev.KEY_LEFTSHIFT, value = 0 },
{ usage = evdev.KEY_LEFTMETA, value = 0 },
})
-- Insert our replacement press sequence
local replacement_frame = {}
for _, rv in ipairs(replacement_sequence) do
table.insert(replacement_frame, { usage = rv, value = 1 })
end
device:append_frame(replacement_frame)
else
-- Insert our replacement release sequence
local replacement_frame = {}
for idx = #replacement_sequence, 1, -1 do
table.insert(replacement_frame, { usage = replacement_sequence[idx], value = 0 })
end
device:append_frame(replacement_frame)
-- we don't care about re-pressing shift/meta because the
-- rest of the stack will filter the release for an
-- unpressed key anyway.
end
return {} -- discard this frame
end
end
end
end
function device_new(device)
local info = device:info()
if info.vid == KEYBOARD_VID and info.pid == KEYBOARD_PID then
device:connect("evdev-frame", frame)
end
end
libinput:connect("new-evdev-device", device_new)

View file

@ -0,0 +1,64 @@
-- SPDX-License-Identifier: MIT
--
-- This is an example libinput plugin
--
-- This plugin delays any event with relative motion by the given DELAY
-- by storing it in a table and replaying it via a timer callback later.
-- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN
-- libinput:register({1})
DELAY = 1500 * 1000 -- 1.5s
next_timer_expiry = 0
devices = {}
function timer_expired(time_in_microseconds)
next_timer_expiry = 0
for device, frames in pairs(devices) do
while #frames > 0 and frames[1].time <= time_in_microseconds do
--- we don't have a current frame so it doesn't matter
--- whether we prepend or append
device:prepend_frame(frames[1].frame)
table.remove(frames, 1)
end
local next_frame = frames[1]
if next_frame and (next_timer_expiry == 0 or next_frame.time < next_timer_expiry) then
next_timer_expiry = next_frame.time
end
end
if next_timer_expiry ~= 0 then
libinput:timer_set_absolute(next_timer_expiry)
end
end
function frame(device, frame, timestamp)
for _, v in ipairs(frame) do
if v.usage == evdev.REL_X or v.usage == evdev.REL_Y then
local next_time = timestamp + DELAY
table.insert(devices[device], {
time = next_time,
frame = frame
})
if next_timer_expiry == 0 then
next_timer_expiry = next_time
libinput:timer_set_absolute(next_timer_expiry)
end
return {} -- discard frame
end
end
return nil
end
function device_new(device)
local usages = device:usages()
if usages[evdev.REL_X] then
devices[device] = {}
device:connect("evdev-frame", frame)
device:connect("device-removed", function(dev)
devices[dev] = nil
end)
end
end
libinput:connect("new-evdev-device", device_new)
libinput:connect("timer-expired", timer_expired)

View file

@ -0,0 +1,16 @@
-- SPDX-License-Identifier: MIT
--
-- An example plugin to show how to disable an internal feature.
--
-- Typically one would expect the plugin to re-implement the feature
-- in a more device-specific manner but that's not done here.
-- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN
-- libinput:register({1})
libinput:connect("new-evdev-device", function(device)
local udev_info = device:udev_properties()
if udev_info["ID_INPUT_TOUCHPAD"] then
libinput:log_info("Disabling palm detection on " .. device:name())
device:disable_feature("touchpad-palm-detection")
end
end)

40
plugins/10-dwt.lua Normal file
View file

@ -0,0 +1,40 @@
-- SPDX-License-Identifier: MIT
--
-- This plugin implements a very simple version of disable-while-typing.
-- It monitors all keyboard devices and if any of them send an event,
-- any touchpad device is disabled for 2 seconds.
-- And "disabled" means any event from that touchpad is simply
-- discarded.
--
-- Install this file in /etc/libinput/plugins and
--
-- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN
-- libinput:register({1})
tp_enabled = true
libinput:connect("timer-expired", function(now)
libinput:log_debug("touchpad enabled")
tp_enabled = true
end)
libinput:connect("new-evdev-device", function (device)
local props = device:udev_properties()
if props.ID_INPUT_KEYBOARD then
device:connect("evdev-frame", function (device, frame, timestamp)
libinput:timer_set_relative(2000000)
if tp_enabled then
libinput:log_debug("touchpad disabled")
tp_enabled = false
end
end)
elseif props.ID_INPUT_TOUCHPAD then
libinput:log_debug("Touchpad detected: " .. device:name())
device:connect("evdev-frame", function (device, frame, timestamp)
if not tp_enabled then
-- Returning an empty table discards the event.
return {}
end
end)
end
end)

87
plugins/10-example.lua Normal file
View file

@ -0,0 +1,87 @@
-- SPDX-License-Identifier: MIT
--
-- This is an example libinput plugin
--
-- This plugin swaps left and right buttons on any device that has both buttons.
-- Let's create a plugin. A single Lua script may create more than one
-- plugin instance but that's a bit of a nice use-case. Most scripts
-- should be a single plugin.
-- A plugin needs to be registered to activate. If it isn't, it is
-- cleaned up immediately. In the register call we supply
-- the list of plugin versions we support. Currently we only
-- have version 1.
--
-- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN
-- libinput:register({1})
-- Note to the reader: it will be easier to understand this example
-- if you read it bottom-up from here on.
-- The callback for our "evdev-frame" signal.
-- These frames are sent *before* libinput gets to handle them so
-- any modifications will affect libinput.
function frame(device, frame, time_in_microseconds)
-- Frame is a table in the form
-- { { usage: 123, value: 3 }, ... }
-- Let's use the evdev module to make it more readable, evdev.KEY_A
-- is simply the value (0x1 << 16) | 0x1 (see linux/input-event-codes.h)
for _, v in ipairs(frame) do
-- If we get a right button event, change it to left, and
-- vice versa. Because this happens before libinput (or the next
-- plugin in the precedence order) sees the
-- frame it doesn't know that the swap happened.
if v.usage == evdev.BTN_RIGHT then
v.usage = evdev.BTN_LEFT
elseif v.usage == evdev.BTN_LEFT then
v.usage = evdev.BTN_RIGHT
end
end
-- We changed the frame, let's return it. If we return nil
-- the original frame is being used as-is.
return frame
end
-- This is a timer callback. It is invoked after one second
-- (see below) but only once - re-set the timer so
-- it goes off every second.
function timer_expired(time_in_microseconds)
libinput:timer_set_absolute(time_in_microseconds + 1000000)
end
-- Callback for the "new-evdev-device" signal, see below
-- The argument is the EvdevDevice object, see the documentation.
function device_new(device)
-- A table of evdev usages available on our device.
-- Using the evdev module makes it more readable but you can
-- use numbers (which is what evdev.EV_KEY and
-- friends resolve to anyway).
local usages = device:usages()
if usages[evdev.BTN_LEFT] and usages[evdev.BTN_RIGHT] then
-- The "evdev-frame" callback is invoked whenever the device
-- provided us with one evdev frame, i.e. a bunch of events up
-- to excluding EV_SYN SYN_REPORT.
device:connect("evdev-frame", frame)
end
-- The device has udev information, let's print it out. Right
-- now all we get are the ID_INPUT_ bits.
-- If this is empty we know libinput will ignore this device anyway
local udev_info = device:udev_properties()
for k, v in pairs(udev_info) do
libinput:log_debug(k .. "=" .. v)
end
end
-- Let's connect to the "new-evdev-device" signal. This function
-- is invoked when libinput detects a new evdev device (but before
-- that device is actually available to libinput as libinput device).
-- This allows us to e.g. change properties on the device.
libinput:connect("new-evdev-device", device_new)
-- Set our timer to expire 1s from now (in microseconds).
-- Timers are absolute, so they need to be added to the
-- current time
libinput:connect("timer-expired", timer_expired)
libinput:timer_set_relative(1000000)

View file

@ -0,0 +1,32 @@
-- SPDX-License-Identifier: MIT
--
-- This plugin inverts the horizontal scroll direction of
-- the Logitech MX Master mouse. OOTB the mouse scrolls
-- in the opposite direction to all other mice out there.
--
-- This plugin is only needed when the mouse is connected
-- to the Logitech Bolt receiver - on that receiver we cannot
-- tell which device is connected.
--
-- For the Logitech Unifying receiver and Bluetooth please
-- add the ModelInvertHorizontalScrolling=1 quirk
-- in quirks/30-vendor-logitech.quirks.
--
--
-- Install this file in /etc/libinput/plugins and
--
-- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN
-- libinput:register({1})
libinput:connect("new-evdev-device", function (device)
local info = device:info()
if info.vid == 0x046D and info.pid == 0xC548 then
device:connect("evdev-frame", function (device, frame, timestamp)
for _, event in ipairs(frame) do
if event.usage == evdev.REL_HWHEEL or event.usage == evdev.REL_HWHEEL_HI_RES then
event.value = -event.value
end
end
return frame
end)
end
end)

View file

@ -0,0 +1,22 @@
-- SPDX-License-Identifier: MIT
--
-- An example plugin to make the pointer go three times as fast
--
-- Install this file in /etc/libinput/plugins and
--
-- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN
-- libinput:register({1})
libinput:connect("new-evdev-device", function(device)
local usages = device:usages()
if usages[evdev.REL_X] then
device:connect("evdev-frame", function(device, frame, timestamp)
for _, v in ipairs(frame) do
if v.usage == evdev.REL_X or v.usage == evdev.REL_Y then
-- Multiply the relative motion by 3
v.value = v.value * 3
end
end
return frame
end)
end
end)

View file

@ -0,0 +1,56 @@
-- SPDX-License-Identifier: MIT
--
-- An example plugin to make the pointer go three times as slow
--
-- Install this file in /etc/libinput/plugins and
--
-- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN
-- libinput:register({1})
remainders = {}
function split(v)
if math.abs(v) >= 1.0 then
local i = math.floor(math.abs(v))
local r = math.abs(v) % 1.0
if v < 0.0 then
i = -i
r = -r
end
return i, r
else
return 0, v
end
end
function decelerate(device, x, y)
local remainder = remainders[device]
local rx, ry = 0, 0
if x ~= 0.0 then
rx, remainder.x = split(remainder.x + x/3.0)
end
if y ~= 0.0 then
ry, remainder.y = split(remainder.y + y/3.0)
end
return rx, ry
end
libinput:connect("new-evdev-device", function(device)
local usages = device:usages()
if usages[evdev.REL_X] then
remainders[device] = { x = 0.0, y = 0.0 }
device:connect("evdev-frame", function(device, frame, timestamp)
for _, v in ipairs(frame) do
if v.usage == evdev.REL_X then
v.value, _ = decelerate(device, v.value, 0.0)
elseif v.usage == evdev.REL_Y then
_, v.value = decelerate(device, 0.0, v.value)
end
end
return frame
end)
device:connect("device-removed", function(dev)
remainders[dev] = nil
end)
end
end)

View file

@ -0,0 +1,64 @@
-- SPDX-License-Identifier: MIT
--
-- This is an example libinput plugin
--
-- This plugin maps a downwards mouse wheel to a button down event and
-- an upwards wheel movement to a button up event.
-- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN
-- libinput:register({1})
-- The button we want to press on wheel events
local wheel_button = evdev.BTN_EXTRA
local button_states = {}
local function evdev_frame(device, frame, timestamp)
local events = {}
local modified = false
for _, v in ipairs(frame) do
if v.usage == evdev.REL_WHEEL then
-- REL_WHEEL is inverted, neg value -> down, pos value -> up
if v.value < 0 then
if not button_states[device] then
table.insert(events, { usage = wheel_button, value = 1 })
button_states[device] = true
end
else
if button_states[device] then
table.insert(events, { usage = wheel_button, value = 0 })
button_states[device] = false
end
end
modified = true
-- Because REL_WHEEL is no longer a wheel, the high-res
-- events are dropped
elseif v.usage == evdev.REL_WHEEL_HI_RES then
modified = true
else
table.insert(events, v)
end
end
if modified then
return events
else
return nil
end
end
local function device_new(device)
local usages = device:usages()
if usages[evdev.REL_WHEEL] then
button_states[device] = false
if not usages[wheel_button] then
device:enable_evdev_usage(wheel_button)
end
device:connect("evdev-frame", evdev_frame)
device:connect("device-removed", function(dev)
button_states[dev] = nil
end)
end
end
libinput:connect("new-evdev-device", device_new)

16
plugins/meson.build Normal file
View file

@ -0,0 +1,16 @@
plugins = [
'10-example.lua',
'10-dwt.lua',
'10-logitech-mx-master-horiz-scroll.lua',
'10-pointer-go-faster.lua',
'10-pointer-go-slower.lua',
'10-delay-motion.lua',
'10-disable-feature.lua',
'10-copilot-key-override.lua',
'10-wheel-to-button.lua',
]
fs = import('fs')
foreach plugin : plugins
fs.copyfile(plugin)
endforeach

View file

@ -82,37 +82,45 @@ ModelInvertHorizontalScrolling=1
MatchVendor=0x046D
MatchProduct=0x4069
ModelInvertHorizontalScrolling=1
ModelScrollOnMiddleClick=1
# MX Master 2S has a different PID on bluetooth
[Logitech MX Master 2S Bluetooth]
MatchVendor=0x046D
MatchProduct=0xB019
ModelInvertHorizontalScrolling=1
ModelScrollOnMiddleClick=1
[Logitech MX Master 3 USB]
MatchVendor=0x046D
MatchProduct=0x4082
ModelInvertHorizontalScrolling=1
ModelLogitechMXMaster3=1
ModelScrollOnMiddleClick=1
# MX Master 3 has a different PID on bluetooth
[Logitech MX Master 3 Bluetooth]
MatchVendor=0x046D
MatchProduct=0xB023
ModelInvertHorizontalScrolling=1
ModelLogitechMXMaster3=1
ModelScrollOnMiddleClick=1
[Logitech MX Master 3S]
MatchVendor=0x046D
MatchProduct=0xB034
ModelInvertHorizontalScrolling=1
ModelLogitechMXMaster3=1
ModelScrollOnMiddleClick=1
[Logitech MX Master 3B]
MatchVendor=0x046D
MatchProduct=0xB028
ModelInvertHorizontalScrolling=1
ModelLogitechMXMaster3=1
ModelScrollOnMiddleClick=1
[Logitech MX Master 4]
MatchVendor=0x046D
MatchProduct=0xB042
ModelInvertHorizontalScrolling=1
ModelScrollOnMiddleClick=1
# Don't add quirks for the Logitech Bolt Receiver:
# MatchVendor=0x046D

View file

@ -19,6 +19,7 @@ AttrKeyboardIntegration=internal
MatchName=*Microsoft Surface *Cover*
MatchDMIModalias=dmi:*svnMicrosoftCorporation:*
AttrKeyboardIntegration=internal
AttrEventCode=-BTN_0;
[Microsoft Surface Laptop Studio Touchpad]
MatchVendor=0x045E

View file

@ -0,0 +1,9 @@
# Nulea M501 does not name itself as a "Trackball"
# In USB mode, it shares a generic dongle and name with other non-trackball
# mice. As such, trackball quirks are enabled only for bluetooth modes.
[Nulea M501 Bluetooth Trackball]
MatchBus=bluetooth
MatchVendor=0x000E
MatchProduct=0x3412
ModelTrackball=1

View file

@ -1,3 +1,3 @@
[VirtualBox mouse integration]
MatchName=*VirtualBox mouse integration*
ModelBouncingKeys=1
AttrIsVirtual=1

View file

@ -1,3 +1,3 @@
[QEMU/KVM mouse integration]
MatchName=*spice vdagent tablet*
ModelBouncingKeys=1
AttrIsVirtual=1

View file

@ -309,3 +309,24 @@ MatchBus=usb
MatchVendor=0x1532
MatchProduct=0x02B8
AttrKeyboardIntegration=internal
[RazerBlade142025 Keyboard]
MatchUdevType=keyboard
MatchBus=usb
MatchVendor=0x1532
MatchProduct=0x02C5
AttrKeyboardIntegration=internal
[RazerBlade162025 Keyboard]
MatchUdevType=keyboard
MatchBus=usb
MatchVendor=0x1532
MatchProduct=0x02C6
AttrKeyboardIntegration=internal
[RazerBlade182025 Keyboard]
MatchUdevType=keyboard
MatchBus=usb
MatchVendor=0x1532
MatchProduct=0x02C7
AttrKeyboardIntegration=internal

View file

@ -7,6 +7,14 @@ MatchVendor=0x0002
MatchProduct=0x0007
ModelSynapticsSerialTouchpad=1
# "SYNA3580:00 06CB:CFD2 Touchpad": pressure touchpad mostly used in HP laptops.
[Synaptics 06CB:CFD2 Touchpad]
MatchBus=i2c
MatchVendor=0x06CB
MatchProduct=0xCFD2
MatchUdevType=touchpad
AttrInputProp=+INPUT_PROP_PRESSUREPAD
# SYNA3602:00 0911:5288 touchpad, clickpad pretending it has a right button.
# Integrated into several systems, including
# Purism Librem 14v1

View file

@ -2,8 +2,8 @@
[VMWare Virtual PS/2 Mouse]
MatchName=*VirtualPS/2 VMware VMMouse*
ModelBouncingKeys=1
AttrIsVirtual=1
[VMware VMware Virtual USB Mouse]
MatchName=*VMware VMware Virtual USB Mouse*
ModelBouncingKeys=1
AttrIsVirtual=1

View file

@ -47,3 +47,11 @@ AttrKeyboardIntegration=internal
MatchName=AT Translated Set 2 keyboard
MatchDMIModalias=dmi:*svnASUSTeKCOMPUTERINC.:pnROGFlowZ13GZ302EA_GZ302EA:*
ModelTabletModeNoSuspend=1
# enable "disable touchpad while typing" to work with z13 keyboard
[Asus ROG FLow Z13 2025 keyboard]
MatchUdevType=keyboard
MatchBus=usb
MatchVendor=0x0B05
MatchProduct=0x1A30
AttrKeyboardIntegration=internal

View file

@ -89,6 +89,16 @@ MatchName=*AT Translated Set 2 keyboard*
MatchDMIModalias=dmi:*:svnDellInc.:pnLatitude7275:*
ModelTabletModeNoSuspend=1
[Latitude 7285]
MatchName=*AT Translated Set 2 keyboard*
MatchDMIModalias=dmi:*:svnDellInc.:pnLatitude7285:*
ModelTabletModeNoSuspend=1
[Dell Pro Rugged Tablet]
MatchName=*AT Translated Set 2 keyboard*
MatchDMIModalias=dmi:*:svnDellInc.:pnDellProRugged*TabletRA*:*
ModelTabletModeNoSuspend=1
[Latitude 7480 Touchpad]
MatchName=DLL07A0*
MatchDMIModalias=dmi:**bvnDellInc.:*:pnLatitude7480*
@ -126,13 +136,13 @@ ModelTabletModeNoSuspend=1
MatchBus=i2c
MatchVendor=0x27C6
MatchProduct=0x0F60
ModelPressurePad=1
AttrInputProp=+INPUT_PROP_PRESSUREPAD
[Dell Mayabay Touchpad]
MatchBus=i2c
MatchVendor=0x06CB
MatchProduct=0xCFA0
ModelPressurePad=1
AttrInputProp=+INPUT_PROP_PRESSUREPAD
[Dell Precision 5480]
MatchBus=i2c
@ -146,22 +156,58 @@ ModelTouchpadVisibleMarker=0
MatchBus=i2c
MatchVendor=0x06CB
MatchProduct=0xCFF8
ModelPressurePad=1
AttrInputProp=+INPUT_PROP_PRESSUREPAD
[Dell laptop 16 Synaptics touchpad]
MatchBus=i2c
MatchVendor=0x06CB
MatchProduct=0xCFF9
ModelPressurePad=1
AttrInputProp=+INPUT_PROP_PRESSUREPAD
[Dell laptop 14 Goodix touchpad]
MatchBus=i2c
MatchVendor=0x27C6
MatchProduct=0x0F61
ModelPressurePad=1
AttrInputProp=+INPUT_PROP_PRESSUREPAD
[Dell laptop 16 Goodix touchpad]
MatchBus=i2c
MatchVendor=0x27C6
MatchProduct=0x0F62
ModelPressurePad=1
AttrInputProp=+INPUT_PROP_PRESSUREPAD
[Dell 14 Premium DA14250 touchpad]
MatchBus=i2c
MatchVendor=0x0488
MatchProduct=0x108C
AttrInputProp=+INPUT_PROP_PRESSUREPAD
[Dell 16 Premium DA16250 touchpad]
MatchBus=i2c
MatchVendor=0x0488
MatchProduct=0x108D
AttrInputProp=+INPUT_PROP_PRESSUREPAD
[Dell laptop 14_1 Synaptics touchpad]
MatchBus=i2c
MatchVendor=0x06CB
MatchProduct=0xD01D
AttrInputProp=+INPUT_PROP_PRESSUREPAD
[Dell laptop 16_1 Synaptics touchpad]
MatchBus=i2c
MatchVendor=0x06CB
MatchProduct=0xD01A
AttrInputProp=+INPUT_PROP_PRESSUREPAD
[Dell laptop 14 Sensel touchpad]
MatchBus=i2c
MatchVendor=0x2C2F
MatchProduct=0x0034
AttrInputProp=+INPUT_PROP_PRESSUREPAD
[Dell laptop 16 Sensel touchpad]
MatchBus=i2c
MatchVendor=0x2C2F
MatchProduct=0x0033
AttrInputProp=+INPUT_PROP_PRESSUREPAD

View file

@ -178,3 +178,17 @@ ModelChromebook=1
AttrPressureRange=0:-2
AttrThumbPressureThreshold=45
AttrPalmPressureThreshold=0
[Google Chromebook Roric]
MatchUdevType=touchpad
MatchName=PNP0C50:00 093A:3307 Touchpad
MatchDMIModalias=dmi:*svnGoogle:pnRoric*
ModelChromebook=1
AttrPressureRange=20:10
[Google Chromebook Rull]
MatchUdevType=touchpad
MatchName=PNP0C50:00 093A:3307 Touchpad
MatchDMIModalias=dmi:*svnGoogle:pnRull*
ModelChromebook=1
AttrPressureRange=20:10

View file

@ -3,3 +3,9 @@ MatchName=HTIX5288:00 093A:0255 Touchpad
MatchUdevType=touchpad
MatchDMIModalias=dmi:*svnGPD:*pnG1619-*
AttrEventCode=-BTN_RIGHT
[GPD MicroPC 2 Touchpad]
MatchName=ALPS0001:00 36B6:C001 Touchpad
MatchUdevType=touchpad
MatchDMIModalias=dmi:*svnGPD:pnG1688-08:*
AttrInputProp=-INPUT_PROP_BUTTONPAD

View file

@ -1,8 +1,8 @@
# Clickpad that announces BTN_RIGHT
# requires ModelPressurePad=1
# requires AttrInputProp=+INPUT_PROP_PRESSUREPAD
[HONOR MagicBook Art 14]
MatchName=*TOPS0102*
MatchDMIModalias=dmi:*svnHONOR:*pnMRA-XXX*
MatchUdevType=touchpad
AttrEventCode=-BTN_RIGHT
ModelPressurePad=1
AttrInputProp=+INPUT_PROP_PRESSUREPAD

View file

@ -1,5 +1,10 @@
# Do not edit this file, it will be overwritten on update
#
[HP Chromebook 14]
MatchName=*Cypress APA Trackpad *cyapa*
MatchDMIModalias=dmi:*svnHewlett-Packard*:pnFalco*
AttrPressureRange=12:8
# Claims to have double/tripletap but doesn't actually send it
# https://bugs.freedesktop.org/show_bug.cgi?id=98538
[HP Compaq 6910p]
@ -14,43 +19,59 @@ MatchName=*SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*svnHewlett-Packard:*pnHPCompaq8510w*
AttrEventCode=-BTN_TOOL_DOUBLETAP;-BTN_TOOL_TRIPLETAP;
[HP Elite x2 1013 G3 Keyboard]
MatchUdevType=keyboard
MatchBus=ps2
MatchDMIModalias=dmi:*svnHP:pnHPElitex21013G3:*
AttrKeyboardIntegration=external
[HP Elite x2 1013 G3 Tablet Mode Switch]
MatchName=*Intel Virtual Button*
MatchDMIModalias=dmi:*svnHP:pnHPElitex21013G3:*
ModelTabletModeSwitchUnreliable=1
[HP Elite x2 1013 G3 Touchpad]
MatchUdevType=touchpad
MatchBus=usb
MatchVendor=0x044E
MatchProduct=0x1221
AttrTPKComboLayout=below
# The HP OmniBook Ultra Flip Laptop 14-fh0xxx's custom Intel ISH firmware
# filters out events from its builtin keyboard and touchpad when the hinge is
# opened little more than 180 degrees but toggles tablet-mode when it's opened
# little less than 180 degrees.
# Do not suspend the keyboard and touchpad to let use the device in flat
# position and also give consistency with some keyboard keys controlled by the
# Video Bus device (brightness down/up), the HP WMI hotkeys device (mic mute and
# hp hubs launcher key) and the backlight getting on and off by the firmware at
# the same time it enables disables the input.
# This one is for the keyboard and...
[HP OmniBook Ultra Flip Laptop 14-fh0xxx Keyboard]
MatchBus=ps2
MatchUdevType=keyboard
MatchDMIModalias=dmi:*:svnHP:pnHPOmniBookUltraFlipLaptop14-fh0xxx:*
ModelTabletModeNoSuspend=1
# ...this one is for the touchpad.
[HP OmniBook Ultra Flip Laptop 14-fh0xxx Touchpad]
MatchBus=i2c
MatchVendor=0x06CB
MatchProduct=0xCFD2
MatchUdevType=touchpad
MatchDMIModalias=dmi:*:svnHP:pnHPOmniBookUltraFlipLaptop14-fh0xxx:*
ModelTabletModeNoSuspend=1
[HP Pavilion dmi4]
MatchName=*SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*svnHewlett-Packard:*pnHPPaviliondm4NotebookPC*
ModelHPPavilionDM4Touchpad=1
# Touchpad is a clickpad but INPUT_PROP_BUTTONPAD is not set, see
# https://bugs.freedesktop.org/show_bug.cgi?id=97147
[HP Stream 11]
MatchName=SYN1EDE:00 06CB:7442*
MatchDMIModalias=dmi:*svnHewlett-Packard:pnHPStreamNotebookPC11*
AttrInputProp=+INPUT_PROP_BUTTONPAD
# The HP stream x360's embedded-controller filters out events form its builtin
# keyboard when in tablet-mode itself; and it has a capacitive home-button
# (windows logo) underneath its display which also sends PS/2 key-events.
# Do not suspend the keyboard when in tablet-mode so that the home button
# keeps working when in tablet-mode.
[HP Stream x360 11]
MatchName=AT Translated Set 2 keyboard
MatchDMIModalias=dmi:*:svnHewlett-Packard:pnHPStreamx360ConvertiblePC11:*
ModelTabletModeNoSuspend=1
[HP Spectre x360 Convertible 15-bl000]
MatchName=AT Translated Set 2 keyboard
MatchDMIModalias=dmi:*:svnHP:pnHPSpectrex360Convertible15-bl0XX:*
ModelTabletModeNoSuspend=1
[HP ZBook Studio G3]
MatchName=AlpsPS/2 ALPS GlidePoint
MatchDMIModalias=dmi:*svnHP:pnHPZBookStudioG3:*
ModelHPZBookStudioG3=1
[HP Chromebook 14]
MatchName=*Cypress APA Trackpad *cyapa*
MatchDMIModalias=dmi:*svnHewlett-Packard*:pnFalco*
AttrPressureRange=12:8
[HP Spectre x360 Convertible 15-bl1xx]
MatchUdevType=touchpad
MatchName=*SynPS/2 Synaptics TouchPad
@ -75,26 +96,24 @@ MatchName=AT Translated Set 2 keyboard
MatchDMIModalias=dmi:*svnHP:pnHPSpectrex360Convertible15-ch0xx:*
ModelTabletModeNoSuspend=1
[HP Elite x2 1013 G3 Tablet Mode Switch]
MatchName=*Intel Virtual Button*
MatchDMIModalias=dmi:*svnHP:pnHPElitex21013G3:*
ModelTabletModeSwitchUnreliable=1
# Touchpad is a clickpad but INPUT_PROP_BUTTONPAD is not set, see
# https://bugs.freedesktop.org/show_bug.cgi?id=97147
[HP Stream 11]
MatchName=SYN1EDE:00 06CB:7442*
MatchDMIModalias=dmi:*svnHewlett-Packard:pnHPStreamNotebookPC11*
AttrInputProp=+INPUT_PROP_BUTTONPAD
[HP Elite x2 1013 G3 Touchpad]
MatchUdevType=touchpad
MatchBus=usb
MatchVendor=0x044E
MatchProduct=0x1221
AttrTPKComboLayout=below
# The HP stream x360's embedded-controller filters out events form its builtin
# keyboard when in tablet-mode itself; and it has a capacitive home-button
# (windows logo) underneath its display which also sends PS/2 key-events.
# Do not suspend the keyboard when in tablet-mode so that the home button
# keeps working when in tablet-mode.
[HP Stream x360 11]
MatchName=AT Translated Set 2 keyboard
MatchDMIModalias=dmi:*:svnHewlett-Packard:pnHPStreamx360ConvertiblePC11:*
ModelTabletModeNoSuspend=1
[HP Elite x2 1013 G3 Keyboard]
MatchUdevType=keyboard
MatchBus=ps2
MatchDMIModalias=dmi:*svnHP:pnHPElitex21013G3:*
AttrKeyboardIntegration=external
[HP Haptics Touchpad CFD2]
MatchBus=i2c
MatchVendor=0x06CB
MatchProduct=0xCFD2
ModelPressurePad=1
[HP ZBook Studio G3]
MatchName=AlpsPS/2 ALPS GlidePoint
MatchDMIModalias=dmi:*svnHP:pnHPZBookStudioG3:*
ModelHPZBookStudioG3=1

View file

@ -12,10 +12,10 @@ MatchUdevType=touchpad
MatchDMIModalias=dmi:*svnHUAWEI:*pnMRGF-XX*
AttrEventCode=-BTN_RIGHT
# 2024 model requires ModelPressurePad=1
# 2024 model requires AttrInputProp=+INPUT_PROP_PRESSUREPAD
[Huawei MateBook X Pro 2024 Touchpad]
MatchName=GXTP7863:00 27C6:01E0 Touchpad
MatchUdevType=touchpad
MatchDMIModalias=dmi:*svnHUAWEI:*pnVGHH-XX*
AttrEventCode=-BTN_RIGHT
ModelPressurePad=1
AttrInputProp=+INPUT_PROP_PRESSUREPAD

View file

@ -162,7 +162,7 @@ AttrTrackpointMultiplier=1.25
MatchBus=i2c
MatchVendor=0x06CB
MatchProduct=0xCE37
ModelPressurePad=1
AttrInputProp=+INPUT_PROP_PRESSUREPAD
[Lenovo Yoga C930 Tablet]
MatchBus=i2c
@ -317,7 +317,7 @@ AttrKeyboardIntegration=internal
MatchBus=i2c
MatchVendor=0x27C6
MatchProduct=0x01E8
ModelPressurePad=1
AttrInputProp=+INPUT_PROP_PRESSUREPAD
# Duet 7i tablet switch activated by folding keyboard cover, or removing it.
# We must not disable volume rocker 'keyboard'.
@ -360,6 +360,12 @@ MatchDMIModalias=dmi:*svnLENOVO:*:pvrThinkPadE14Gen2*
AttrPalmPressureThreshold=300
AttrPressureRange=10:8
[Lenovo ThinkPad E16 Gen 3]
MatchUdevType=pointingstick
MatchName=*TPPS/2 Elan TrackPoint*
MatchDMIModalias=dmi:*svnLENOVO:*:pvrThinkPadE16Gen3*
AttrTrackpointMultiplier=0.5
# White Backlit Keyboard
[Lenovo Ideapad Gaming 3]
MatchUdevType=keyboard
@ -388,14 +394,14 @@ AttrKeyboardIntegration=internal
MatchName=*GXTP5100*
MatchDMIModalias=dmi:*svnLENOVO:*pvrThinkBook*G7+ASP*:*
MatchUdevType=touchpad
ModelPressurePad=1
AttrInputProp=+INPUT_PROP_PRESSUREPAD
# The ThinkBook 14/16 G6+ IMH also has a similar issue as the G7+ mentioned above.
[Lenovo ThinkBook G6+ IMH]
MatchName=*GXTP5100*
MatchDMIModalias=dmi:*svnLENOVO:*pvrThinkBook*G6+IMH*:*
MatchUdevType=touchpad
ModelPressurePad=1
AttrInputProp=+INPUT_PROP_PRESSUREPAD
# Lenovo Yoga Slim 7i Carbon sends bogus ABS_MT_TOOL_TYPE MT_TOOL_PALM events
[Lenovo Yoga Slim 7i Carbon]
@ -409,11 +415,11 @@ AttrEventCode=-ABS_MT_TOOL_TYPE
MatchName=*GXTP5100*
MatchDMIModalias=dmi:*svnLENOVO:*pvrThinkPadX9-15Gen1*:*
MatchUdevType=touchpad
ModelPressurePad=1
AttrInputProp=+INPUT_PROP_PRESSUREPAD
# The ThinkBook 14/16 G7+ IAH also has a similar issue as the ASP mentioned above.
[Lenovo ThinkBook G7+ IAH touchpad]
MatchName=*GXTP5100*
MatchDMIModalias=dmi:*svnLENOVO:*pvrThinkBook*G7+IAH*:*
MatchUdevType=touchpad
ModelPressurePad=1
AttrInputProp=+INPUT_PROP_PRESSUREPAD

View file

@ -0,0 +1,5 @@
[TongFang GX4 (X4SP4NAL) Touchpad]
MatchName=UNIW0001:00 093A:0255 Touchpad
MatchUdevType=touchpad
MatchDMIModalias=dmi:*svnAiStone:pnX4SP4NAL:*
AttrEventCode=-BTN_RIGHT

View file

@ -33,7 +33,7 @@
static void
fallback_keyboard_notify_key(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time,
usec_t time,
evdev_usage_t usage,
enum libinput_key_state state)
{
@ -53,7 +53,7 @@ fallback_keyboard_notify_key(struct fallback_dispatch *dispatch,
static void
fallback_lid_notify_toggle(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
usec_t time)
{
if (dispatch->lid.is_closed ^ dispatch->lid.is_closed_client_state) {
switch_notify_toggle(&device->base,
@ -67,7 +67,7 @@ fallback_lid_notify_toggle(struct fallback_dispatch *dispatch,
void
fallback_notify_physical_button(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time,
usec_t time,
evdev_usage_t button,
enum libinput_button_state state)
{
@ -82,20 +82,23 @@ fallback_interface_get_switch_state(struct evdev_dispatch *evdev_dispatch,
switch (sw) {
case LIBINPUT_SWITCH_TABLET_MODE:
return dispatch->tablet_mode.sw.state ? LIBINPUT_SWITCH_STATE_ON
: LIBINPUT_SWITCH_STATE_OFF;
break;
case LIBINPUT_SWITCH_KEYPAD_SLIDE:
return dispatch->keypad_slide.sw.state ? LIBINPUT_SWITCH_STATE_ON
: LIBINPUT_SWITCH_STATE_OFF;
break;
default:
/* Internal function only, so we can abort here */
abort();
}
return dispatch->tablet_mode.sw.state ? LIBINPUT_SWITCH_STATE_ON
: LIBINPUT_SWITCH_STATE_OFF;
}
static inline bool
post_button_scroll(struct evdev_device *device,
struct device_float_coords raw,
uint64_t time)
usec_t time)
{
if (device->scroll.method != LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN)
return false;
@ -117,7 +120,8 @@ post_button_scroll(struct evdev_device *device,
filter_dispatch_scroll(device->pointer.filter,
&raw,
device,
time);
time,
FILTER_SCROLL_TYPE_CONTINUOUS);
evdev_post_scroll(device,
time,
LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS,
@ -170,7 +174,7 @@ fallback_rotate_relative(struct fallback_dispatch *dispatch,
static void
fallback_flush_relative_motion(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
usec_t time)
{
struct libinput_device *base = &device->base;
struct normalized_coords accel;
@ -204,12 +208,8 @@ fallback_flush_relative_motion(struct fallback_dispatch *dispatch,
static void
fallback_flush_wheels(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
usec_t time)
{
struct normalized_coords wheel_degrees = { 0.0, 0.0 };
struct discrete_coords discrete = { 0.0, 0.0 };
struct wheel_v120 v120 = { 0.0, 0.0 };
if (!libinput_device_has_capability(&device->base, LIBINPUT_DEVICE_CAP_POINTER))
return;
@ -225,7 +225,8 @@ fallback_flush_wheels(struct fallback_dispatch *dispatch,
filter_dispatch_scroll(device->pointer.filter,
&raw,
device,
time);
time,
FILTER_SCROLL_TYPE_WHEEL);
evdev_post_scroll(device,
time,
LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS,
@ -238,66 +239,93 @@ fallback_flush_wheels(struct fallback_dispatch *dispatch,
return;
}
if (dispatch->wheel.hi_res.y != 0) {
int value = dispatch->wheel.hi_res.y;
v120.y = -1 * value;
wheel_degrees.y =
-1 * value / 120.0 * device->scroll.wheel_click_angle.y;
evdev_notify_axis_wheel(device,
/* High-resolution wheel events */
if (dispatch->wheel.hi_res.x != 0 || dispatch->wheel.hi_res.y != 0) {
const struct device_float_coords v120_unaccelerated = {
.x = dispatch->wheel.hi_res.x,
.y = -1 * dispatch->wheel.hi_res.y,
};
const struct normalized_coords v120_accelerated =
device->pointer.filter
? filter_dispatch_scroll(device->pointer.filter,
&v120_unaccelerated,
device,
time,
FILTER_SCROLL_TYPE_WHEEL)
: (const struct normalized_coords){
.x = v120_unaccelerated.x,
.y = v120_unaccelerated.y
};
/* Truncate the fractional part when converting floating-point to
integer. This is acceptable because the v120 unit maps one logical
click to 120 units; values are effectively measured in 1/120 of a
click, so truncation does not lose meaningful resolution. */
const struct wheel_v120 v120 = {
.x = v120_accelerated.x,
.y = v120_accelerated.y,
};
const struct normalized_coords wheel_degrees = {
.x = v120.x / 120.0 * device->scroll.wheel_click_angle.x,
.y = v120.y / 120.0 * device->scroll.wheel_click_angle.y,
};
if (v120.x != 0) {
evdev_notify_axis_wheel(
device,
time,
bit(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL),
&wheel_degrees,
&v120);
}
if (v120.y != 0) {
evdev_notify_axis_wheel(
device,
time,
bit(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
&wheel_degrees,
&v120);
}
dispatch->wheel.hi_res.x = 0;
dispatch->wheel.hi_res.y = 0;
}
if (dispatch->wheel.lo_res.y != 0) {
int value = dispatch->wheel.lo_res.y;
wheel_degrees.y = -1 * value * device->scroll.wheel_click_angle.y;
discrete.y = -1 * value;
/* Low-resolution wheel events */
if (dispatch->wheel.lo_res.x != 0 || dispatch->wheel.lo_res.y != 0) {
/* Do not accelerate low-resolution wheel events: they use different
units than high-resolution events and should not be accelerated with
the same function. */
const struct discrete_coords discrete = {
.x = dispatch->wheel.lo_res.x,
.y = -1 * dispatch->wheel.lo_res.y,
};
const struct normalized_coords wheel_degrees = {
.x = discrete.x * device->scroll.wheel_click_angle.x,
.y = discrete.y * device->scroll.wheel_click_angle.y,
};
if (discrete.x != 0) {
evdev_notify_axis_legacy_wheel(
device,
time,
bit(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL),
&wheel_degrees,
&discrete);
}
if (discrete.y != 0) {
evdev_notify_axis_legacy_wheel(
device,
time,
bit(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
&wheel_degrees,
&discrete);
dispatch->wheel.lo_res.y = 0;
}
if (dispatch->wheel.hi_res.x != 0) {
int value = dispatch->wheel.hi_res.x;
v120.x = value;
wheel_degrees.x = value / 120.0 * device->scroll.wheel_click_angle.x;
evdev_notify_axis_wheel(device,
time,
bit(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL),
&wheel_degrees,
&v120);
dispatch->wheel.hi_res.x = 0;
}
if (dispatch->wheel.lo_res.x != 0) {
int value = dispatch->wheel.lo_res.x;
wheel_degrees.x = value * device->scroll.wheel_click_angle.x;
discrete.x = value;
evdev_notify_axis_legacy_wheel(
device,
time,
bit(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL),
&wheel_degrees,
&discrete);
dispatch->wheel.lo_res.x = 0;
dispatch->wheel.lo_res.y = 0;
}
}
static void
fallback_flush_absolute_motion(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
usec_t time)
{
struct libinput_device *base = &device->base;
struct device_coords point;
@ -315,7 +343,7 @@ static bool
fallback_flush_mt_down(struct fallback_dispatch *dispatch,
struct evdev_device *device,
int slot_idx,
uint64_t time)
usec_t time)
{
struct libinput_device *base = &device->base;
struct libinput_seat *seat = base->seat;
@ -354,7 +382,7 @@ static bool
fallback_flush_mt_motion(struct fallback_dispatch *dispatch,
struct evdev_device *device,
int slot_idx,
uint64_t time)
usec_t time)
{
struct libinput_device *base = &device->base;
struct device_coords point;
@ -384,7 +412,7 @@ static bool
fallback_flush_mt_up(struct fallback_dispatch *dispatch,
struct evdev_device *device,
int slot_idx,
uint64_t time)
usec_t time)
{
struct libinput_device *base = &device->base;
struct libinput_seat *seat = base->seat;
@ -412,7 +440,7 @@ static bool
fallback_flush_mt_cancel(struct fallback_dispatch *dispatch,
struct evdev_device *device,
int slot_idx,
uint64_t time)
usec_t time)
{
struct libinput_device *base = &device->base;
struct libinput_seat *seat = base->seat;
@ -439,7 +467,7 @@ fallback_flush_mt_cancel(struct fallback_dispatch *dispatch,
static bool
fallback_flush_st_down(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
usec_t time)
{
struct libinput_device *base = &device->base;
struct libinput_seat *seat = base->seat;
@ -475,7 +503,7 @@ fallback_flush_st_down(struct fallback_dispatch *dispatch,
static bool
fallback_flush_st_motion(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
usec_t time)
{
struct libinput_device *base = &device->base;
struct device_coords point;
@ -497,7 +525,7 @@ fallback_flush_st_motion(struct fallback_dispatch *dispatch,
static bool
fallback_flush_st_up(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
usec_t time)
{
struct libinput_device *base = &device->base;
struct libinput_seat *seat = base->seat;
@ -522,7 +550,7 @@ fallback_flush_st_up(struct fallback_dispatch *dispatch,
static bool
fallback_flush_st_cancel(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
usec_t time)
{
struct libinput_device *base = &device->base;
struct libinput_seat *seat = base->seat;
@ -547,7 +575,7 @@ fallback_flush_st_cancel(struct fallback_dispatch *dispatch,
static void
fallback_process_touch_button(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time,
usec_t time,
int value)
{
dispatch->pending_event |=
@ -558,7 +586,7 @@ static inline void
fallback_process_key(struct fallback_dispatch *dispatch,
struct evdev_device *device,
struct evdev_event *e,
uint64_t time)
usec_t time)
{
/* ignore kernel key repeat */
if (e->value == 2)
@ -600,7 +628,7 @@ static void
fallback_process_touch(struct fallback_dispatch *dispatch,
struct evdev_device *device,
struct evdev_event *e,
uint64_t time)
usec_t time)
{
struct mt_slot *slot = &dispatch->mt.slots[dispatch->mt.slot];
@ -701,7 +729,7 @@ fallback_process_absolute_motion(struct fallback_dispatch *dispatch,
}
static void
fallback_lid_keyboard_event(uint64_t time, struct libinput_event *event, void *data)
fallback_lid_keyboard_event(usec_t time, struct libinput_event *event, void *data)
{
struct fallback_dispatch *dispatch = fallback_dispatch(data);
@ -716,8 +744,8 @@ fallback_lid_keyboard_event(uint64_t time, struct libinput_event *event, void *d
int rc;
struct input_event ev[2];
ev[0] = input_event_init(0, EV_SW, SW_LID, 0);
ev[1] = input_event_init(0, EV_SYN, SYN_REPORT, 0);
ev[0] = input_event_init(usec_from_uint64_t(0), EV_SW, SW_LID, 0);
ev[1] = input_event_init(usec_from_uint64_t(0), EV_SYN, SYN_REPORT, 0);
rc = write(fd, ev, sizeof(ev));
@ -775,7 +803,7 @@ static inline void
fallback_process_switch(struct fallback_dispatch *dispatch,
struct evdev_device *device,
struct evdev_event *e,
uint64_t time)
usec_t time)
{
enum libinput_switch_state state;
bool is_closed;
@ -808,6 +836,20 @@ fallback_process_switch(struct fallback_dispatch *dispatch,
LIBINPUT_SWITCH_TABLET_MODE,
state);
break;
case EVDEV_SW_KEYPAD_SLIDE:
if (dispatch->keypad_slide.sw.state == e->value)
return;
dispatch->keypad_slide.sw.state = e->value;
if (e->value)
state = LIBINPUT_SWITCH_STATE_ON;
else
state = LIBINPUT_SWITCH_STATE_OFF;
switch_notify_toggle(&device->base,
time,
LIBINPUT_SWITCH_KEYPAD_SLIDE,
state);
break;
default:
break;
}
@ -816,7 +858,7 @@ fallback_process_switch(struct fallback_dispatch *dispatch,
static inline bool
fallback_reject_relative(struct evdev_device *device,
const struct evdev_event *e,
uint64_t time)
usec_t time)
{
switch (evdev_usage_enum(e->usage)) {
case EVDEV_REL_X:
@ -851,7 +893,7 @@ static inline void
fallback_process_relative(struct fallback_dispatch *dispatch,
struct evdev_device *device,
struct evdev_event *e,
uint64_t time)
usec_t time)
{
if (fallback_reject_relative(device, e, time))
return;
@ -890,7 +932,7 @@ static inline void
fallback_process_absolute(struct fallback_dispatch *dispatch,
struct evdev_device *device,
struct evdev_event *e,
uint64_t time)
usec_t time)
{
if (device->is_mt) {
fallback_process_touch(dispatch, device, e, time);
@ -934,7 +976,7 @@ fallback_arbitrate_touch(struct fallback_dispatch *dispatch, struct mt_slot *slo
static inline bool
fallback_flush_mt_events(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
usec_t time)
{
bool sent = false;
@ -1005,7 +1047,7 @@ fallback_flush_mt_events(struct fallback_dispatch *dispatch,
static void
fallback_handle_state(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
usec_t time)
{
bool need_touch_frame = false;
@ -1073,7 +1115,7 @@ static void
fallback_interface_process_event(struct evdev_dispatch *evdev_dispatch,
struct evdev_device *device,
struct evdev_event *event,
uint64_t time)
usec_t time)
{
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
static bool warned = false;
@ -1113,7 +1155,7 @@ static void
fallback_interface_process(struct evdev_dispatch *dispatch,
struct evdev_device *device,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
size_t nevents;
struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
@ -1127,7 +1169,7 @@ static void
cancel_touches(struct fallback_dispatch *dispatch,
struct evdev_device *device,
const struct device_coord_rect *rect,
uint64_t time)
usec_t time)
{
unsigned int idx;
bool need_frame = false;
@ -1158,7 +1200,7 @@ cancel_touches(struct fallback_dispatch *dispatch,
static void
release_pressed_keys(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
usec_t time)
{
for (evdev_usage_t usage = evdev_usage_from(EVDEV_KEY_RESERVED);
evdev_usage_le(usage, EVDEV_KEY_MAX);
@ -1208,9 +1250,9 @@ fallback_return_to_neutral_state(struct fallback_dispatch *dispatch,
struct evdev_device *device)
{
struct libinput *libinput = evdev_libinput_context(device);
uint64_t time;
usec_t time = libinput_now(libinput);
if ((time = libinput_now(libinput)) == 0)
if (usec_is_zero(time))
return;
cancel_touches(dispatch, device, NULL, time);
@ -1250,7 +1292,7 @@ fallback_interface_sync_initial_state(struct evdev_device *device,
struct evdev_dispatch *evdev_dispatch)
{
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
uint64_t time = libinput_now(evdev_libinput_context(device));
usec_t time = libinput_now(evdev_libinput_context(device));
if (device->tags & EVDEV_TAG_LID_SWITCH) {
struct libevdev *evdev = device->evdev;
@ -1278,13 +1320,20 @@ fallback_interface_sync_initial_state(struct evdev_device *device,
LIBINPUT_SWITCH_TABLET_MODE,
LIBINPUT_SWITCH_STATE_ON);
}
if (dispatch->keypad_slide.sw.state) {
switch_notify_toggle(&device->base,
time,
LIBINPUT_SWITCH_KEYPAD_SLIDE,
LIBINPUT_SWITCH_STATE_ON);
}
}
static void
fallback_interface_update_rect(struct evdev_dispatch *evdev_dispatch,
struct evdev_device *device,
const struct phys_rect *phys_rect,
uint64_t time)
usec_t time)
{
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
struct device_coord_rect rect;
@ -1303,7 +1352,7 @@ fallback_interface_toggle_touch(struct evdev_dispatch *evdev_dispatch,
struct evdev_device *device,
enum evdev_arbitration_state which,
const struct phys_rect *phys_rect,
uint64_t time)
usec_t time)
{
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
struct device_coord_rect rect = { 0 };
@ -1322,7 +1371,7 @@ fallback_interface_toggle_touch(struct evdev_dispatch *evdev_dispatch,
* arbitration by just a little bit so that any touch in
* event is caught as palm touch. */
libinput_timer_set(&dispatch->arbitration.arbitration_timer,
time + ms2us(90));
usec_add_millis(time, 90));
state = "not-active";
break;
case ARBITRATION_IGNORE_RECT:
@ -1416,9 +1465,7 @@ fallback_suspend(struct fallback_dispatch *dispatch, struct evdev_device *device
}
static void
fallback_tablet_mode_switch_event(uint64_t time,
struct libinput_event *event,
void *data)
fallback_tablet_mode_switch_event(usec_t time, struct libinput_event *event, void *data)
{
struct fallback_dispatch *dispatch = data;
struct evdev_device *device = dispatch->device;
@ -1708,11 +1755,16 @@ fallback_dispatch_init_switch(struct fallback_dispatch *dispatch,
dispatch->tablet_mode.sw.state = val;
}
if (device->tags & EVDEV_TAG_KEYPAD_SLIDE_SWITCH) {
val = libevdev_get_event_value(device->evdev, EV_SW, SW_KEYPAD_SLIDE);
dispatch->keypad_slide.sw.state = val;
}
libinput_device_init_event_listener(&dispatch->tablet_mode.other.listener);
}
static void
fallback_arbitration_timeout(uint64_t now, void *data)
fallback_arbitration_timeout(usec_t now, void *data)
{
struct fallback_dispatch *dispatch = data;

View file

@ -116,6 +116,13 @@ struct fallback_dispatch {
} other;
} tablet_mode;
struct {
/* Switch */
struct {
int state;
} sw;
} keypad_slide;
/* Bitmask of pressed keys used to ignore initial release events from
* the kernel. */
unsigned long hw_key_mask[NLONGS(KEY_CNT)];
@ -213,11 +220,11 @@ get_key_down_count(struct evdev_device *device, evdev_usage_t usage)
}
void
fallback_debounce_handle_state(struct fallback_dispatch *dispatch, uint64_t time);
fallback_debounce_handle_state(struct fallback_dispatch *dispatch, usec_t time);
void
fallback_notify_physical_button(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time,
usec_t time,
evdev_usage_t button,
enum libinput_button_state state);

View file

@ -29,6 +29,7 @@
#include <linux/input.h>
#include <stdbool.h>
#include "util-bits.h"
#include "util-input-event.h"
#include "util-mem.h"
#include "util-newtype.h"
@ -49,6 +50,10 @@ enum evdev_usage {
EVDEV_KEY_RESERVED = _evbit(EV_KEY, KEY_RESERVED),
EVDEV_KEY_ESC = _evbit(EV_KEY, KEY_ESC),
EVDEV_KEY_A = _evbit(EV_KEY, KEY_A),
EVDEV_KEY_CAPSLOCK = _evbit(EV_KEY, KEY_CAPSLOCK),
EVDEV_KEY_KP7 = _evbit(EV_KEY, KEY_KP7),
EVDEV_KEY_KPDOT = _evbit(EV_KEY, KEY_KPDOT),
EVDEV_KEY_MICMUTE = _evbit(EV_KEY, KEY_MICMUTE),
EVDEV_KEY_OK = _evbit(EV_KEY, KEY_OK),
EVDEV_KEY_LIGHTS_TOGGLE = _evbit(EV_KEY, KEY_LIGHTS_TOGGLE),
@ -134,6 +139,7 @@ enum evdev_usage {
EVDEV_SW_LID = _evbit(EV_SW, SW_LID),
EVDEV_SW_TABLET_MODE = _evbit(EV_SW, SW_TABLET_MODE),
EVDEV_SW_KEYPAD_SLIDE = _evbit(EV_SW, SW_KEYPAD_SLIDE),
EVDEV_SW_MAX = _evbit(EV_SW, SW_MAX),
EVDEV_MSC_SCAN = _evbit(EV_MSC, MSC_SCAN),
@ -150,6 +156,110 @@ enum evdev_usage {
*/
DECLARE_NEWTYPE(evdev_usage, uint32_t);
static inline const char *
evdev_usage_name(evdev_usage_t usage)
{
switch (evdev_usage_as_uint32_t(usage)) {
CASE_RETURN_STRING(EVDEV_SYN_REPORT);
CASE_RETURN_STRING(EVDEV_KEY_RESERVED);
CASE_RETURN_STRING(EVDEV_KEY_ESC);
CASE_RETURN_STRING(EVDEV_KEY_MICMUTE);
CASE_RETURN_STRING(EVDEV_KEY_OK);
CASE_RETURN_STRING(EVDEV_KEY_LIGHTS_TOGGLE);
CASE_RETURN_STRING(EVDEV_KEY_ALS_TOGGLE);
CASE_RETURN_STRING(EVDEV_KEY_MAX);
CASE_RETURN_STRING(EVDEV_BTN_LEFT);
CASE_RETURN_STRING(EVDEV_BTN_RIGHT);
CASE_RETURN_STRING(EVDEV_BTN_MIDDLE);
CASE_RETURN_STRING(EVDEV_BTN_SIDE);
CASE_RETURN_STRING(EVDEV_BTN_EXTRA);
CASE_RETURN_STRING(EVDEV_BTN_FORWARD);
CASE_RETURN_STRING(EVDEV_BTN_BACK);
CASE_RETURN_STRING(EVDEV_BTN_TASK);
CASE_RETURN_STRING(EVDEV_BTN_JOYSTICK);
CASE_RETURN_STRING(EVDEV_BTN_0);
CASE_RETURN_STRING(EVDEV_BTN_1);
CASE_RETURN_STRING(EVDEV_BTN_2);
CASE_RETURN_STRING(EVDEV_BTN_STYLUS);
CASE_RETURN_STRING(EVDEV_BTN_STYLUS2);
CASE_RETURN_STRING(EVDEV_BTN_STYLUS3);
CASE_RETURN_STRING(EVDEV_BTN_TOUCH);
CASE_RETURN_STRING(EVDEV_BTN_TOOL_PEN);
CASE_RETURN_STRING(EVDEV_BTN_TOOL_RUBBER);
CASE_RETURN_STRING(EVDEV_BTN_TOOL_BRUSH);
CASE_RETURN_STRING(EVDEV_BTN_TOOL_PENCIL);
CASE_RETURN_STRING(EVDEV_BTN_TOOL_AIRBRUSH);
CASE_RETURN_STRING(EVDEV_BTN_TOOL_MOUSE);
CASE_RETURN_STRING(EVDEV_BTN_TOOL_LENS);
CASE_RETURN_STRING(EVDEV_BTN_TOOL_QUINTTAP);
CASE_RETURN_STRING(EVDEV_BTN_TOOL_DOUBLETAP);
CASE_RETURN_STRING(EVDEV_BTN_TOOL_TRIPLETAP);
CASE_RETURN_STRING(EVDEV_BTN_TOOL_QUADTAP);
CASE_RETURN_STRING(EVDEV_BTN_TOOL_FINGER);
/* CASE_RETURN_STRING(EVDEV_BTN_MISC); - alias of BTN_0 */
/* CASE_RETURN_STRING(EVDEV_BTN_DIGI); - alias of BTN_TOOL_PEN */
CASE_RETURN_STRING(EVDEV_BTN_WHEEL);
CASE_RETURN_STRING(EVDEV_BTN_GEAR_UP);
CASE_RETURN_STRING(EVDEV_BTN_DPAD_UP);
CASE_RETURN_STRING(EVDEV_BTN_DPAD_RIGHT);
CASE_RETURN_STRING(EVDEV_BTN_TRIGGER_HAPPY);
CASE_RETURN_STRING(EVDEV_BTN_TRIGGER_HAPPY40);
CASE_RETURN_STRING(EVDEV_REL_X);
CASE_RETURN_STRING(EVDEV_REL_Y);
CASE_RETURN_STRING(EVDEV_REL_WHEEL);
CASE_RETURN_STRING(EVDEV_REL_WHEEL_HI_RES);
CASE_RETURN_STRING(EVDEV_REL_HWHEEL);
CASE_RETURN_STRING(EVDEV_REL_HWHEEL_HI_RES);
CASE_RETURN_STRING(EVDEV_REL_DIAL);
CASE_RETURN_STRING(EVDEV_REL_MAX);
CASE_RETURN_STRING(EVDEV_ABS_X);
CASE_RETURN_STRING(EVDEV_ABS_Y);
CASE_RETURN_STRING(EVDEV_ABS_Z);
CASE_RETURN_STRING(EVDEV_ABS_RX);
CASE_RETURN_STRING(EVDEV_ABS_RY);
CASE_RETURN_STRING(EVDEV_ABS_RZ);
CASE_RETURN_STRING(EVDEV_ABS_PRESSURE);
CASE_RETURN_STRING(EVDEV_ABS_DISTANCE);
CASE_RETURN_STRING(EVDEV_ABS_THROTTLE);
CASE_RETURN_STRING(EVDEV_ABS_RUDDER);
CASE_RETURN_STRING(EVDEV_ABS_WHEEL);
CASE_RETURN_STRING(EVDEV_ABS_MISC);
CASE_RETURN_STRING(EVDEV_ABS_TILT_X);
CASE_RETURN_STRING(EVDEV_ABS_TILT_Y);
CASE_RETURN_STRING(EVDEV_ABS_MT_SLOT);
CASE_RETURN_STRING(EVDEV_ABS_MT_POSITION_X);
CASE_RETURN_STRING(EVDEV_ABS_MT_POSITION_Y);
CASE_RETURN_STRING(EVDEV_ABS_MT_TOOL_TYPE);
CASE_RETURN_STRING(EVDEV_ABS_MT_TRACKING_ID);
CASE_RETURN_STRING(EVDEV_ABS_MT_TOUCH_MAJOR);
CASE_RETURN_STRING(EVDEV_ABS_MT_TOUCH_MINOR);
CASE_RETURN_STRING(EVDEV_ABS_MT_ORIENTATION);
CASE_RETURN_STRING(EVDEV_ABS_MT_PRESSURE);
CASE_RETURN_STRING(EVDEV_ABS_MT_DISTANCE);
CASE_RETURN_STRING(EVDEV_ABS_MAX);
CASE_RETURN_STRING(EVDEV_SW_LID);
CASE_RETURN_STRING(EVDEV_SW_TABLET_MODE);
CASE_RETURN_STRING(EVDEV_SW_KEYPAD_SLIDE);
CASE_RETURN_STRING(EVDEV_SW_MAX);
CASE_RETURN_STRING(EVDEV_MSC_SCAN);
CASE_RETURN_STRING(EVDEV_MSC_SERIAL);
CASE_RETURN_STRING(EVDEV_MSC_TIMESTAMP);
}
return NULL;
}
static inline evdev_usage_t
evdev_usage_from(enum evdev_usage usage)
{
@ -284,9 +394,9 @@ evdev_event_get_code_name(const struct evdev_event *e)
}
static inline struct input_event
evdev_event_to_input_event(const struct evdev_event *e, uint64_t time)
evdev_event_to_input_event(const struct evdev_event *e, usec_t time)
{
struct timeval tv = us2tv(time);
struct timeval tv = usec_to_timeval(time);
return (struct input_event){
.type = evdev_event_type(e),
.code = evdev_event_code(e),
@ -297,7 +407,7 @@ evdev_event_to_input_event(const struct evdev_event *e, uint64_t time)
}
static inline struct evdev_event
evdev_event_from_input_event(const struct input_event *e, uint64_t *time)
evdev_event_from_input_event(const struct input_event *e, usec_t *time)
{
if (time)
*time = input_event_time(e);
@ -324,7 +434,7 @@ struct evdev_frame {
int refcount;
size_t max_size;
size_t count;
uint64_t time;
usec_t time;
struct evdev_event events[];
};
@ -377,12 +487,12 @@ evdev_frame_get_events(struct evdev_frame *frame, size_t *nevents)
* Set the timestamp for all events in this event frame.
*/
static inline void
evdev_frame_set_time(struct evdev_frame *frame, uint64_t time)
evdev_frame_set_time(struct evdev_frame *frame, usec_t time)
{
frame->time = time;
}
static inline uint64_t
static inline usec_t
evdev_frame_get_time(const struct evdev_frame *frame)
{
return frame->time;
@ -401,7 +511,7 @@ static inline struct evdev_frame *
evdev_frame_new(size_t max_size)
{
struct evdev_frame *frame =
zalloc(max_size * sizeof(sizeof(*frame->events)) + sizeof(*frame));
zalloc(max_size * sizeof(*frame->events) + sizeof(*frame));
frame->refcount = 1;
frame->max_size = max_size;
@ -410,21 +520,6 @@ evdev_frame_new(size_t max_size)
return frame;
}
static inline struct evdev_frame *
evdev_frame_new_on_stack(size_t max_size)
{
assert(max_size <= 64);
struct evdev_frame *frame =
alloca(max_size * sizeof(*frame->events) + sizeof(*frame));
frame->refcount = 1;
frame->max_size = max_size;
frame->count = 1; /* SYN_REPORT is always there */
memset(frame->events, 0, max_size * sizeof(*frame->events));
return frame;
}
/**
* Append events to the event frame. nevents must be larger than 0
* and specifies the number of elements in events. If any events in
@ -497,7 +592,7 @@ evdev_frame_append_input_event(struct evdev_frame *frame,
{
struct evdev_event e = evdev_event_from_input_event(event, NULL);
if (evdev_usage_as_uint32_t(e.usage) == EVDEV_SYN_REPORT) {
uint64_t time = input_event_time(event);
usec_t time = input_event_time(event);
evdev_frame_set_time(frame, time);
}
return evdev_frame_append(frame, &e, 1);
@ -545,3 +640,121 @@ evdev_frame_clone(struct evdev_frame *frame)
return clone;
}
struct evdev_mask {
bitmask_t ev;
bitmask_t rel;
bitmask_t sw;
infmask_t key; /* < BTN_MISC */
infmask_t btn; /* >= BTN_MISC */
infmask_t abs;
};
static_assert(sizeof(bitmask_t) * 8 >= EV_MAX, "bitmask size too small");
static_assert(sizeof(bitmask_t) * 8 >= EV_REL, "bitmask size too small");
static_assert(sizeof(bitmask_t) * 8 >= EV_SW, "bitmask size too small");
static inline void
evdev_mask_reset(struct evdev_mask *mask)
{
mask->ev = bitmask_new();
mask->rel = bitmask_new();
mask->sw = bitmask_new();
infmask_reset(&mask->key);
infmask_reset(&mask->btn);
infmask_reset(&mask->abs);
}
static inline struct evdev_mask *
evdev_mask_new(void)
{
struct evdev_mask *mask = zalloc(sizeof(*mask));
evdev_mask_reset(mask);
return mask;
}
static inline void
evdev_mask_destroy(struct evdev_mask *mask)
{
if (mask) {
evdev_mask_reset(mask);
free(mask);
}
}
DEFINE_DESTROY_CLEANUP_FUNC(evdev_mask);
static inline void
evdev_mask_set_usage(struct evdev_mask *mask, evdev_usage_t usage)
{
unsigned int type = evdev_usage_type(usage);
unsigned int code = evdev_usage_code(usage);
if (type >= EV_MAX)
return;
bitmask_set_bit(&mask->ev, type);
switch (type) {
case EV_ABS:
if (code <= ABS_MAX)
infmask_set_bit(&mask->abs, code);
break;
case EV_KEY:
if (code < BTN_MISC)
infmask_set_bit(&mask->key, code);
else if (code <= KEY_MAX)
infmask_set_bit(&mask->btn, code - BTN_MISC);
break;
case EV_REL:
if (code <= REL_MAX)
bitmask_set_bit(&mask->rel, code);
break;
case EV_SW:
if (code <= SW_MAX)
bitmask_set_bit(&mask->sw, code);
break;
}
}
static inline void
evdev_mask_set_enum(struct evdev_mask *mask, enum evdev_usage usage)
{
evdev_mask_set_usage(mask, evdev_usage_from(usage));
}
static inline bool
evdev_mask_is_set(const struct evdev_mask *mask, evdev_usage_t usage)
{
unsigned int type = evdev_usage_type(usage);
unsigned int code = evdev_usage_code(usage);
if (type >= EV_MAX)
return false;
if (!bitmask_bit_is_set(mask->ev, type))
return false;
bool isset = false;
switch (type) {
case EV_ABS:
isset = infmask_bit_is_set(&mask->abs, code);
break;
case EV_KEY:
if (code < BTN_MISC)
isset = infmask_bit_is_set(&mask->key, code);
else
isset = infmask_bit_is_set(&mask->btn, code - BTN_MISC);
break;
case EV_REL:
isset = bitmask_bit_is_set(mask->rel, code);
break;
case EV_SW:
isset = bitmask_bit_is_set(mask->sw, code);
break;
default:
break;
}
return isset;
}

View file

@ -27,7 +27,7 @@
#include "evdev.h"
#define MIDDLEBUTTON_TIMEOUT ms2us(50)
#define MIDDLEBUTTON_TIMEOUT usec_from_millis(50)
/*****************************************
* BEFORE YOU EDIT THIS FILE, look at the state diagram in
@ -88,9 +88,10 @@ middlebutton_state_error(struct evdev_device *device,
}
static void
middlebutton_timer_set(struct evdev_device *device, uint64_t now)
middlebutton_timer_set(struct evdev_device *device, usec_t now)
{
libinput_timer_set(&device->middlebutton.timer, now + MIDDLEBUTTON_TIMEOUT);
libinput_timer_set(&device->middlebutton.timer,
usec_add(now, MIDDLEBUTTON_TIMEOUT));
}
static void
@ -102,7 +103,7 @@ middlebutton_timer_cancel(struct evdev_device *device)
static inline void
middlebutton_set_state(struct evdev_device *device,
enum evdev_middlebutton_state state,
uint64_t now)
usec_t now)
{
switch (state) {
case MIDDLEBUTTON_LEFT_DOWN:
@ -127,7 +128,7 @@ middlebutton_set_state(struct evdev_device *device,
static void
middlebutton_post_event(struct evdev_device *device,
uint64_t now,
usec_t now,
evdev_usage_t button,
enum libinput_button_state state)
{
@ -136,7 +137,7 @@ middlebutton_post_event(struct evdev_device *device,
static int
evdev_middlebutton_idle_handle_event(struct evdev_device *device,
uint64_t time,
usec_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
@ -162,7 +163,7 @@ evdev_middlebutton_idle_handle_event(struct evdev_device *device,
static int
evdev_middlebutton_ldown_handle_event(struct evdev_device *device,
uint64_t time,
usec_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
@ -214,7 +215,7 @@ evdev_middlebutton_ldown_handle_event(struct evdev_device *device,
static int
evdev_middlebutton_rdown_handle_event(struct evdev_device *device,
uint64_t time,
usec_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
@ -266,7 +267,7 @@ evdev_middlebutton_rdown_handle_event(struct evdev_device *device,
static int
evdev_middlebutton_middle_handle_event(struct evdev_device *device,
uint64_t time,
usec_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
@ -308,7 +309,7 @@ evdev_middlebutton_middle_handle_event(struct evdev_device *device,
static int
evdev_middlebutton_lup_pending_handle_event(struct evdev_device *device,
uint64_t time,
usec_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
@ -344,7 +345,7 @@ evdev_middlebutton_lup_pending_handle_event(struct evdev_device *device,
static int
evdev_middlebutton_rup_pending_handle_event(struct evdev_device *device,
uint64_t time,
usec_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
@ -380,7 +381,7 @@ evdev_middlebutton_rup_pending_handle_event(struct evdev_device *device,
static int
evdev_middlebutton_passthrough_handle_event(struct evdev_device *device,
uint64_t time,
usec_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
@ -403,7 +404,7 @@ evdev_middlebutton_passthrough_handle_event(struct evdev_device *device,
static int
evdev_middlebutton_ignore_lr_handle_event(struct evdev_device *device,
uint64_t time,
usec_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
@ -432,7 +433,7 @@ evdev_middlebutton_ignore_lr_handle_event(struct evdev_device *device,
static int
evdev_middlebutton_ignore_l_handle_event(struct evdev_device *device,
uint64_t time,
usec_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
@ -457,7 +458,7 @@ evdev_middlebutton_ignore_l_handle_event(struct evdev_device *device,
}
static int
evdev_middlebutton_ignore_r_handle_event(struct evdev_device *device,
uint64_t time,
usec_t time,
enum evdev_middlebutton_event event)
{
switch (event) {
@ -483,7 +484,7 @@ evdev_middlebutton_ignore_r_handle_event(struct evdev_device *device,
static int
evdev_middlebutton_handle_event(struct evdev_device *device,
uint64_t time,
usec_t time,
enum evdev_middlebutton_event event)
{
int rc = 0;
@ -553,7 +554,7 @@ evdev_middlebutton_apply_config(struct evdev_device *device)
bool
evdev_middlebutton_filter_button(struct evdev_device *device,
uint64_t time,
usec_t time,
evdev_usage_t button,
enum libinput_button_state state)
{
@ -616,7 +617,7 @@ evdev_middlebutton_filter_button(struct evdev_device *device,
}
static void
evdev_middlebutton_handle_timeout(uint64_t now, void *data)
evdev_middlebutton_handle_timeout(usec_t now, void *data)
{
struct evdev_device *device = evdev_device(data);

View file

@ -32,8 +32,8 @@
#include "evdev-mt-touchpad.h"
#include "linux/input.h"
#define DEFAULT_BUTTON_ENTER_TIMEOUT ms2us(100)
#define DEFAULT_BUTTON_LEAVE_TIMEOUT ms2us(300)
#define DEFAULT_BUTTON_ENTER_TIMEOUT usec_from_millis(100)
#define DEFAULT_BUTTON_LEAVE_TIMEOUT usec_from_millis(300)
/*****************************************
* BEFORE YOU EDIT THIS FILE, look at the state diagram in
@ -121,15 +121,17 @@ is_inside_top_middle_area(const struct tp_dispatch *tp, const struct tp_touch *t
}
static void
tp_button_set_enter_timer(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_button_set_enter_timer(struct tp_dispatch *tp, struct tp_touch *t, usec_t time)
{
libinput_timer_set(&t->button.timer, time + DEFAULT_BUTTON_ENTER_TIMEOUT);
libinput_timer_set(&t->button.timer,
usec_add(time, DEFAULT_BUTTON_ENTER_TIMEOUT));
}
static void
tp_button_set_leave_timer(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_button_set_leave_timer(struct tp_dispatch *tp, struct tp_touch *t, usec_t time)
{
libinput_timer_set(&t->button.timer, time + DEFAULT_BUTTON_LEAVE_TIMEOUT);
libinput_timer_set(&t->button.timer,
usec_add(time, DEFAULT_BUTTON_LEAVE_TIMEOUT));
}
/*
@ -141,7 +143,7 @@ tp_button_set_state(struct tp_dispatch *tp,
struct tp_touch *t,
enum button_state new_state,
enum button_event event,
uint64_t time)
usec_t time)
{
libinput_timer_cancel(&t->button.timer);
@ -176,7 +178,7 @@ static void
tp_button_none_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum button_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case BUTTON_EVENT_IN_BOTTOM_R:
@ -206,7 +208,7 @@ static void
tp_button_area_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum button_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case BUTTON_EVENT_IN_BOTTOM_R:
@ -233,23 +235,22 @@ tp_button_area_handle_event(struct tp_dispatch *tp,
* that triggered this call).
*/
static inline void
tp_button_release_other_bottom_touches(struct tp_dispatch *tp,
uint64_t other_start_time)
tp_button_release_other_bottom_touches(struct tp_dispatch *tp, usec_t other_start_time)
{
struct tp_touch *t;
tp_for_each_touch(tp, t) {
uint64_t tdelta;
usec_t tdelta;
if (t->button.state != BUTTON_STATE_BOTTOM || t->button.has_moved)
continue;
if (other_start_time > t->button.initial_time)
tdelta = other_start_time - t->button.initial_time;
if (usec_cmp(other_start_time, t->button.initial_time) > 0)
tdelta = usec_delta(other_start_time, t->button.initial_time);
else
tdelta = t->button.initial_time - other_start_time;
tdelta = usec_delta(t->button.initial_time, other_start_time);
if (tdelta > ms2us(80))
if (usec_cmp(tdelta, usec_from_millis(80)) > 0)
continue;
t->button.has_moved = true;
@ -260,7 +261,7 @@ static void
tp_button_bottom_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum button_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case BUTTON_EVENT_IN_BOTTOM_R:
@ -296,7 +297,7 @@ static void
tp_button_top_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum button_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case BUTTON_EVENT_IN_BOTTOM_R:
@ -327,7 +328,7 @@ static void
tp_button_top_new_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum button_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case BUTTON_EVENT_IN_BOTTOM_R:
@ -362,7 +363,7 @@ static void
tp_button_top_to_ignore_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum button_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case BUTTON_EVENT_IN_TOP_R:
@ -394,7 +395,7 @@ static void
tp_button_ignore_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum button_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case BUTTON_EVENT_IN_BOTTOM_R:
@ -422,7 +423,7 @@ static void
tp_button_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum button_event event,
uint64_t time)
usec_t time)
{
enum button_state current = t->button.state;
@ -496,7 +497,7 @@ tp_button_check_for_movement(struct tp_dispatch *tp, struct tp_touch *t)
}
void
tp_button_handle_state(struct tp_dispatch *tp, uint64_t time)
tp_button_handle_state(struct tp_dispatch *tp, usec_t time)
{
struct tp_touch *t;
@ -547,7 +548,7 @@ tp_button_handle_state(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_button_handle_timeout(uint64_t now, void *data)
tp_button_handle_timeout(usec_t now, void *data)
{
struct tp_touch *t = data;
@ -555,7 +556,7 @@ tp_button_handle_timeout(uint64_t now, void *data)
}
void
tp_process_button(struct tp_dispatch *tp, const struct evdev_event *e, uint64_t time)
tp_process_button(struct tp_dispatch *tp, const struct evdev_event *e, usec_t time)
{
uint32_t mask = bit(evdev_usage_enum(e->usage) - EVDEV_BTN_LEFT);
@ -577,7 +578,7 @@ tp_process_button(struct tp_dispatch *tp, const struct evdev_event *e, uint64_t
}
void
tp_release_all_buttons(struct tp_dispatch *tp, uint64_t time)
tp_release_all_buttons(struct tp_dispatch *tp, usec_t time)
{
if (tp->buttons.state) {
tp->buttons.state = 0;
@ -1032,7 +1033,7 @@ tp_remove_buttons(struct tp_dispatch *tp)
}
static int
tp_post_physical_buttons(struct tp_dispatch *tp, uint64_t time)
tp_post_physical_buttons(struct tp_dispatch *tp, usec_t time)
{
uint32_t current, old, button;
@ -1180,7 +1181,7 @@ out:
static int
tp_notify_clickpadbutton(struct tp_dispatch *tp,
uint64_t time,
usec_t time,
evdev_usage_t button,
uint32_t is_topbutton,
enum libinput_button_state state)
@ -1227,10 +1228,10 @@ tp_notify_clickpadbutton(struct tp_dispatch *tp,
}
static int
tp_post_clickpadbutton_buttons(struct tp_dispatch *tp, uint64_t time)
tp_post_clickpadbutton_buttons(struct tp_dispatch *tp, usec_t time)
{
uint32_t current, old, is_top;
evdev_usage_t button = evdev_usage_from_uint32_t(0);
evdev_usage_t button;
enum libinput_button_state state;
enum { AREA = 0x01, LEFT = 0x02, MIDDLE = 0x04, RIGHT = 0x08 };
bool want_left_handed = true;
@ -1332,7 +1333,7 @@ tp_post_clickpadbutton_buttons(struct tp_dispatch *tp, uint64_t time)
}
int
tp_post_button_events(struct tp_dispatch *tp, uint64_t time)
tp_post_button_events(struct tp_dispatch *tp, usec_t time)
{
if (tp->buttons.is_clickpad ||
tp->device->model_flags & EVDEV_MODEL_APPLE_TOUCHPAD_ONEBUTTON)

View file

@ -87,9 +87,9 @@ tp_touch_get_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
}
static inline void
tp_edge_scroll_set_timer(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_edge_scroll_set_timer(struct tp_dispatch *tp, struct tp_touch *t, usec_t time)
{
const int DEFAULT_SCROLL_LOCK_TIMEOUT = ms2us(300);
const usec_t DEFAULT_SCROLL_LOCK_TIMEOUT = usec_from_millis(300);
/* 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.
@ -97,14 +97,15 @@ tp_edge_scroll_set_timer(struct tp_dispatch *tp, struct tp_touch *t, uint64_t ti
if (tp->buttons.click_method == LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS)
return;
libinput_timer_set(&t->scroll.timer, time + DEFAULT_SCROLL_LOCK_TIMEOUT);
libinput_timer_set(&t->scroll.timer,
usec_add(time, DEFAULT_SCROLL_LOCK_TIMEOUT));
}
static void
tp_edge_scroll_set_state(struct tp_dispatch *tp,
struct tp_touch *t,
enum tp_edge_scroll_touch_state state,
uint64_t time)
usec_t time)
{
libinput_timer_cancel(&t->scroll.timer);
@ -131,7 +132,7 @@ static void
tp_edge_scroll_handle_none(struct tp_dispatch *tp,
struct tp_touch *t,
enum scroll_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case SCROLL_EVENT_TOUCH:
@ -164,7 +165,7 @@ static void
tp_edge_scroll_handle_edge_new(struct tp_dispatch *tp,
struct tp_touch *t,
enum scroll_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case SCROLL_EVENT_TOUCH:
@ -196,7 +197,7 @@ static void
tp_edge_scroll_handle_edge(struct tp_dispatch *tp,
struct tp_touch *t,
enum scroll_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case SCROLL_EVENT_TOUCH:
@ -230,7 +231,7 @@ static void
tp_edge_scroll_handle_area(struct tp_dispatch *tp,
struct tp_touch *t,
enum scroll_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case SCROLL_EVENT_TOUCH:
@ -252,7 +253,7 @@ static void
tp_edge_scroll_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum scroll_event event,
uint64_t time)
usec_t time)
{
enum tp_edge_scroll_touch_state current = t->scroll.edge_state;
@ -281,7 +282,7 @@ tp_edge_scroll_handle_event(struct tp_dispatch *tp,
}
static void
tp_edge_scroll_handle_timeout(uint64_t now, void *data)
tp_edge_scroll_handle_timeout(usec_t now, void *data)
{
struct tp_touch *t = data;
@ -349,7 +350,7 @@ tp_remove_edge_scroll(struct tp_dispatch *tp)
}
void
tp_edge_scroll_handle_state(struct tp_dispatch *tp, uint64_t time)
tp_edge_scroll_handle_state(struct tp_dispatch *tp, usec_t time)
{
struct tp_touch *t;
@ -393,7 +394,7 @@ tp_edge_scroll_handle_state(struct tp_dispatch *tp, uint64_t time)
}
int
tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
tp_edge_scroll_post_events(struct tp_dispatch *tp, usec_t time)
{
struct evdev_device *device = tp->device;
struct tp_touch *t;
@ -478,7 +479,7 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
}
void
tp_edge_scroll_stop_events(struct tp_dispatch *tp, uint64_t time)
tp_edge_scroll_stop_events(struct tp_dispatch *tp, usec_t time)
{
struct evdev_device *device = tp->device;
struct tp_touch *t;

View file

@ -33,11 +33,11 @@ enum gesture_cancelled {
CANCEL_GESTURE = 1,
};
#define QUICK_GESTURE_HOLD_TIMEOUT ms2us(40)
#define DEFAULT_GESTURE_HOLD_TIMEOUT ms2us(180)
#define DEFAULT_GESTURE_SWITCH_TIMEOUT ms2us(100)
#define DEFAULT_GESTURE_SWIPE_TIMEOUT ms2us(150)
#define DEFAULT_GESTURE_PINCH_TIMEOUT ms2us(300)
#define QUICK_GESTURE_HOLD_TIMEOUT usec_from_millis(40)
#define DEFAULT_GESTURE_HOLD_TIMEOUT usec_from_millis(180)
#define DEFAULT_GESTURE_SWITCH_TIMEOUT usec_from_millis(100)
#define DEFAULT_GESTURE_SWIPE_TIMEOUT usec_from_millis(150)
#define DEFAULT_GESTURE_PINCH_TIMEOUT usec_from_millis(300)
#define HOLD_AND_MOTION_THRESHOLD 0.5 /* mm */
#define PINCH_DISAMBIGUATION_MOVE_THRESHOLD 1.5 /* mm */
@ -152,10 +152,10 @@ tp_gesture_init_scroll(struct tp_dispatch *tp)
struct phys_coords zero = { 0.0, 0.0 };
tp->scroll.active.h = false;
tp->scroll.active.v = false;
tp->scroll.duration.h = 0;
tp->scroll.duration.v = 0;
tp->scroll.duration.h = usec_from_uint64_t(0);
tp->scroll.duration.v = usec_from_uint64_t(0);
tp->scroll.vector = zero;
tp->scroll.time_prev = 0;
tp->scroll.time_prev = usec_from_uint64_t(0);
}
static inline struct device_float_coords
@ -185,7 +185,7 @@ tp_get_raw_pointer_motion(struct tp_dispatch *tp)
}
static bool
tp_has_pending_pointer_motion(struct tp_dispatch *tp, uint64_t time)
tp_has_pending_pointer_motion(struct tp_dispatch *tp, usec_t time)
{
struct device_float_coords raw;
@ -202,7 +202,7 @@ tp_has_pending_pointer_motion(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_gesture_post_pointer_motion(struct tp_dispatch *tp, uint64_t time)
tp_gesture_post_pointer_motion(struct tp_dispatch *tp, usec_t time)
{
struct device_float_coords raw;
struct normalized_coords delta;
@ -316,12 +316,12 @@ tp_gesture_init_pinch(struct tp_dispatch *tp)
}
static inline void
tp_gesture_init_3fg_drag(struct tp_dispatch *tp, uint64_t time)
tp_gesture_init_3fg_drag(struct tp_dispatch *tp, usec_t time)
{
}
static inline void
tp_gesture_stop_3fg_drag(struct tp_dispatch *tp, uint64_t time)
tp_gesture_stop_3fg_drag(struct tp_dispatch *tp, usec_t time)
{
}
@ -344,24 +344,27 @@ static void
tp_gesture_apply_scroll_constraints(struct tp_dispatch *tp,
struct device_float_coords *raw,
struct normalized_coords *delta,
uint64_t time)
usec_t time)
{
uint64_t tdelta = 0;
usec_t tdelta = usec_from_millis(0);
struct phys_coords delta_mm, vector;
double vector_decay, vector_length, slope;
const uint64_t ACTIVE_THRESHOLD = ms2us(100), INACTIVE_THRESHOLD = ms2us(50),
EVENT_TIMEOUT = ms2us(100);
const usec_t ACTIVE_THRESHOLD = usec_from_millis(100),
INACTIVE_THRESHOLD = usec_from_millis(50),
EVENT_TIMEOUT = usec_from_millis(100);
/* Both axes active == true means free scrolling is enabled */
if (tp->scroll.active.h && tp->scroll.active.v)
return;
/* Determine time delta since last movement event */
if (tp->scroll.time_prev != 0)
tdelta = time - tp->scroll.time_prev;
if (tdelta > EVENT_TIMEOUT)
tdelta = 0;
if (!usec_is_zero(tp->scroll.time_prev)) {
usec_t diff = usec_delta(time, tp->scroll.time_prev);
if (usec_cmp(diff, EVENT_TIMEOUT) <= 0)
tdelta = diff;
}
tp->scroll.time_prev = time;
/* Delta since last movement event in mm */
@ -372,11 +375,13 @@ tp_gesture_apply_scroll_constraints(struct tp_dispatch *tp,
* EVENT_TIMEOUT of 100, vector_decay = (0.97)^tdelta. This linear
* approximation allows easier tweaking of EVENT_TIMEOUT and is faster.
*/
if (tdelta > 0) {
if (usec_gt(tdelta, 0)) {
uint64_t delta = usec_as_uint64_t(tdelta);
double recent, later;
recent = ((EVENT_TIMEOUT / 2.0) - tdelta) / (EVENT_TIMEOUT / 2.0);
later = (EVENT_TIMEOUT - tdelta) / (EVENT_TIMEOUT * 2.0);
vector_decay = tdelta <= (0.33 * EVENT_TIMEOUT) ? recent : later;
uint64_t timeout_us = usec_as_uint64_t(EVENT_TIMEOUT);
recent = ((timeout_us / 2.0) - delta) / (timeout_us / 2.0);
later = (timeout_us - delta) / (timeout_us * 2.0);
vector_decay = delta <= (0.33 * timeout_us) ? recent : later;
} else {
vector_decay = 0.0;
}
@ -407,36 +412,38 @@ tp_gesture_apply_scroll_constraints(struct tp_dispatch *tp,
const double MIN_VECTOR = 0.15;
if (slope >= DEGREE_30 && vector_length > MIN_VECTOR) {
tp->scroll.duration.v += tdelta;
if (tp->scroll.duration.v > ACTIVE_THRESHOLD)
tp->scroll.duration.v = usec_add(tp->scroll.duration.v, tdelta);
if (usec_cmp(tp->scroll.duration.v, ACTIVE_THRESHOLD) > 0)
tp->scroll.duration.v = ACTIVE_THRESHOLD;
if (slope >= DEGREE_75) {
if (tp->scroll.duration.h > tdelta)
tp->scroll.duration.h -= tdelta;
if (usec_cmp(tp->scroll.duration.h, tdelta) > 0)
tp->scroll.duration.h =
usec_sub(tp->scroll.duration.h, tdelta);
else
tp->scroll.duration.h = 0;
tp->scroll.duration.h = usec_from_uint64_t(0);
}
}
if (slope < DEGREE_60 && vector_length > MIN_VECTOR) {
tp->scroll.duration.h += tdelta;
if (tp->scroll.duration.h > ACTIVE_THRESHOLD)
tp->scroll.duration.h = usec_add(tp->scroll.duration.h, tdelta);
if (usec_cmp(tp->scroll.duration.h, ACTIVE_THRESHOLD) > 0)
tp->scroll.duration.h = ACTIVE_THRESHOLD;
if (slope < DEGREE_15) {
if (tp->scroll.duration.v > tdelta)
tp->scroll.duration.v -= tdelta;
if (usec_cmp(tp->scroll.duration.v, tdelta) > 0)
tp->scroll.duration.v =
usec_sub(tp->scroll.duration.v, tdelta);
else
tp->scroll.duration.v = 0;
tp->scroll.duration.v = usec_from_uint64_t(0);
}
}
if (tp->scroll.duration.h == ACTIVE_THRESHOLD) {
if (usec_cmp(tp->scroll.duration.h, ACTIVE_THRESHOLD) == 0) {
tp->scroll.active.h = true;
if (tp->scroll.duration.v < INACTIVE_THRESHOLD)
if (usec_cmp(tp->scroll.duration.v, INACTIVE_THRESHOLD) < 0)
tp->scroll.active.v = false;
}
if (tp->scroll.duration.v == ACTIVE_THRESHOLD) {
if (usec_cmp(tp->scroll.duration.v, ACTIVE_THRESHOLD) == 0) {
tp->scroll.active.v = true;
if (tp->scroll.duration.h < INACTIVE_THRESHOLD)
if (usec_cmp(tp->scroll.duration.h, INACTIVE_THRESHOLD) < 0)
tp->scroll.active.h = false;
}
@ -519,9 +526,9 @@ tp_gesture_use_hold_timer(struct tp_dispatch *tp)
}
static void
tp_gesture_set_hold_timer(struct tp_dispatch *tp, uint64_t time)
tp_gesture_set_hold_timer(struct tp_dispatch *tp, usec_t time)
{
uint64_t timeout;
usec_t timeout;
if (!tp->gesture.hold_enabled)
return;
@ -530,14 +537,14 @@ tp_gesture_set_hold_timer(struct tp_dispatch *tp, uint64_t time)
timeout = tp_gesture_is_quick_hold(tp) ? QUICK_GESTURE_HOLD_TIMEOUT
: DEFAULT_GESTURE_HOLD_TIMEOUT;
libinput_timer_set(&tp->gesture.hold_timer, time + timeout);
libinput_timer_set(&tp->gesture.hold_timer, usec_add(time, timeout));
}
}
static void
tp_gesture_handle_event_on_state_none(struct tp_dispatch *tp,
enum gesture_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case GESTURE_EVENT_RESET:
@ -581,7 +588,7 @@ tp_gesture_handle_event_on_state_none(struct tp_dispatch *tp,
static void
tp_gesture_handle_event_on_state_unknown(struct tp_dispatch *tp,
enum gesture_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case GESTURE_EVENT_RESET:
@ -634,7 +641,7 @@ tp_gesture_handle_event_on_state_unknown(struct tp_dispatch *tp,
static void
tp_gesture_handle_event_on_state_hold(struct tp_dispatch *tp,
enum gesture_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case GESTURE_EVENT_RESET:
@ -692,7 +699,7 @@ tp_gesture_handle_event_on_state_hold(struct tp_dispatch *tp,
static void
tp_gesture_handle_event_on_state_hold_and_motion(struct tp_dispatch *tp,
enum gesture_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case GESTURE_EVENT_RESET:
@ -733,7 +740,7 @@ tp_gesture_handle_event_on_state_hold_and_motion(struct tp_dispatch *tp,
static void
tp_gesture_handle_event_on_state_pointer_motion(struct tp_dispatch *tp,
enum gesture_event event,
uint64_t time)
usec_t time)
{
struct tp_touch *first;
struct phys_coords first_moved;
@ -779,7 +786,7 @@ tp_gesture_handle_event_on_state_pointer_motion(struct tp_dispatch *tp,
static void
tp_gesture_handle_event_on_state_scroll_start(struct tp_dispatch *tp,
enum gesture_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case GESTURE_EVENT_RESET:
@ -812,7 +819,7 @@ tp_gesture_handle_event_on_state_scroll_start(struct tp_dispatch *tp,
static void
tp_gesture_handle_event_on_state_scroll(struct tp_dispatch *tp,
enum gesture_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case GESTURE_EVENT_RESET:
@ -847,7 +854,7 @@ tp_gesture_handle_event_on_state_scroll(struct tp_dispatch *tp,
static void
tp_gesture_handle_event_on_state_pinch_start(struct tp_dispatch *tp,
enum gesture_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case GESTURE_EVENT_RESET:
@ -875,7 +882,7 @@ tp_gesture_handle_event_on_state_pinch_start(struct tp_dispatch *tp,
static void
tp_gesture_handle_event_on_state_pinch(struct tp_dispatch *tp,
enum gesture_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case GESTURE_EVENT_RESET:
@ -914,7 +921,7 @@ tp_gesture_handle_event_on_state_pinch(struct tp_dispatch *tp,
static void
tp_gesture_handle_event_on_state_swipe_start(struct tp_dispatch *tp,
enum gesture_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case GESTURE_EVENT_RESET:
@ -943,7 +950,7 @@ tp_gesture_handle_event_on_state_swipe_start(struct tp_dispatch *tp,
static void
tp_gesture_handle_event_on_state_swipe(struct tp_dispatch *tp,
enum gesture_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case GESTURE_EVENT_RESET:
@ -981,7 +988,7 @@ tp_gesture_handle_event_on_state_swipe(struct tp_dispatch *tp,
static void
tp_gesture_handle_event_on_state_3fg_drag_start(struct tp_dispatch *tp,
enum gesture_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case GESTURE_EVENT_RESET:
@ -1008,16 +1015,16 @@ tp_gesture_handle_event_on_state_3fg_drag_start(struct tp_dispatch *tp,
}
static void
tp_gesture_set_3fg_drag_timer(struct tp_dispatch *tp, uint64_t time)
tp_gesture_set_3fg_drag_timer(struct tp_dispatch *tp, usec_t time)
{
tp->gesture.drag_3fg_release_time = time;
libinput_timer_set(&tp->gesture.drag_3fg_timer, time + ms2us(700));
libinput_timer_set(&tp->gesture.drag_3fg_timer, usec_add_millis(time, 700));
}
static void
tp_gesture_handle_event_on_state_3fg_drag(struct tp_dispatch *tp,
enum gesture_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case GESTURE_EVENT_RESET:
@ -1065,7 +1072,7 @@ tp_gesture_handle_event_on_state_3fg_drag(struct tp_dispatch *tp,
static void
tp_gesture_handle_event_on_state_3fg_drag_released(struct tp_dispatch *tp,
enum gesture_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case GESTURE_EVENT_RESET:
@ -1125,7 +1132,7 @@ tp_gesture_handle_event_on_state_3fg_drag_released(struct tp_dispatch *tp,
}
static void
tp_gesture_handle_event(struct tp_dispatch *tp, enum gesture_event event, uint64_t time)
tp_gesture_handle_event(struct tp_dispatch *tp, enum gesture_event event, usec_t time)
{
enum tp_gesture_state oldstate;
@ -1187,7 +1194,7 @@ tp_gesture_handle_event(struct tp_dispatch *tp, enum gesture_event event, uint64
}
static void
tp_gesture_hold_timeout(uint64_t now, void *data)
tp_gesture_hold_timeout(usec_t now, void *data)
{
struct tp_dispatch *tp = data;
@ -1198,7 +1205,7 @@ tp_gesture_hold_timeout(uint64_t now, void *data)
}
void
tp_gesture_tap_timeout(struct tp_dispatch *tp, uint64_t time)
tp_gesture_tap_timeout(struct tp_dispatch *tp, usec_t time)
{
if (!tp->gesture.hold_enabled)
return;
@ -1208,7 +1215,7 @@ tp_gesture_tap_timeout(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_gesture_3fg_drag_timeout(uint64_t now, void *data)
tp_gesture_3fg_drag_timeout(usec_t now, void *data)
{
struct tp_dispatch *tp = data;
@ -1216,7 +1223,7 @@ tp_gesture_3fg_drag_timeout(uint64_t now, void *data)
}
static void
tp_gesture_detect_motion_gestures(struct tp_dispatch *tp, uint64_t time)
tp_gesture_detect_motion_gestures(struct tp_dispatch *tp, usec_t time)
{
struct tp_touch *first = tp->gesture.touches[0],
*second = tp->gesture.touches[1], *thumb;
@ -1281,7 +1288,9 @@ tp_gesture_detect_motion_gestures(struct tp_dispatch *tp, uint64_t time)
/* If both touches are within 7mm vertically and 40mm horizontally
* past the timeout, assume scroll/swipe */
if ((!tp->gesture.enabled || (distance_mm.x < 40.0 && distance_mm.y < 7.0)) &&
time > (tp->gesture.initial_time + DEFAULT_GESTURE_SWIPE_TIMEOUT)) {
usec_cmp(time,
usec_add(tp->gesture.initial_time,
DEFAULT_GESTURE_SWIPE_TIMEOUT)) > 0) {
if (tp->gesture.finger_count == 2)
tp_gesture_handle_event(tp, GESTURE_EVENT_SCROLL_START, time);
else if (tp->drag_3fg.nfingers == tp->gesture.finger_count)
@ -1416,7 +1425,7 @@ tp_gesture_is_pinch(struct tp_dispatch *tp)
}
static void
tp_gesture_handle_state_none(struct tp_dispatch *tp, uint64_t time)
tp_gesture_handle_state_none(struct tp_dispatch *tp, usec_t time)
{
struct tp_touch *first, *second;
struct tp_touch *touches[4];
@ -1485,23 +1494,21 @@ tp_gesture_handle_state_none(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_gesture_handle_state_unknown(struct tp_dispatch *tp,
uint64_t time,
bool ignore_motion)
tp_gesture_handle_state_unknown(struct tp_dispatch *tp, usec_t time, bool ignore_motion)
{
if (!ignore_motion)
tp_gesture_detect_motion_gestures(tp, time);
}
static void
tp_gesture_handle_state_hold(struct tp_dispatch *tp, uint64_t time, bool ignore_motion)
tp_gesture_handle_state_hold(struct tp_dispatch *tp, usec_t time, bool ignore_motion)
{
if (!ignore_motion)
tp_gesture_detect_motion_gestures(tp, time);
}
static void
tp_gesture_handle_state_hold_and_pointer_motion(struct tp_dispatch *tp, uint64_t time)
tp_gesture_handle_state_hold_and_pointer_motion(struct tp_dispatch *tp, usec_t time)
{
if (tp->queued & TOUCHPAD_EVENT_MOTION)
tp_gesture_post_pointer_motion(tp, time);
@ -1510,14 +1517,14 @@ tp_gesture_handle_state_hold_and_pointer_motion(struct tp_dispatch *tp, uint64_t
}
static void
tp_gesture_handle_state_pointer_motion(struct tp_dispatch *tp, uint64_t time)
tp_gesture_handle_state_pointer_motion(struct tp_dispatch *tp, usec_t time)
{
if (tp->queued & TOUCHPAD_EVENT_MOTION)
tp_gesture_post_pointer_motion(tp, time);
}
static void
tp_gesture_handle_state_scroll_start(struct tp_dispatch *tp, uint64_t time)
tp_gesture_handle_state_scroll_start(struct tp_dispatch *tp, usec_t time)
{
struct device_float_coords raw;
struct normalized_coords delta;
@ -1528,7 +1535,9 @@ tp_gesture_handle_state_scroll_start(struct tp_dispatch *tp, uint64_t time)
/* We may confuse a pinch for a scroll initially,
* allow ourselves to correct our guess.
*/
if (time < (tp->gesture.initial_time + DEFAULT_GESTURE_PINCH_TIMEOUT) &&
if (usec_cmp(time,
usec_add(tp->gesture.initial_time,
DEFAULT_GESTURE_PINCH_TIMEOUT)) < 0 &&
tp_gesture_is_pinch(tp)) {
tp_gesture_handle_event(tp, GESTURE_EVENT_PINCH_START, time);
return;
@ -1547,7 +1556,7 @@ tp_gesture_handle_state_scroll_start(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_gesture_handle_state_scroll(struct tp_dispatch *tp, uint64_t time)
tp_gesture_handle_state_scroll(struct tp_dispatch *tp, usec_t time)
{
struct device_float_coords raw;
struct normalized_coords delta;
@ -1558,7 +1567,9 @@ tp_gesture_handle_state_scroll(struct tp_dispatch *tp, uint64_t time)
/* We may confuse a pinch for a scroll initially,
* allow ourselves to correct our guess.
*/
if (time < (tp->gesture.initial_time + DEFAULT_GESTURE_PINCH_TIMEOUT) &&
if (usec_cmp(time,
usec_add(tp->gesture.initial_time,
DEFAULT_GESTURE_PINCH_TIMEOUT)) < 0 &&
tp_gesture_is_pinch(tp)) {
tp_gesture_handle_event(tp, GESTURE_EVENT_PINCH_START, time);
return;
@ -1580,7 +1591,7 @@ tp_gesture_handle_state_scroll(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_gesture_handle_state_swipe_start(struct tp_dispatch *tp, uint64_t time)
tp_gesture_handle_state_swipe_start(struct tp_dispatch *tp, usec_t time)
{
struct device_float_coords raw;
struct normalized_coords delta;
@ -1601,7 +1612,7 @@ tp_gesture_handle_state_swipe_start(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_gesture_handle_state_swipe(struct tp_dispatch *tp, uint64_t time)
tp_gesture_handle_state_swipe(struct tp_dispatch *tp, usec_t time)
{
struct device_float_coords raw;
struct normalized_coords delta, unaccel;
@ -1621,7 +1632,7 @@ tp_gesture_handle_state_swipe(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_gesture_handle_state_pinch_start(struct tp_dispatch *tp, uint64_t time)
tp_gesture_handle_state_pinch_start(struct tp_dispatch *tp, usec_t time)
{
const struct normalized_coords zero = { 0.0, 0.0 };
double angle, angle_delta, distance, scale;
@ -1662,7 +1673,7 @@ tp_gesture_handle_state_pinch_start(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_gesture_handle_state_pinch(struct tp_dispatch *tp, uint64_t time)
tp_gesture_handle_state_pinch(struct tp_dispatch *tp, usec_t time)
{
double angle, angle_delta, distance, scale;
struct device_float_coords center, fdelta;
@ -1702,7 +1713,7 @@ tp_gesture_handle_state_pinch(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_gesture_handle_state_3fg_drag_start(struct tp_dispatch *tp, uint64_t time)
tp_gesture_handle_state_3fg_drag_start(struct tp_dispatch *tp, usec_t time)
{
evdev_pointer_notify_button(tp->device,
time,
@ -1713,7 +1724,7 @@ tp_gesture_handle_state_3fg_drag_start(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_gesture_handle_state_3fg_drag(struct tp_dispatch *tp, uint64_t time)
tp_gesture_handle_state_3fg_drag(struct tp_dispatch *tp, usec_t time)
{
if (tp->queued & TOUCHPAD_EVENT_MOTION)
tp_gesture_post_pointer_motion(tp, time);
@ -1721,14 +1732,14 @@ tp_gesture_handle_state_3fg_drag(struct tp_dispatch *tp, uint64_t time)
static void
tp_gesture_handle_state_3fg_drag_released(struct tp_dispatch *tp,
uint64_t time,
usec_t time,
bool ignore_motion)
{
tp_gesture_detect_motion_gestures(tp, time);
}
static void
tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time, bool ignore_motion)
tp_gesture_handle_state(struct tp_dispatch *tp, usec_t time, bool ignore_motion)
{
enum tp_gesture_state oldstate = tp->gesture.state;
enum tp_gesture_state transitions[16] = { 0 };
@ -1839,7 +1850,7 @@ tp_gesture_thumb_moved(struct tp_dispatch *tp)
}
void
tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time, bool ignore_motion)
tp_gesture_post_events(struct tp_dispatch *tp, usec_t time, bool ignore_motion)
{
if (tp->gesture.finger_count == 0)
return;
@ -1867,7 +1878,9 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time, bool ignore_motion
/* When pinching, the thumb tends to move slower than the finger,
* so we may suppress it too early. Give it some time to move.
*/
if (time < (tp->gesture.initial_time + DEFAULT_GESTURE_PINCH_TIMEOUT) &&
if (usec_cmp(time,
usec_add(tp->gesture.initial_time,
DEFAULT_GESTURE_PINCH_TIMEOUT)) < 0 &&
tp_gesture_thumb_moved(tp))
tp_thumb_reset(tp);
@ -1876,7 +1889,7 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time, bool ignore_motion
}
void
tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, usec_t time)
{
if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG)
return;
@ -1885,7 +1898,7 @@ tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_gesture_end(struct tp_dispatch *tp, uint64_t time, enum gesture_cancelled cancelled)
tp_gesture_end(struct tp_dispatch *tp, usec_t time, enum gesture_cancelled cancelled)
{
switch (tp->gesture.state) {
case GESTURE_STATE_NONE:
@ -1917,13 +1930,13 @@ tp_gesture_end(struct tp_dispatch *tp, uint64_t time, enum gesture_cancelled can
}
void
tp_gesture_cancel(struct tp_dispatch *tp, uint64_t time)
tp_gesture_cancel(struct tp_dispatch *tp, usec_t time)
{
tp_gesture_end(tp, time, CANCEL_GESTURE);
}
void
tp_gesture_cancel_motion_gestures(struct tp_dispatch *tp, uint64_t time)
tp_gesture_cancel_motion_gestures(struct tp_dispatch *tp, usec_t time)
{
switch (tp->gesture.state) {
@ -1952,13 +1965,13 @@ tp_gesture_cancel_motion_gestures(struct tp_dispatch *tp, uint64_t time)
}
void
tp_gesture_stop(struct tp_dispatch *tp, uint64_t time)
tp_gesture_stop(struct tp_dispatch *tp, usec_t time)
{
tp_gesture_end(tp, time, END_GESTURE);
}
static void
tp_gesture_finger_count_switch_timeout(uint64_t now, void *data)
tp_gesture_finger_count_switch_timeout(usec_t now, void *data)
{
struct tp_dispatch *tp = data;
@ -1996,7 +2009,7 @@ tp_gesture_debounce_finger_changes(struct tp_dispatch *tp)
}
void
tp_gesture_update_finger_state(struct tp_dispatch *tp, uint64_t time)
tp_gesture_update_finger_state(struct tp_dispatch *tp, usec_t time)
{
unsigned int active_touches = 0;
struct tp_touch *t;
@ -2026,8 +2039,9 @@ tp_gesture_update_finger_state(struct tp_dispatch *tp, uint64_t time)
/* Else debounce finger changes */
} else if (active_touches != tp->gesture.finger_count_pending) {
tp->gesture.finger_count_pending = active_touches;
libinput_timer_set(&tp->gesture.finger_count_switch_timer,
time + DEFAULT_GESTURE_SWITCH_TIMEOUT);
libinput_timer_set(
&tp->gesture.finger_count_switch_timer,
usec_add(time, DEFAULT_GESTURE_SWITCH_TIMEOUT));
}
} else {
tp->gesture.finger_count_pending = 0;

View file

@ -29,10 +29,10 @@
#include "evdev-mt-touchpad.h"
#define DEFAULT_TAP_TIMEOUT_PERIOD ms2us(180)
#define DEFAULT_DRAG_TIMEOUT_PERIOD_BASE ms2us(160)
#define DEFAULT_DRAG_TIMEOUT_PERIOD_PERFINGER ms2us(20)
#define DEFAULT_DRAGLOCK_TIMEOUT_PERIOD ms2us(300)
#define DEFAULT_TAP_TIMEOUT_PERIOD usec_from_millis(180)
#define DEFAULT_DRAG_TIMEOUT_PERIOD_BASE usec_from_millis(160)
#define DEFAULT_DRAG_TIMEOUT_PERIOD_PERFINGER usec_from_millis(20)
#define DEFAULT_DRAGLOCK_TIMEOUT_PERIOD usec_from_millis(300)
#define DEFAULT_TAP_MOVE_THRESHOLD 1.3 /* mm */
enum tap_event {
@ -120,7 +120,7 @@ log_tap_bug(struct tp_dispatch *tp, struct tp_touch *t, enum tap_event event)
static void
tp_tap_notify(struct tp_dispatch *tp,
uint64_t time,
usec_t time,
int nfingers,
enum libinput_button_state state)
{
@ -151,24 +151,25 @@ tp_tap_notify(struct tp_dispatch *tp,
}
static void
tp_tap_set_timer(struct tp_dispatch *tp, uint64_t time)
tp_tap_set_timer(struct tp_dispatch *tp, usec_t time)
{
libinput_timer_set(&tp->tap.timer, time + DEFAULT_TAP_TIMEOUT_PERIOD);
libinput_timer_set(&tp->tap.timer, usec_add(time, DEFAULT_TAP_TIMEOUT_PERIOD));
}
static void
tp_tap_set_drag_timer(struct tp_dispatch *tp, uint64_t time, int nfingers_tapped)
tp_tap_set_drag_timer(struct tp_dispatch *tp, usec_t time, int nfingers_tapped)
{
libinput_timer_set(
&tp->tap.timer,
time + DEFAULT_DRAG_TIMEOUT_PERIOD_BASE +
(nfingers_tapped * DEFAULT_DRAG_TIMEOUT_PERIOD_PERFINGER));
usec_t per_finger_timeout =
usec_mul(DEFAULT_DRAG_TIMEOUT_PERIOD_PERFINGER, nfingers_tapped);
usec_t timeout = usec_add(DEFAULT_DRAG_TIMEOUT_PERIOD_BASE, per_finger_timeout);
libinput_timer_set(&tp->tap.timer, usec_add(time, timeout));
}
static void
tp_tap_set_draglock_timer(struct tp_dispatch *tp, uint64_t time)
tp_tap_set_draglock_timer(struct tp_dispatch *tp, usec_t time)
{
libinput_timer_set(&tp->tap.timer, time + DEFAULT_DRAGLOCK_TIMEOUT_PERIOD);
libinput_timer_set(&tp->tap.timer,
usec_add(time, DEFAULT_DRAGLOCK_TIMEOUT_PERIOD));
}
static void
@ -189,7 +190,7 @@ static void
tp_tap_idle_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case TAP_EVENT_TOUCH:
@ -222,7 +223,7 @@ static void
tp_tap_touch_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event,
uint64_t time)
usec_t time)
{
switch (event) {
@ -276,7 +277,7 @@ static void
tp_tap_hold_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event,
uint64_t time)
usec_t time)
{
switch (event) {
@ -314,7 +315,7 @@ static void
tp_tap_tapped_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event,
uint64_t time,
usec_t time,
int nfingers_tapped)
{
switch (event) {
@ -363,7 +364,7 @@ static void
tp_tap_touch2_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event,
uint64_t time)
usec_t time)
{
switch (event) {
@ -401,7 +402,7 @@ static void
tp_tap_touch2_hold_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event,
uint64_t time)
usec_t time)
{
switch (event) {
@ -436,7 +437,7 @@ static void
tp_tap_touch2_release_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event,
uint64_t time)
usec_t time)
{
switch (event) {
@ -505,7 +506,7 @@ static void
tp_tap_touch3_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event,
uint64_t time)
usec_t time)
{
switch (event) {
@ -543,7 +544,7 @@ static void
tp_tap_touch3_hold_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event,
uint64_t time)
usec_t time)
{
switch (event) {
@ -576,7 +577,7 @@ static void
tp_tap_touch3_release_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event,
uint64_t time)
usec_t time)
{
switch (event) {
@ -644,7 +645,7 @@ static void
tp_tap_touch3_release2_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event,
uint64_t time)
usec_t time)
{
switch (event) {
@ -742,7 +743,7 @@ static void
tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event,
uint64_t time,
usec_t time,
int nfingers_tapped)
{
switch (event) {
@ -808,7 +809,7 @@ static void
tp_tap_dragging_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event,
uint64_t time,
usec_t time,
int nfingers_tapped)
{
@ -872,7 +873,7 @@ static void
tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event,
uint64_t time,
usec_t time,
int nfingers_tapped)
{
@ -919,7 +920,7 @@ static void
tp_tap_dragging_tap_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event,
uint64_t time,
usec_t time,
int nfingers_tapped)
{
@ -979,7 +980,7 @@ static void
tp_tap_dragging2_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event,
uint64_t time,
usec_t time,
int nfingers_tapped)
{
@ -1033,7 +1034,7 @@ static void
tp_tap_dead_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event,
uint64_t time)
usec_t time)
{
switch (event) {
@ -1060,7 +1061,7 @@ static void
tp_tap_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
enum tap_event event,
uint64_t time)
usec_t time)
{
enum tp_tap_state current;
@ -1206,7 +1207,7 @@ tp_tap_enabled(struct tp_dispatch *tp)
}
int
tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
tp_tap_handle_state(struct tp_dispatch *tp, usec_t time)
{
struct tp_touch *t;
int filter_motion = 0;
@ -1341,7 +1342,7 @@ tp_tap_post_process_state(struct tp_dispatch *tp)
}
static void
tp_tap_handle_timeout(uint64_t time, void *data)
tp_tap_handle_timeout(usec_t time, void *data)
{
struct tp_dispatch *tp = data;
struct tp_touch *t;
@ -1357,10 +1358,7 @@ tp_tap_handle_timeout(uint64_t time, void *data)
}
static void
tp_tap_enabled_update(struct tp_dispatch *tp,
bool suspended,
bool enabled,
uint64_t time)
tp_tap_enabled_update(struct tp_dispatch *tp, bool suspended, bool enabled, usec_t time)
{
bool was_enabled = tp_tap_enabled(tp);
@ -1599,7 +1597,7 @@ tp_remove_tap(struct tp_dispatch *tp)
}
void
tp_release_all_taps(struct tp_dispatch *tp, uint64_t now)
tp_release_all_taps(struct tp_dispatch *tp, usec_t now)
{
struct tp_touch *t;
int i;
@ -1626,13 +1624,13 @@ tp_release_all_taps(struct tp_dispatch *tp, uint64_t now)
}
void
tp_tap_suspend(struct tp_dispatch *tp, uint64_t time)
tp_tap_suspend(struct tp_dispatch *tp, usec_t time)
{
tp_tap_enabled_update(tp, true, tp->tap.enabled, time);
}
void
tp_tap_resume(struct tp_dispatch *tp, uint64_t time)
tp_tap_resume(struct tp_dispatch *tp, usec_t time)
{
tp_tap_enabled_update(tp, false, tp->tap.enabled, time);
}

View file

@ -29,7 +29,7 @@
/* distance between fingers to assume it is not a scroll */
#define SCROLL_MM_X 35
#define SCROLL_MM_Y 25
#define THUMB_TIMEOUT ms2us(100)
#define THUMB_TIMEOUT usec_from_millis(100)
static inline const char *
thumb_state_to_str(enum tp_thumb_state state)
@ -193,7 +193,7 @@ tp_thumb_revive(struct tp_dispatch *tp, struct tp_touch *t)
}
void
tp_thumb_update_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_thumb_update_touch(struct tp_dispatch *tp, struct tp_touch *t, usec_t time)
{
if (!tp->thumb.detect_thumbs)
return;
@ -274,7 +274,7 @@ tp_thumb_update_multifinger(struct tp_dispatch *tp)
speed_exceeded_count =
max(speed_exceeded_count, t->speed.exceeded_count);
if (!oldest || t->initial_time < oldest->initial_time) {
if (!oldest || usec_cmp(t->initial_time, oldest->initial_time) < 0) {
oldest = t;
}
@ -337,11 +337,14 @@ tp_thumb_update_multifinger(struct tp_dispatch *tp)
* the behavior of the other touches.)
*/
if (newest && (newest->initial_time - oldest->initial_time) < THUMB_TIMEOUT &&
if (newest) {
usec_t delta = usec_delta(newest->initial_time, oldest->initial_time);
if (usec_cmp(delta, THUMB_TIMEOUT) < 0 &&
first->point.y < tp->thumb.lower_thumb_line) {
tp_thumb_lift(tp);
return;
}
}
/* If we're past the THUMB_TIMEOUT, and the touches are relatively far
* apart, then the new touch is unlikely to be a tap or clickfinger.

View file

@ -28,19 +28,20 @@
#include <math.h>
#include <stdbool.h>
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
#include <libwacom/libwacom.h>
#endif
#include "util-input-event.h"
#include "evdev-mt-touchpad.h"
#include "libinput-feature.h"
#include "quirks.h"
#define DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT ms2us(300)
#define DEFAULT_TRACKPOINT_EVENT_TIMEOUT ms2us(40)
#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1 ms2us(200)
#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2 ms2us(500)
#define DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT usec_from_millis(300)
#define DEFAULT_TRACKPOINT_EVENT_TIMEOUT usec_from_millis(40)
#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1 usec_from_millis(200)
#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2 usec_from_millis(500)
#define FAKE_FINGER_OVERFLOW bit(7)
#define THUMB_IGNORE_SPEED_THRESHOLD 20 /* mm/s */
@ -61,7 +62,7 @@ tp_motion_history_offset(struct tp_touch *t, int offset)
struct normalized_coords
tp_filter_motion(struct tp_dispatch *tp,
const struct device_float_coords *unaccelerated,
uint64_t time)
usec_t time)
{
struct device_float_coords raw;
const struct normalized_coords zero = { 0.0, 0.0 };
@ -78,7 +79,7 @@ tp_filter_motion(struct tp_dispatch *tp,
struct normalized_coords
tp_filter_motion_unaccelerated(struct tp_dispatch *tp,
const struct device_float_coords *unaccelerated,
uint64_t time)
usec_t time)
{
struct device_float_coords raw;
const struct normalized_coords zero = { 0.0, 0.0 };
@ -95,7 +96,7 @@ tp_filter_motion_unaccelerated(struct tp_dispatch *tp,
struct normalized_coords
tp_filter_scroll(struct tp_dispatch *tp,
const struct device_float_coords *unaccelerated,
uint64_t time)
usec_t time)
{
struct device_float_coords raw;
const struct normalized_coords zero = { 0.0, 0.0 };
@ -106,17 +107,19 @@ tp_filter_scroll(struct tp_dispatch *tp,
/* Convert to device units with x/y in the same resolution */
raw = tp_scale_to_xaxis(tp, *unaccelerated);
return filter_dispatch_scroll(tp->device->pointer.filter, &raw, tp, time);
return filter_dispatch_scroll(tp->device->pointer.filter,
&raw,
tp,
time,
FILTER_SCROLL_TYPE_FINGER);
}
static inline void
tp_calculate_motion_speed(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_calculate_motion_speed(struct tp_dispatch *tp, struct tp_touch *t, usec_t time)
{
const struct tp_history_point *last;
struct device_coords delta;
struct phys_coords mm;
double distance;
double speed;
/* Don't do this on single-touch or semi-mt devices */
if (!tp->has_mt || tp->semi_mt)
@ -144,15 +147,16 @@ tp_calculate_motion_speed(struct tp_dispatch *tp, struct tp_touch *t, uint64_t t
delta.y = abs(t->point.y - last->point.y);
mm = evdev_device_unit_delta_to_mm(tp->device, &delta);
distance = length_in_mm(mm);
speed = distance / (time - last->time); /* mm/us */
usec_t tdelta = usec_delta(time, last->time);
double distance = length_in_mm(mm);
double speed = distance / usec_as_uint64_t(tdelta); /* mm/us */
speed *= 1000000; /* mm/s */
t->speed.last_speed = speed;
}
static inline void
tp_motion_history_push(struct tp_touch *t, uint64_t time)
tp_motion_history_push(struct tp_touch *t, usec_t time)
{
int motion_index = (t->history.index + 1) % TOUCHPAD_HISTORY_LENGTH;
@ -176,10 +180,9 @@ tp_motion_history_push(struct tp_touch *t, uint64_t time)
* This only looks at x changes, y changes are ignored.
*/
static inline void
tp_detect_wobbling(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_detect_wobbling(struct tp_dispatch *tp, struct tp_touch *t, usec_t time)
{
int dx, dy;
uint64_t dtime;
const struct device_coords *prev_point;
if (tp->nfingers_down != 1 || tp->nfingers_down != tp->old_nfingers_down)
@ -196,11 +199,11 @@ tp_detect_wobbling(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
prev_point = &tp_motion_history_offset(t, 0)->point;
dx = prev_point->x - t->point.x;
dy = prev_point->y - t->point.y;
dtime = time - tp->hysteresis.last_motion_time;
usec_t dtime = usec_delta(time, tp->hysteresis.last_motion_time);
tp->hysteresis.last_motion_time = time;
if ((dx == 0 && dy != 0) || dtime > ms2us(40)) {
if ((dx == 0 && dy != 0) || usec_cmp(dtime, usec_from_millis(40)) > 0) {
t->hysteresis.x_motion_history = 0;
return;
}
@ -318,7 +321,7 @@ tp_fake_finger_set(struct tp_dispatch *tp, evdev_usage_t usage, bool is_press)
}
static inline void
tp_new_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_new_touch(struct tp_dispatch *tp, struct tp_touch *t, usec_t time)
{
if (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE ||
t->state == TOUCH_HOVERING)
@ -353,7 +356,7 @@ tp_new_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
}
static inline void
tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, usec_t time)
{
t->dirty = true;
t->state = TOUCH_BEGIN;
@ -377,7 +380,7 @@ tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
* need.
*/
static inline void
tp_maybe_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_maybe_end_touch(struct tp_dispatch *tp, struct tp_touch *t, usec_t time)
{
switch (t->state) {
case TOUCH_NONE:
@ -422,7 +425,7 @@ tp_recover_ended_touch(struct tp_dispatch *tp, struct tp_touch *t)
* Use tp_maybe_end_touch() instead.
*/
static inline void
tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, usec_t time)
{
if (t->state != TOUCH_MAYBE_END) {
evdev_log_bug_libinput(tp->device,
@ -436,7 +439,7 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
t->palm.state = PALM_NONE;
t->state = TOUCH_END;
t->pinned.is_pinned = false;
t->palm.time = 0;
t->palm.time = usec_from_uint64_t(0);
t->speed.exceeded_count = 0;
tp->queued |= TOUCHPAD_EVENT_MOTION;
}
@ -445,14 +448,14 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
* End the touch sequence on ABS_MT_TRACKING_ID -1 or when the BTN_TOOL_* 0 is received.
*/
static inline void
tp_end_sequence(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_end_sequence(struct tp_dispatch *tp, struct tp_touch *t, usec_t time)
{
t->has_ended = true;
tp_maybe_end_touch(tp, t, time);
}
static void
tp_stop_actions(struct tp_dispatch *tp, uint64_t time)
tp_stop_actions(struct tp_dispatch *tp, usec_t time)
{
tp_edge_scroll_stop_events(tp, time);
tp_gesture_cancel(tp, time);
@ -500,7 +503,7 @@ rotated(struct tp_dispatch *tp, evdev_usage_t usage, int value)
}
static void
tp_process_absolute(struct tp_dispatch *tp, const struct evdev_event *e, uint64_t time)
tp_process_absolute(struct tp_dispatch *tp, const struct evdev_event *e, usec_t time)
{
struct tp_touch *t = tp_current_touch(tp);
@ -555,9 +558,7 @@ tp_process_absolute(struct tp_dispatch *tp, const struct evdev_event *e, uint64_
}
static void
tp_process_absolute_st(struct tp_dispatch *tp,
const struct evdev_event *e,
uint64_t time)
tp_process_absolute_st(struct tp_dispatch *tp, const struct evdev_event *e, usec_t time)
{
struct tp_touch *t = tp_current_touch(tp);
@ -585,7 +586,7 @@ tp_process_absolute_st(struct tp_dispatch *tp,
}
static inline void
tp_restore_synaptics_touches(struct tp_dispatch *tp, uint64_t time)
tp_restore_synaptics_touches(struct tp_dispatch *tp, usec_t time)
{
unsigned int i;
unsigned int nfake_touches;
@ -619,7 +620,7 @@ tp_restore_synaptics_touches(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_process_fake_touches(struct tp_dispatch *tp, uint64_t time)
tp_process_fake_touches(struct tp_dispatch *tp, usec_t time)
{
struct tp_touch *t;
unsigned int nfake_touches;
@ -672,7 +673,7 @@ tp_process_fake_touches(struct tp_dispatch *tp, uint64_t time)
static void
tp_process_trackpoint_button(struct tp_dispatch *tp,
const struct evdev_event *e,
uint64_t time)
usec_t time)
{
struct evdev_dispatch *dispatch;
evdev_usage_t button;
@ -704,7 +705,7 @@ tp_process_trackpoint_button(struct tp_dispatch *tp,
}
static void
tp_process_key(struct tp_dispatch *tp, const struct evdev_event *e, uint64_t time)
tp_process_key(struct tp_dispatch *tp, const struct evdev_event *e, usec_t time)
{
/* ignore kernel key repeat */
if (e->value == 2)
@ -735,12 +736,12 @@ tp_process_key(struct tp_dispatch *tp, const struct evdev_event *e, uint64_t tim
}
static void
tp_process_msc(struct tp_dispatch *tp, const struct evdev_event *e, uint64_t time)
tp_process_msc(struct tp_dispatch *tp, const struct evdev_event *e, usec_t time)
{
if (evdev_usage_eq(e->usage, EVDEV_MSC_TIMESTAMP))
return;
tp->quirks.msc_timestamp.now = e->value;
tp->quirks.msc_timestamp.now = usec_from_uint64_t(e->value);
tp->queued |= TOUCHPAD_EVENT_TIMESTAMP;
}
@ -826,7 +827,7 @@ tp_palm_in_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
}
static bool
tp_palm_detect_dwt_triggered(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_palm_detect_dwt_triggered(struct tp_dispatch *tp, struct tp_touch *t, usec_t time)
{
if (tp->dwt.dwt_enabled && tp->dwt.keyboard_active && t->state == TOUCH_BEGIN) {
t->palm.state = PALM_TYPING;
@ -842,8 +843,8 @@ tp_palm_detect_dwt_triggered(struct tp_dispatch *tp, struct tp_touch *t, uint64_
started once we stop typing will be able to control the
pointer (alas not tap, etc.).
*/
if (t->palm.time == 0 ||
t->palm.time > tp->dwt.keyboard_last_press_time) {
if (usec_is_zero(t->palm.time) ||
usec_cmp(t->palm.time, tp->dwt.keyboard_last_press_time) > 0) {
t->palm.state = PALM_NONE;
evdev_log_debug(
tp->device,
@ -858,7 +859,7 @@ tp_palm_detect_dwt_triggered(struct tp_dispatch *tp, struct tp_touch *t, uint64_
static bool
tp_palm_detect_trackpoint_triggered(struct tp_dispatch *tp,
struct tp_touch *t,
uint64_t time)
usec_t time)
{
if (!tp->palm.monitor_trackpoint)
return false;
@ -872,8 +873,8 @@ tp_palm_detect_trackpoint_triggered(struct tp_dispatch *tp,
if (t->palm.state == PALM_TRACKPOINT && t->state == TOUCH_UPDATE &&
!tp->palm.trackpoint_active) {
if (t->palm.time == 0 ||
t->palm.time > tp->palm.trackpoint_last_event_time) {
if (usec_is_zero(t->palm.time) ||
usec_cmp(t->palm.time, tp->palm.trackpoint_last_event_time) > 0) {
t->palm.state = PALM_NONE;
evdev_log_debug(
tp->device,
@ -886,7 +887,7 @@ tp_palm_detect_trackpoint_triggered(struct tp_dispatch *tp,
}
static bool
tp_palm_detect_tool_triggered(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_palm_detect_tool_triggered(struct tp_dispatch *tp, struct tp_touch *t, usec_t time)
{
if (!tp->palm.use_mt_tool)
return false;
@ -903,16 +904,15 @@ tp_palm_detect_tool_triggered(struct tp_dispatch *tp, struct tp_touch *t, uint64
}
static inline bool
tp_palm_detect_move_out_of_edge(struct tp_dispatch *tp,
struct tp_touch *t,
uint64_t time)
tp_palm_detect_move_out_of_edge(struct tp_dispatch *tp, struct tp_touch *t, usec_t time)
{
const int PALM_TIMEOUT = ms2us(200);
const usec_t PALM_TIMEOUT = usec_from_millis(200);
int directions = 0;
struct device_float_coords delta;
int dirs;
if (time < t->palm.time + PALM_TIMEOUT && !tp_palm_in_edge(tp, t)) {
if (usec_cmp(time, usec_add(t->palm.time, PALM_TIMEOUT)) < 0 &&
!tp_palm_in_edge(tp, t)) {
if (tp_palm_was_in_side_edge(tp, t))
directions = NE | E | SE | SW | W | NW;
else if (tp_palm_was_in_top_edge(tp, t))
@ -930,7 +930,7 @@ tp_palm_detect_move_out_of_edge(struct tp_dispatch *tp,
}
static inline bool
tp_palm_detect_multifinger(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_palm_detect_multifinger(struct tp_dispatch *tp, struct tp_touch *t, usec_t time)
{
struct tp_touch *other;
@ -960,7 +960,7 @@ tp_palm_detect_multifinger(struct tp_dispatch *tp, struct tp_touch *t, uint64_t
static inline bool
tp_palm_detect_touch_size_triggered(struct tp_dispatch *tp,
struct tp_touch *t,
uint64_t time)
usec_t time)
{
if (!tp->palm.use_size)
return false;
@ -983,7 +983,7 @@ tp_palm_detect_touch_size_triggered(struct tp_dispatch *tp,
}
static inline bool
tp_palm_detect_edge(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_palm_detect_edge(struct tp_dispatch *tp, struct tp_touch *t, usec_t time)
{
if (t->palm.state == PALM_EDGE) {
if (tp_palm_detect_multifinger(tp, t, time)) {
@ -1030,7 +1030,7 @@ tp_palm_detect_edge(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
static bool
tp_palm_detect_pressure_triggered(struct tp_dispatch *tp,
struct tp_touch *t,
uint64_t time)
usec_t time)
{
if (!tp->palm.use_pressure)
return false;
@ -1047,7 +1047,7 @@ tp_palm_detect_pressure_triggered(struct tp_dispatch *tp,
static bool
tp_palm_detect_arbitration_triggered(struct tp_dispatch *tp,
struct tp_touch *t,
uint64_t time)
usec_t time)
{
if (tp->arbitration.state == ARBITRATION_NOT_ACTIVE)
return false;
@ -1058,7 +1058,7 @@ tp_palm_detect_arbitration_triggered(struct tp_dispatch *tp,
}
static void
tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, usec_t time)
{
const char *palm_state;
enum touch_palm_state oldstate = t->palm.state;
@ -1134,7 +1134,7 @@ out:
}
static void
tp_unhover_pressure(struct tp_dispatch *tp, uint64_t time)
tp_unhover_pressure(struct tp_dispatch *tp, usec_t time)
{
struct tp_touch *t;
int i;
@ -1218,7 +1218,7 @@ tp_unhover_pressure(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_unhover_size(struct tp_dispatch *tp, uint64_t time)
tp_unhover_size(struct tp_dispatch *tp, usec_t time)
{
struct tp_touch *t;
int low = tp->touch_size.low, high = tp->touch_size.high;
@ -1258,7 +1258,7 @@ tp_unhover_size(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_unhover_fake_touches(struct tp_dispatch *tp, uint64_t time)
tp_unhover_fake_touches(struct tp_dispatch *tp, usec_t time)
{
struct tp_touch *t;
unsigned int nfake_touches;
@ -1312,7 +1312,7 @@ tp_unhover_fake_touches(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
tp_unhover_touches(struct tp_dispatch *tp, usec_t time)
{
if (tp->pressure.use_pressure)
tp_unhover_pressure(tp, time);
@ -1398,17 +1398,17 @@ tp_need_motion_history_reset(struct tp_dispatch *tp)
}
static bool
tp_detect_jumps(const struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_detect_jumps(const struct tp_dispatch *tp, struct tp_touch *t, usec_t time)
{
struct device_coords delta;
struct phys_coords mm;
struct tp_history_point *last;
double abs_distance, rel_distance;
bool is_jump = false;
uint64_t tdelta;
usec_t tdelta;
/* Reference interval from the touchpad the various thresholds
* were measured from */
unsigned int reference_interval = ms2us(12);
usec_t reference_interval = usec_from_millis(12);
/* On some touchpads the firmware does funky stuff and we cannot
* have our own jump detection, e.g. Lenovo Carbon X1 Gen 6 (see
@ -1431,7 +1431,7 @@ tp_detect_jumps(const struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
/* called before tp_motion_history_push, so offset 0 is the most
* recent coordinate */
last = tp_motion_history_offset(t, 0);
tdelta = time - last->time;
tdelta = usec_delta(time, last->time);
/* For test devices we always force the time delta to 12, at least
until the test suite actually does proper intervals. */
@ -1441,7 +1441,8 @@ tp_detect_jumps(const struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
/* If the last frame is more than 30ms ago, we have irregular
* frames, who knows what's a pointer jump here and what's
* legitimate movement.... */
if (tdelta > 2.5 * reference_interval || tdelta == 0)
if (usec_cmp(tdelta, usec_mul(reference_interval, 2.5)) > 0 ||
usec_is_zero(tdelta))
return false;
/* We historically expected ~12ms frame intervals, so the numbers
@ -1450,7 +1451,8 @@ tp_detect_jumps(const struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
delta.x = abs(t->point.x - last->point.x);
delta.y = abs(t->point.y - last->point.y);
mm = evdev_device_unit_delta_to_mm(tp->device, &delta);
abs_distance = hypot(mm.x, mm.y) * reference_interval / tdelta;
abs_distance = hypot(mm.x, mm.y) * usec_as_uint64_t(reference_interval) /
usec_as_uint64_t(tdelta);
rel_distance = abs_distance - t->jumps.last_delta_mm;
/* Special case for the ALPS devices in the Lenovo ThinkPad E465,
@ -1491,9 +1493,9 @@ tp_detect_jumps(const struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
static inline void
tp_motion_history_fix_last(struct tp_dispatch *tp,
struct tp_touch *t,
unsigned int jumping_interval,
unsigned int normal_interval,
uint64_t time)
usec_t jumping_interval,
usec_t normal_interval,
usec_t time)
{
if (t->state != TOUCH_UPDATE)
return;
@ -1509,12 +1511,13 @@ tp_motion_history_fix_last(struct tp_dispatch *tp,
struct tp_history_point *p;
p = tp_motion_history_offset(t, i);
p->time = time - jumping_interval - normal_interval * i;
p->time = usec_sub(usec_sub(time, jumping_interval),
usec_mul(normal_interval, i));
}
}
static void
tp_process_msc_timestamp(struct tp_dispatch *tp, uint64_t time)
tp_process_msc_timestamp(struct tp_dispatch *tp, usec_t time)
{
struct msc_timestamp *m = &tp->quirks.msc_timestamp;
@ -1548,15 +1551,15 @@ tp_process_msc_timestamp(struct tp_dispatch *tp, uint64_t time)
delta is equivalent to 10 events and the movement is x, we
instead pretend there was movement of x/10.
*/
if (m->now == 0) {
if (usec_is_zero(m->now)) {
m->state = JUMP_STATE_EXPECT_FIRST;
m->interval = 0;
m->interval = usec_from_uint64_t(0);
return;
}
switch (m->state) {
case JUMP_STATE_EXPECT_FIRST:
if (m->now > ms2us(20)) {
if (usec_cmp(m->now, usec_from_millis(20)) > 0) {
m->state = JUMP_STATE_IGNORE;
} else {
m->state = JUMP_STATE_EXPECT_DELAY;
@ -1564,13 +1567,13 @@ tp_process_msc_timestamp(struct tp_dispatch *tp, uint64_t time)
}
break;
case JUMP_STATE_EXPECT_DELAY:
if (m->now > m->interval * 2) {
uint32_t tdelta; /* µs */
if (usec_cmp(m->now, usec_mul(m->interval, 2)) > 0) {
usec_t tdelta; /* µs */
struct tp_touch *t;
/* The current time is > 2 times the interval so we
* have a jump. Fix the motion history */
tdelta = m->now - m->interval;
tdelta = usec_delta(m->now, m->interval);
tp_for_each_touch(tp, t) {
tp_motion_history_fix_last(tp,
@ -1588,7 +1591,9 @@ tp_process_msc_timestamp(struct tp_dispatch *tp, uint64_t time)
* only ever see those jumps over the first three events it
* doesn't matter.
*/
filter_restart(tp->device->pointer.filter, tp, time - tdelta);
filter_restart(tp->device->pointer.filter,
tp,
usec_sub(time, tdelta));
}
break;
case JUMP_STATE_IGNORE:
@ -1597,7 +1602,7 @@ tp_process_msc_timestamp(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_pre_process_state(struct tp_dispatch *tp, uint64_t time)
tp_pre_process_state(struct tp_dispatch *tp, usec_t time)
{
struct tp_touch *t;
@ -1619,7 +1624,7 @@ tp_pre_process_state(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_process_state(struct tp_dispatch *tp, uint64_t time)
tp_process_state(struct tp_dispatch *tp, usec_t time)
{
struct tp_touch *t;
bool restart_filter = false;
@ -1730,7 +1735,7 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_post_process_state(struct tp_dispatch *tp, uint64_t time)
tp_post_process_state(struct tp_dispatch *tp, usec_t time)
{
struct tp_touch *t;
@ -1764,7 +1769,7 @@ tp_post_process_state(struct tp_dispatch *tp, uint64_t time)
}
static void
tp_post_events(struct tp_dispatch *tp, uint64_t time)
tp_post_events(struct tp_dispatch *tp, usec_t time)
{
bool ignore_motion = false;
@ -1815,7 +1820,7 @@ tp_apply_rotation(struct evdev_device *device)
}
static void
tp_handle_state(struct tp_dispatch *tp, uint64_t time)
tp_handle_state(struct tp_dispatch *tp, usec_t time)
{
tp_pre_process_state(tp, time);
tp_process_state(tp, time);
@ -1853,7 +1858,7 @@ static void
tp_interface_process_event(struct evdev_dispatch *dispatch,
struct evdev_device *device,
struct evdev_event *e,
uint64_t time)
usec_t time)
{
struct tp_dispatch *tp = tp_dispatch(dispatch);
@ -1884,11 +1889,27 @@ static void
tp_interface_process(struct evdev_dispatch *dispatch,
struct evdev_device *device,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
size_t nevents;
struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
struct evdev_event *ev_syn = &events[nevents - 1];
if (evdev_usage_enum(ev_syn->usage) == EVDEV_SYN_REPORT) {
/* A SYN_REPORT 1 event is a kernel-inserted SYN_REPORT.
* This happens most commonly on key repeat but in the
* touchpad code this causes issues with
* timestamp deltas (see e.g. #1145).
*
* Let's drop this frame, hoping it wasn't important.
*/
if (ev_syn->value == 1) {
return;
}
} else {
evdev_log_bug_libinput(device, "Terminating event is not a SYN_REPORT");
}
for (size_t i = 0; i < nevents; i++) {
tp_interface_process_event(dispatch, device, &events[i], time);
}
@ -1961,7 +1982,7 @@ tp_release_fake_touches(struct tp_dispatch *tp)
static void
tp_clear_state(struct tp_dispatch *tp)
{
uint64_t now = libinput_now(tp_libinput_context(tp));
usec_t now = libinput_now(tp_libinput_context(tp));
struct tp_touch *t;
/* Unroll the touchpad state.
@ -2085,7 +2106,7 @@ tp_resume(struct tp_dispatch *tp,
}
static void
tp_trackpoint_timeout(uint64_t now, void *data)
tp_trackpoint_timeout(usec_t now, void *data)
{
struct tp_dispatch *tp = data;
@ -2097,7 +2118,7 @@ tp_trackpoint_timeout(uint64_t now, void *data)
}
static void
tp_trackpoint_event(uint64_t time, struct libinput_event *event, void *data)
tp_trackpoint_event(usec_t time, struct libinput_event *event, void *data)
{
struct tp_dispatch *tp = data;
@ -2115,7 +2136,7 @@ tp_trackpoint_event(uint64_t time, struct libinput_event *event, void *data)
/* Require at least three events before enabling palm detection */
if (tp->palm.trackpoint_event_count < 3) {
libinput_timer_set(&tp->palm.trackpoint_timer,
time + DEFAULT_TRACKPOINT_EVENT_TIMEOUT);
usec_add(time, DEFAULT_TRACKPOINT_EVENT_TIMEOUT));
return;
}
@ -2125,18 +2146,18 @@ tp_trackpoint_event(uint64_t time, struct libinput_event *event, void *data)
}
libinput_timer_set(&tp->palm.trackpoint_timer,
time + DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT);
usec_add(time, tp->palm.timeout));
}
static void
tp_keyboard_timeout(uint64_t now, void *data)
tp_keyboard_timeout(usec_t now, void *data)
{
struct tp_dispatch *tp = data;
if (tp->dwt.dwt_enabled &&
long_any_bit_set(tp->dwt.key_mask, ARRAY_LENGTH(tp->dwt.key_mask))) {
libinput_timer_set(&tp->dwt.keyboard_timer,
now + DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2);
usec_add(now, tp->dwt.timeout));
tp->dwt.keyboard_last_press_time = now;
evdev_log_debug(tp->device, "palm: keyboard timeout refresh\n");
return;
@ -2198,11 +2219,11 @@ tp_key_ignore_for_dwt(unsigned int keycode)
}
static void
tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data)
tp_keyboard_event(usec_t time, struct libinput_event *event, void *data)
{
struct tp_dispatch *tp = data;
struct libinput_event_keyboard *kbdev;
unsigned int timeout;
usec_t timeout;
unsigned int key;
bool is_modifier;
@ -2251,12 +2272,12 @@ tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data)
tp->dwt.keyboard_active = true;
timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1;
} else {
timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2;
timeout = tp->dwt.timeout;
}
tp->dwt.keyboard_last_press_time = time;
long_set_bit(tp->dwt.key_mask, key);
libinput_timer_set(&tp->dwt.keyboard_timer, time + timeout);
libinput_timer_set(&tp->dwt.keyboard_timer, usec_add(time, timeout));
}
static bool
@ -2342,7 +2363,7 @@ tp_pair_trackpoint(struct evdev_device *touchpad, struct evdev_device *trackpoin
}
static void
tp_lid_switch_event(uint64_t time, struct libinput_event *event, void *data)
tp_lid_switch_event(usec_t time, struct libinput_event *event, void *data)
{
struct tp_dispatch *tp = data;
struct libinput_event_switch *swev;
@ -2367,7 +2388,7 @@ tp_lid_switch_event(uint64_t time, struct libinput_event *event, void *data)
}
static void
tp_tablet_mode_switch_event(uint64_t time, struct libinput_event *event, void *data)
tp_tablet_mode_switch_event(usec_t time, struct libinput_event *event, void *data)
{
struct tp_dispatch *tp = data;
struct libinput_event_switch *swev;
@ -2673,7 +2694,7 @@ evdev_tag_touchpad(struct evdev_device *device, struct udev_device *udev_device)
}
static void
tp_arbitration_timeout(uint64_t now, void *data)
tp_arbitration_timeout(usec_t now, void *data)
{
struct tp_dispatch *tp = data;
@ -2686,7 +2707,7 @@ tp_interface_toggle_touch(struct evdev_dispatch *dispatch,
struct evdev_device *device,
enum evdev_arbitration_state which,
const struct phys_rect *rect,
uint64_t time)
usec_t time)
{
struct tp_dispatch *tp = tp_dispatch(dispatch);
@ -2709,7 +2730,7 @@ tp_interface_toggle_touch(struct evdev_dispatch *dispatch,
* arbitration by just a little bit so that any touch in
* event is caught as palm touch. */
libinput_timer_set(&tp->arbitration.arbitration_timer,
time + ms2us(90));
usec_add_millis(time, 90));
break;
}
}
@ -2738,6 +2759,32 @@ tp_interface_left_handed_toggled(struct evdev_dispatch *dispatch,
tp_change_rotation(device, DONT_NOTIFY);
}
static void
tp_interface_disable_feature(struct evdev_dispatch *dispatch,
enum libinput_feature feature)
{
struct tp_dispatch *tp = tp_dispatch(dispatch);
switch (feature) {
case LIBINPUT_FEATURE_TOUCHPAD_JUMP_DETECTION:
tp->jump.detection_disabled = true;
break;
case LIBINPUT_FEATURE_TOUCHPAD_HYSTERESIS:
tp->hysteresis.enabled = false;
break;
case LIBINPUT_FEATURE_TOUCHPAD_PALM_DETECTION:
tp->palm.use_mt_tool = false;
tp->palm.use_pressure = false;
tp->palm.use_size = false;
tp->palm.right_edge = INT_MAX;
tp->palm.left_edge = INT_MIN;
tp->palm.upper_edge = INT_MIN;
break;
default:
return;
}
}
static struct evdev_dispatch_interface tp_interface = {
.process = tp_interface_process,
.suspend = tp_interface_suspend,
@ -2752,6 +2799,7 @@ static struct evdev_dispatch_interface tp_interface = {
.touch_arbitration_update_rect = NULL,
.get_switch_state = NULL,
.left_handed_toggle = tp_interface_left_handed_toggled,
.disable_feature = tp_interface_disable_feature,
};
static void
@ -2890,12 +2938,12 @@ tp_init_accel(struct tp_dispatch *tp, enum libinput_config_accel_profile which)
tp->device->model_flags & EVDEV_MODEL_LENOVO_X220_TOUCHPAD_FW81) {
filter = create_pointer_accelerator_filter_lenovo_x230(dpi, use_v_avg);
} else {
uint64_t eds_threshold = 0;
uint64_t eds_value = 0;
usec_t eds_threshold = usec_from_uint64_t(0);
usec_t eds_value = usec_from_uint64_t(0);
if (libevdev_get_id_bustype(device->evdev) == BUS_BLUETOOTH) {
eds_threshold = ms2us(50);
eds_value = ms2us(10);
eds_threshold = usec_from_millis(50);
eds_value = usec_from_millis(10);
}
filter = create_pointer_accelerator_filter_touchpad(dpi,
eds_threshold,
@ -2984,7 +3032,7 @@ tp_scroll_config_scroll_method_set_method(struct libinput_device *device,
{
struct evdev_device *evdev = evdev_device(device);
struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
uint64_t time = libinput_now(tp_libinput_context(tp));
usec_t time = libinput_now(tp_libinput_context(tp));
if (method == tp->scroll.method)
return LIBINPUT_CONFIG_STATUS_SUCCESS;
@ -3122,6 +3170,30 @@ tp_dwt_config_get_default(struct libinput_device *device)
: LIBINPUT_CONFIG_DWT_DISABLED;
}
static enum libinput_config_status
tp_dwt_config_set_timeout(struct libinput_device *device, usec_t timeout)
{
struct evdev_device *evdev = evdev_device(device);
struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
if (usec_cmp(timeout, usec_from_millis(100)) < 0 ||
usec_cmp(timeout, usec_from_millis(5000)) > 0)
return LIBINPUT_CONFIG_STATUS_INVALID;
tp->dwt.timeout = timeout;
return LIBINPUT_CONFIG_STATUS_SUCCESS;
}
static usec_t
tp_dwt_config_get_timeout(struct libinput_device *device)
{
struct evdev_device *evdev = evdev_device(device);
struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
return tp->dwt.timeout;
}
static int
tp_dwtp_config_is_available(struct libinput_device *device)
{
@ -3174,6 +3246,30 @@ tp_dwtp_config_get_default(struct libinput_device *device)
: LIBINPUT_CONFIG_DWTP_DISABLED;
}
static enum libinput_config_status
tp_dwtp_config_set_timeout(struct libinput_device *device, usec_t timeout)
{
struct evdev_device *evdev = evdev_device(device);
struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
if (usec_cmp(timeout, usec_from_millis(100)) < 0 ||
usec_cmp(timeout, usec_from_millis(5000)) > 0)
return LIBINPUT_CONFIG_STATUS_INVALID;
tp->palm.timeout = timeout;
return LIBINPUT_CONFIG_STATUS_SUCCESS;
}
static usec_t
tp_dwtp_config_get_timeout(struct libinput_device *device)
{
struct evdev_device *evdev = evdev_device(device);
struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
return tp->palm.timeout;
}
static inline bool
tp_is_tpkb_combo_below(struct evdev_device *device)
{
@ -3210,7 +3306,10 @@ tp_init_dwt(struct tp_dispatch *tp, struct evdev_device *device)
tp->dwt.config.set_enabled = tp_dwt_config_set;
tp->dwt.config.get_enabled = tp_dwt_config_get;
tp->dwt.config.get_default_enabled = tp_dwt_config_get_default;
tp->dwt.config.set_timeout = tp_dwt_config_set_timeout;
tp->dwt.config.get_timeout = tp_dwt_config_get_timeout;
tp->dwt.dwt_enabled = tp_dwt_default_enabled(tp);
tp->dwt.timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2;
device->base.config.dwt = &tp->dwt.config;
}
@ -3226,6 +3325,9 @@ tp_init_dwtp(struct tp_dispatch *tp, struct evdev_device *device)
tp->palm.config.set_enabled = tp_dwtp_config_set;
tp->palm.config.get_enabled = tp_dwtp_config_get;
tp->palm.config.get_default_enabled = tp_dwtp_config_get_default;
tp->palm.config.set_timeout = tp_dwtp_config_set_timeout;
tp->palm.config.get_timeout = tp_dwtp_config_get_timeout;
tp->palm.timeout = DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT;
device->base.config.dwtp = &tp->palm.config;
}
@ -3574,8 +3676,8 @@ tp_init_pressurepad(struct tp_dispatch *tp, struct evdev_device *device)
*
* See also #562
*/
if (libevdev_get_abs_resolution(device->evdev, ABS_MT_PRESSURE) != 0 ||
evdev_device_has_model_quirk(device, QUIRK_MODEL_PRESSURE_PAD)) {
if (libevdev_has_property(device->evdev, INPUT_PROP_PRESSUREPAD) ||
libevdev_get_abs_resolution(device->evdev, ABS_MT_PRESSURE) != 0) {
libevdev_disable_event_code(device->evdev, EV_ABS, ABS_MT_PRESSURE);
libevdev_disable_event_code(device->evdev, EV_ABS, ABS_PRESSURE);
}
@ -3607,7 +3709,7 @@ tp_init(struct tp_dispatch *tp, struct evdev_device *device)
tp_init_pressure(tp, device);
/* 5 warnings per 24 hours should be enough */
ratelimit_init(&tp->jump.warning, h2us(24), 5);
ratelimit_init(&tp->jump.warning, usec_from_hours(24), 5);
/* Set the dpi to that of the x axis, because that's what we normalize
to when needed*/
@ -3742,7 +3844,7 @@ static bool
tp_requires_rotation(struct tp_dispatch *tp, struct evdev_device *device)
{
bool rotate = false;
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
struct libinput *li = tp_libinput_context(tp);
WacomDeviceDatabase *db = NULL;
WacomDevice **devices = NULL, **d;

View file

@ -194,7 +194,7 @@ struct tp_touch {
bool has_ended; /* TRACKING_ID == -1 */
bool dirty;
struct device_coords point;
uint64_t initial_time;
usec_t initial_time;
int pressure;
bool is_tool_palm; /* MT_TOOL_PALM */
int major, minor;
@ -213,7 +213,7 @@ struct tp_touch {
struct {
struct tp_history_point {
uint64_t time;
usec_t time;
struct device_coords point;
} samples[TOUCHPAD_HISTORY_LENGTH];
unsigned int index;
@ -246,7 +246,7 @@ struct tp_touch {
struct libinput_timer timer;
struct device_coords initial;
bool has_moved; /* has moved more than threshold */
uint64_t initial_time;
usec_t initial_time;
} button;
struct {
@ -267,7 +267,7 @@ struct tp_touch {
struct {
enum touch_palm_state state;
struct device_coords first; /* first coordinates if is_palm == true */
uint64_t time; /* first timestamp if is_palm == true */
usec_t time; /* first timestamp if is_palm == true */
} palm;
struct {
@ -344,7 +344,7 @@ struct tp_dispatch {
bool enabled;
struct device_coords margin;
unsigned int other_event_count;
uint64_t last_motion_time;
usec_t last_motion_time;
} hysteresis;
struct {
@ -361,7 +361,7 @@ struct tp_dispatch {
struct libinput_timer finger_count_switch_timer;
enum tp_gesture_state state;
struct tp_touch *touches[2];
uint64_t initial_time;
usec_t initial_time;
double initial_distance;
double prev_scale;
double angle;
@ -370,7 +370,7 @@ struct tp_dispatch {
bool hold_enabled;
struct libinput_timer drag_3fg_timer;
uint64_t drag_3fg_release_time;
usec_t drag_3fg_release_time;
} gesture;
struct {
@ -421,9 +421,9 @@ struct tp_dispatch {
bool h, v;
} active;
struct phys_coords vector;
uint64_t time_prev;
usec_t time_prev;
struct {
uint64_t h, v;
usec_t h, v;
} duration;
} scroll;
@ -436,7 +436,7 @@ struct tp_dispatch {
struct libinput_timer timer;
enum tp_tap_state state;
uint32_t buttons_pressed;
uint64_t saved_press_time, saved_release_time;
usec_t saved_press_time, saved_release_time;
enum libinput_config_tap_button_map map;
enum libinput_config_tap_button_map want_map;
@ -457,6 +457,7 @@ struct tp_dispatch {
struct {
struct libinput_device_config_dwtp config;
bool dwtp_enabled;
usec_t timeout;
int32_t right_edge; /* in device coordinates */
int32_t left_edge; /* in device coordinates */
@ -465,7 +466,7 @@ struct tp_dispatch {
bool trackpoint_active;
struct libinput_event_listener trackpoint_listener;
struct libinput_timer trackpoint_timer;
uint64_t trackpoint_last_event_time;
usec_t trackpoint_last_event_time;
uint32_t trackpoint_event_count;
bool monitor_trackpoint;
@ -486,6 +487,7 @@ struct tp_dispatch {
struct {
struct libinput_device_config_dwt config;
bool dwt_enabled;
usec_t timeout;
/* We have to allow for more than one device node to be the
* internal dwt keyboard (Razer Blade). But they're the same
@ -498,7 +500,7 @@ struct tp_dispatch {
unsigned long mod_mask[NLONGS(KEY_CNT)];
bool keyboard_active;
struct libinput_timer keyboard_timer;
uint64_t keyboard_last_press_time;
usec_t keyboard_last_press_time;
} dwt;
struct {
@ -529,8 +531,8 @@ struct tp_dispatch {
struct msc_timestamp {
enum tp_jump_state state;
uint32_t interval;
uint32_t now;
usec_t interval;
usec_t now;
} msc_timestamp;
} quirks;
@ -614,17 +616,17 @@ tp_get_delta(struct tp_touch *t);
struct normalized_coords
tp_filter_motion(struct tp_dispatch *tp,
const struct device_float_coords *unaccelerated,
uint64_t time);
usec_t time);
struct normalized_coords
tp_filter_motion_unaccelerated(struct tp_dispatch *tp,
const struct device_float_coords *unaccelerated,
uint64_t time);
usec_t time);
struct normalized_coords
tp_filter_scroll(struct tp_dispatch *tp,
const struct device_float_coords *unaccelerated,
uint64_t time);
usec_t time);
bool
tp_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t);
@ -633,7 +635,7 @@ bool
tp_touch_active_for_gesture(const struct tp_dispatch *tp, const struct tp_touch *t);
int
tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time);
tp_tap_handle_state(struct tp_dispatch *tp, usec_t time);
void
tp_tap_post_process_state(struct tp_dispatch *tp);
@ -659,16 +661,16 @@ void
tp_remove_buttons(struct tp_dispatch *tp);
void
tp_process_button(struct tp_dispatch *tp, const struct evdev_event *e, uint64_t time);
tp_process_button(struct tp_dispatch *tp, const struct evdev_event *e, usec_t time);
void
tp_release_all_buttons(struct tp_dispatch *tp, uint64_t time);
tp_release_all_buttons(struct tp_dispatch *tp, usec_t time);
int
tp_post_button_events(struct tp_dispatch *tp, uint64_t time);
tp_post_button_events(struct tp_dispatch *tp, usec_t time);
void
tp_button_handle_state(struct tp_dispatch *tp, uint64_t time);
tp_button_handle_state(struct tp_dispatch *tp, usec_t time);
bool
tp_button_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t);
@ -678,13 +680,13 @@ tp_button_is_inside_softbutton_area(const struct tp_dispatch *tp,
const struct tp_touch *t);
void
tp_release_all_taps(struct tp_dispatch *tp, uint64_t now);
tp_release_all_taps(struct tp_dispatch *tp, usec_t now);
void
tp_tap_suspend(struct tp_dispatch *tp, uint64_t time);
tp_tap_suspend(struct tp_dispatch *tp, usec_t time);
void
tp_tap_resume(struct tp_dispatch *tp, uint64_t time);
tp_tap_resume(struct tp_dispatch *tp, usec_t time);
bool
tp_tap_dragging(const struct tp_dispatch *tp);
@ -699,13 +701,13 @@ void
tp_remove_edge_scroll(struct tp_dispatch *tp);
void
tp_edge_scroll_handle_state(struct tp_dispatch *tp, uint64_t time);
tp_edge_scroll_handle_state(struct tp_dispatch *tp, usec_t time);
int
tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time);
tp_edge_scroll_post_events(struct tp_dispatch *tp, usec_t time);
void
tp_edge_scroll_stop_events(struct tp_dispatch *tp, uint64_t time);
tp_edge_scroll_stop_events(struct tp_dispatch *tp, usec_t time);
int
tp_edge_scroll_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t);
@ -720,25 +722,25 @@ void
tp_remove_gesture(struct tp_dispatch *tp);
void
tp_gesture_stop(struct tp_dispatch *tp, uint64_t time);
tp_gesture_stop(struct tp_dispatch *tp, usec_t time);
void
tp_gesture_cancel(struct tp_dispatch *tp, uint64_t time);
tp_gesture_cancel(struct tp_dispatch *tp, usec_t time);
void
tp_gesture_cancel_motion_gestures(struct tp_dispatch *tp, uint64_t time);
tp_gesture_cancel_motion_gestures(struct tp_dispatch *tp, usec_t time);
void
tp_gesture_update_finger_state(struct tp_dispatch *tp, uint64_t time);
tp_gesture_update_finger_state(struct tp_dispatch *tp, usec_t time);
void
tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time, bool ignore_motion);
tp_gesture_post_events(struct tp_dispatch *tp, usec_t time, bool ignore_motion);
void
tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time);
tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, usec_t time);
void
tp_gesture_tap_timeout(struct tp_dispatch *tp, uint64_t time);
tp_gesture_tap_timeout(struct tp_dispatch *tp, usec_t time);
void
tp_clickpad_middlebutton_apply_config(struct evdev_device *device);
@ -759,7 +761,7 @@ void
tp_thumb_suppress(struct tp_dispatch *tp, struct tp_touch *t);
void
tp_thumb_update_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time);
tp_thumb_update_touch(struct tp_dispatch *tp, struct tp_touch *t, usec_t time);
void
tp_detect_thumb_while_moving(struct tp_dispatch *tp);

View file

@ -29,72 +29,11 @@
#include "evdev-plugin.h"
#include "evdev.h"
_unused_ static inline void
evdev_print_frame(struct evdev_device *device,
struct evdev_frame *frame,
uint64_t time_in_us)
{
static uint32_t offset = 0;
static uint32_t last_time = 0;
uint32_t time = us2ms(time_in_us);
if (offset == 0) {
offset = time;
last_time = time - offset;
}
time -= offset;
size_t nevents;
struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
for (size_t i = 0; i < nevents; i++) {
struct evdev_event *e = &events[i];
switch (evdev_usage_enum(e->usage)) {
case EVDEV_SYN_REPORT:
evdev_log_debug(
device,
"%u.%03u ----------------- EV_SYN ----------------- +%ums\n",
time / 1000,
time % 1000,
time - last_time);
last_time = time;
break;
case EVDEV_MSC_SERIAL:
evdev_log_debug(device,
"%u.%03u %-16s %-16s %#010x\n",
time / 1000,
time % 1000,
evdev_event_get_type_name(e),
evdev_event_get_code_name(e),
e->value);
break;
default:
evdev_log_debug(device,
"%u.%03u %-16s %-20s %4d\n",
time / 1000,
time % 1000,
evdev_event_get_type_name(e),
evdev_event_get_code_name(e),
e->value);
break;
}
}
}
static inline void
evdev_process_frame(struct evdev_device *device,
struct evdev_frame *frame,
uint64_t time)
evdev_process_frame(struct evdev_device *device, struct evdev_frame *frame, usec_t time)
{
struct evdev_dispatch *dispatch = device->dispatch;
#if EVENT_DEBUGGING
evdev_print_frame(device, frame, time);
#endif
libinput_timer_flush(evdev_libinput_context(device), time);
dispatch->interface->process(dispatch, device, frame, time);
@ -106,7 +45,7 @@ evdev_device_dispatch_frame(struct libinput_plugin *plugin,
struct evdev_frame *frame)
{
struct evdev_device *device = evdev_device(libinput_device);
uint64_t time = evdev_frame_get_time(frame);
usec_t time = evdev_frame_get_time(frame);
evdev_process_frame(device, frame, time);
/* Discard event to make the plugin system aware we're done */

View file

@ -28,7 +28,7 @@
#include "evdev-tablet-pad.h"
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
#include <libwacom/libwacom.h>
#endif
@ -114,7 +114,7 @@ pad_led_destroy(struct libinput *libinput, struct pad_mode_led *led)
free(led);
}
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
static inline struct pad_mode_led *
pad_led_new(struct libinput *libinput, const char *prefix, int group, int mode)
{
@ -198,7 +198,7 @@ pad_get_mode_group(struct pad_dispatch *pad, unsigned int index)
return NULL;
}
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
static inline bool
is_litest_device(struct evdev_device *device)
{
@ -618,7 +618,7 @@ pad_init_leds(struct pad_dispatch *pad, struct evdev_device *device, WacomDevice
}
/* If libwacom fails, we init one fallback group anyway */
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
rc = pad_init_leds_from_libwacom(pad, device, wacom);
#endif
if (rc != 0)

View file

@ -31,7 +31,7 @@
#include "evdev-tablet-pad.h"
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
#include <libwacom/libwacom.h>
#endif
@ -99,7 +99,7 @@ static void
pad_process_relative(struct pad_dispatch *pad,
struct evdev_device *device,
struct evdev_event *e,
uint64_t time)
usec_t time)
{
switch (evdev_usage_enum(e->usage)) {
case EVDEV_REL_DIAL:
@ -172,7 +172,7 @@ static void
pad_process_absolute(struct pad_dispatch *pad,
struct evdev_device *device,
struct evdev_event *e,
uint64_t time)
usec_t time)
{
enum pad_axes axis = PAD_AXIS_NONE;
@ -303,11 +303,15 @@ pad_dial_get_mode_group(struct pad_dispatch *pad, unsigned int dial)
struct libinput_tablet_pad_mode_group *group;
list_for_each(group, &pad->modes.mode_group_list, link) {
assert(group != NULL); /* for clang-tidy */
if (libinput_tablet_pad_mode_group_has_dial(group, dial))
return group;
}
assert(!"Unable to find dial mode group");
evdev_log_bug_libinput_ratelimit(pad->device,
&pad->modes.group_not_found,
"Unable to find mode group for dial %d\n",
dial);
return NULL;
}
@ -318,11 +322,15 @@ pad_ring_get_mode_group(struct pad_dispatch *pad, unsigned int ring)
struct libinput_tablet_pad_mode_group *group;
list_for_each(group, &pad->modes.mode_group_list, link) {
assert(group != NULL); /* for clang-tidy */
if (libinput_tablet_pad_mode_group_has_ring(group, ring))
return group;
}
assert(!"Unable to find ring mode group");
evdev_log_bug_libinput_ratelimit(pad->device,
&pad->modes.group_not_found,
"Unable to find mode group for ring %d\n",
ring);
return NULL;
}
@ -333,11 +341,15 @@ pad_strip_get_mode_group(struct pad_dispatch *pad, unsigned int strip)
struct libinput_tablet_pad_mode_group *group;
list_for_each(group, &pad->modes.mode_group_list, link) {
assert(group != NULL); /* for clang-tidy */
if (libinput_tablet_pad_mode_group_has_strip(group, strip))
return group;
}
assert(!"Unable to find strip mode group");
evdev_log_bug_libinput_ratelimit(pad->device,
&pad->modes.group_not_found,
"Unable to find mode group for strip %d\n",
strip);
return NULL;
}
@ -345,7 +357,7 @@ pad_strip_get_mode_group(struct pad_dispatch *pad, unsigned int strip)
static void
pad_check_notify_axes(struct pad_dispatch *pad,
struct evdev_device *device,
uint64_t time)
usec_t time)
{
struct libinput_device *base = &device->base;
struct libinput_tablet_pad_mode_group *group;
@ -362,11 +374,13 @@ pad_check_notify_axes(struct pad_dispatch *pad,
* so we can't set a source */
if (pad->changed_axes & PAD_AXIS_DIAL1) {
group = pad_dial_get_mode_group(pad, 0);
if (group)
tablet_pad_notify_dial(base, time, 0, pad->dials.dial1, group);
}
if (pad->changed_axes & PAD_AXIS_DIAL2) {
group = pad_dial_get_mode_group(pad, 1);
if (group)
tablet_pad_notify_dial(base, time, 1, pad->dials.dial2, group);
}
@ -376,6 +390,7 @@ pad_check_notify_axes(struct pad_dispatch *pad,
value = -1.0;
group = pad_ring_get_mode_group(pad, 0);
if (group)
tablet_pad_notify_ring(base,
time,
0,
@ -390,6 +405,7 @@ pad_check_notify_axes(struct pad_dispatch *pad,
value = -1.0;
group = pad_ring_get_mode_group(pad, 1);
if (group)
tablet_pad_notify_ring(base,
time,
1,
@ -404,6 +420,7 @@ pad_check_notify_axes(struct pad_dispatch *pad,
value = -1.0;
group = pad_strip_get_mode_group(pad, 0);
if (group)
tablet_pad_notify_strip(base,
time,
0,
@ -418,6 +435,7 @@ pad_check_notify_axes(struct pad_dispatch *pad,
value = -1.0;
group = pad_strip_get_mode_group(pad, 1);
if (group)
tablet_pad_notify_strip(base,
time,
1,
@ -434,7 +452,7 @@ static void
pad_process_key(struct pad_dispatch *pad,
struct evdev_device *device,
struct evdev_event *e,
uint64_t time)
usec_t time)
{
uint32_t is_press = e->value != 0;
@ -463,7 +481,7 @@ pad_button_get_mode_group(struct pad_dispatch *pad, unsigned int button)
static void
pad_notify_button_mask(struct pad_dispatch *pad,
struct evdev_device *device,
uint64_t time,
usec_t time,
const struct button_state *buttons,
enum libinput_button_state state)
{
@ -519,7 +537,7 @@ pad_notify_button_mask(struct pad_dispatch *pad,
static void
pad_notify_buttons(struct pad_dispatch *pad,
struct evdev_device *device,
uint64_t time,
usec_t time,
enum libinput_button_state state)
{
struct button_state buttons;
@ -547,7 +565,7 @@ pad_change_to_left_handed(struct evdev_device *device)
}
static void
pad_flush(struct pad_dispatch *pad, struct evdev_device *device, uint64_t time)
pad_flush(struct pad_dispatch *pad, struct evdev_device *device, usec_t time)
{
if (pad_has_status(pad, PAD_AXES_UPDATED)) {
pad_check_notify_axes(pad, device, time);
@ -576,7 +594,7 @@ static void
pad_process_event(struct evdev_dispatch *dispatch,
struct evdev_device *device,
struct evdev_event *e,
uint64_t time)
usec_t time)
{
struct pad_dispatch *pad = pad_dispatch(dispatch);
@ -611,7 +629,7 @@ static void
pad_process(struct evdev_dispatch *dispatch,
struct evdev_device *device,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
size_t nevents;
struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
@ -667,7 +685,7 @@ pad_init_buttons_from_libwacom(struct pad_dispatch *pad,
WacomDevice *tablet)
{
bool rc = false;
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
if (tablet) {
int num_buttons = libwacom_get_num_buttons(tablet);
@ -759,7 +777,7 @@ pad_init_left_handed(struct evdev_device *device, WacomDevice *wacom)
{
bool has_left_handed = true;
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
has_left_handed = !wacom || libwacom_is_reversible(wacom);
#endif
if (has_left_handed)
@ -772,7 +790,7 @@ pad_init(struct pad_dispatch *pad, struct evdev_device *device)
int rc = 1;
struct libinput *li = evdev_libinput_context(device);
WacomDevice *wacom = NULL;
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
WacomDeviceDatabase *db = libinput_libwacom_ref(li);
if (db) {
char event_path[64];
@ -804,6 +822,7 @@ pad_init(struct pad_dispatch *pad, struct evdev_device *device)
pad->device = device;
pad->status = PAD_NONE;
pad->changed_axes = PAD_AXIS_NONE;
ratelimit_init(&pad->modes.group_not_found, usec_from_hours(1), 3);
/* We expect the kernel to either give us both axes as hires or neither.
* Getting one is a kernel bug we don't need to care about */
@ -823,9 +842,9 @@ pad_init(struct pad_dispatch *pad, struct evdev_device *device)
rc = pad_init_leds(pad, device, wacom);
/* at most 5 "Multiple EV_ABS events" log messages per hour */
ratelimit_init(&pad->duplicate_abs_limit, s2us(60 * 60), 5);
ratelimit_init(&pad->duplicate_abs_limit, usec_from_seconds(60 * 60), 5);
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
if (wacom)
libwacom_destroy(wacom);
if (db)

View file

@ -26,7 +26,7 @@
#include "evdev.h"
#if !HAVE_LIBWACOM
#ifndef HAVE_LIBWACOM
typedef void *WacomDevice;
#endif
@ -87,6 +87,7 @@ struct pad_dispatch {
struct {
struct list mode_group_list;
struct ratelimit group_not_found;
} modes;
struct ratelimit duplicate_abs_limit;

View file

@ -31,7 +31,7 @@
#include "evdev-tablet.h"
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
#include <libwacom/libwacom.h>
#else
typedef void *WacomStylus;
@ -197,7 +197,7 @@ static void
tablet_process_absolute(struct tablet_dispatch *tablet,
struct evdev_device *device,
struct evdev_event *e,
uint64_t time)
usec_t time)
{
enum libinput_tablet_tool_axis axis;
@ -557,7 +557,7 @@ tablet_tool_process_delta(struct tablet_dispatch *tablet,
struct libinput_tablet_tool *tool,
const struct evdev_device *device,
struct tablet_axes *axes,
uint64_t time)
usec_t time)
{
const struct normalized_coords zero = { 0.0, 0.0 };
struct device_coords delta = { 0, 0 };
@ -759,7 +759,7 @@ tablet_check_notify_axes(struct tablet_dispatch *tablet,
struct evdev_device *device,
struct libinput_tablet_tool *tool,
struct tablet_axes *axes_out,
uint64_t time)
usec_t time)
{
struct tablet_axes axes = { 0 };
const char tmp[sizeof(tablet->changed_axes)] = { 0 };
@ -884,7 +884,7 @@ static void
tablet_process_key(struct tablet_dispatch *tablet,
struct evdev_device *device,
struct evdev_event *e,
uint64_t time)
usec_t time)
{
enum libinput_tablet_tool_type type;
@ -930,7 +930,7 @@ static void
tablet_process_relative(struct tablet_dispatch *tablet,
struct evdev_device *device,
struct evdev_event *e,
uint64_t time)
usec_t time)
{
enum libinput_tablet_tool_axis axis;
@ -960,7 +960,7 @@ static void
tablet_process_misc(struct tablet_dispatch *tablet,
struct evdev_device *device,
struct evdev_event *e,
uint64_t time)
usec_t time)
{
switch (evdev_usage_enum(e->usage)) {
case EVDEV_MSC_SERIAL:
@ -1004,7 +1004,7 @@ tool_set_bits_from_libwacom(const struct tablet_dispatch *tablet,
const WacomStylus *s)
{
bool rc = false;
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
int code;
WacomStylusType type;
WacomAxisTypeFlags axes;
@ -1221,7 +1221,7 @@ pressure_range_set(struct libinput_tablet_tool *tool, double min, double max)
tool->pressure.wanted_range.min = min;
tool->pressure.wanted_range.max = max;
tool->pressure.has_configured_range = true;
tool->pressure.has_configured_range = min != 0.0 || max != 1.0;
return LIBINPUT_CONFIG_STATUS_SUCCESS;
}
@ -1272,11 +1272,14 @@ static void
eraser_button_toggle(struct libinput_tablet_tool *tool)
{
struct libinput_device *libinput_device = tool->last_device;
if (libinput_device) {
struct evdev_device *device = evdev_device(libinput_device);
struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch);
tablet_tool_apply_eraser_button(tablet, tool);
}
}
static enum libinput_config_status
eraser_button_set_mode(struct libinput_tablet_tool *tool,
@ -1296,7 +1299,7 @@ eraser_button_set_mode(struct libinput_tablet_tool *tool,
static enum libinput_config_eraser_button_mode
eraser_button_get_mode(struct libinput_tablet_tool *tool)
{
return tool->eraser_button.mode;
return tool->eraser_button.want_mode;
}
static enum libinput_config_eraser_button_mode
@ -1330,7 +1333,7 @@ eraser_button_set_button(struct libinput_tablet_tool *tool, uint32_t button)
static unsigned int
eraser_button_get_button(struct libinput_tablet_tool *tool)
{
return tool->eraser_button.button;
return tool->eraser_button.want_button;
}
static unsigned int
@ -1369,7 +1372,7 @@ tool_init_eraser_button(struct tablet_dispatch *tablet,
if (libinput_tablet_tool_get_type(tool) != LIBINPUT_TABLET_TOOL_TYPE_PEN)
return;
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
/* libwacom's API is a bit terrible here:
* - has_eraser is true on styli that have a separate eraser, all
* those are INVERT so we can exclude them
@ -1400,7 +1403,7 @@ tablet_new_tool(struct tablet_dispatch *tablet,
{
struct libinput_tablet_tool *tool = zalloc(sizeof *tool);
const WacomStylus *s = NULL;
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
WacomDeviceDatabase *db;
db = tablet_libinput_context(tablet)->libwacom.db;
@ -1519,7 +1522,7 @@ tablet_get_tool(struct tablet_dispatch *tablet,
static void
tablet_notify_button_mask(struct tablet_dispatch *tablet,
struct evdev_device *device,
uint64_t time,
usec_t time,
struct libinput_tablet_tool *tool,
const struct button_state *buttons,
enum libinput_button_state state)
@ -1553,7 +1556,7 @@ tablet_notify_button_mask(struct tablet_dispatch *tablet,
static void
tablet_notify_buttons(struct tablet_dispatch *tablet,
struct evdev_device *device,
uint64_t time,
usec_t time,
struct libinput_tablet_tool *tool,
enum libinput_button_state state)
{
@ -1762,8 +1765,9 @@ detect_pressure_offset(struct tablet_dispatch *tablet,
}
evdev_log_info(device,
"Pressure offset detected on tool %s (serial %#x). "
"Pressure offset of %d%% detected on tool %s (serial %#x). "
"See %s/tablet-support.html\n",
(int)(pressure_offset_as_double(offset) * 100),
tablet_tool_type_to_string(tool->type),
tool->serial,
HTTP_DOC_LINK);
@ -1913,7 +1917,7 @@ tablet_calculate_arbitration_rect(struct tablet_dispatch *tablet)
static inline void
tablet_update_touch_device_rect(struct tablet_dispatch *tablet,
const struct tablet_axes *axes,
uint64_t time)
usec_t time)
{
struct evdev_dispatch *dispatch;
struct phys_rect rect = { 0 };
@ -1937,7 +1941,7 @@ tablet_send_proximity_in(struct tablet_dispatch *tablet,
struct libinput_tablet_tool *tool,
struct evdev_device *device,
struct tablet_axes *axes,
uint64_t time)
usec_t time)
{
if (!tablet_has_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY))
return false;
@ -1965,7 +1969,7 @@ tablet_send_proximity_out(struct tablet_dispatch *tablet,
struct libinput_tablet_tool *tool,
struct evdev_device *device,
struct tablet_axes *axes,
uint64_t time)
usec_t time)
{
if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY) &&
!tablet_has_status(tablet, TABLET_TOOL_OUTSIDE_AREA)) {
@ -1985,7 +1989,7 @@ tablet_send_tip(struct tablet_dispatch *tablet,
struct libinput_tablet_tool *tool,
struct evdev_device *device,
struct tablet_axes *axes,
uint64_t time)
usec_t time)
{
if (tablet_has_status(tablet, TABLET_TOOL_ENTERING_CONTACT)) {
tablet_notify_tip(&device->base,
@ -2035,7 +2039,7 @@ tablet_send_axes(struct tablet_dispatch *tablet,
struct libinput_tablet_tool *tool,
struct evdev_device *device,
struct tablet_axes *axes,
uint64_t time)
usec_t time)
{
enum libinput_tablet_tool_tip_state tip_state;
@ -2065,7 +2069,7 @@ static inline void
tablet_send_buttons(struct tablet_dispatch *tablet,
struct libinput_tablet_tool *tool,
struct evdev_device *device,
uint64_t time)
usec_t time)
{
if (tablet_has_status(tablet, TABLET_BUTTONS_RELEASED)) {
tablet_notify_buttons(tablet,
@ -2090,7 +2094,7 @@ static void
tablet_send_events(struct tablet_dispatch *tablet,
struct libinput_tablet_tool *tool,
struct evdev_device *device,
uint64_t time)
usec_t time)
{
struct tablet_axes axes = { 0 };
@ -2127,7 +2131,7 @@ tablet_send_events(struct tablet_dispatch *tablet,
static void
tablet_update_tool_state(struct tablet_dispatch *tablet,
struct evdev_device *device,
uint64_t time)
usec_t time)
{
enum libinput_tablet_tool_type type;
uint32_t changed;
@ -2191,18 +2195,18 @@ update_pressure_range(struct tablet_dispatch *tablet,
threshold->threshold.upper = hi;
threshold->threshold.lower = lo;
if (threshold->has_offset)
set_pressure_offset(threshold, threshold->offset);
/* Disable any heuristics */
if (tool->pressure.has_configured_range) {
threshold->has_offset = true;
threshold->heuristic_state = PRESSURE_HEURISTIC_STATE_DONE;
threshold->offset = pressure_offset_from_double(0.0);
} else if (threshold->has_offset) {
set_pressure_offset(threshold, threshold->offset);
}
}
static void
tablet_flush(struct tablet_dispatch *tablet, struct evdev_device *device, uint64_t time)
tablet_flush(struct tablet_dispatch *tablet, struct evdev_device *device, usec_t time)
{
struct libinput_tablet_tool *tool;
@ -2286,7 +2290,7 @@ static inline void
tablet_set_touch_device_enabled(struct tablet_dispatch *tablet,
enum evdev_arbitration_state which,
const struct phys_rect *rect,
uint64_t time)
usec_t time)
{
struct evdev_device *touch_device = tablet->touch_device;
struct evdev_dispatch *dispatch;
@ -2308,7 +2312,7 @@ tablet_set_touch_device_enabled(struct tablet_dispatch *tablet,
static inline void
tablet_toggle_touch_device(struct tablet_dispatch *tablet,
struct evdev_device *tablet_device,
uint64_t time)
usec_t time)
{
enum evdev_arbitration_state which;
struct phys_rect r = { 0 };
@ -2355,7 +2359,7 @@ static void
tablet_process_event(struct evdev_dispatch *dispatch,
struct evdev_device *device,
struct evdev_event *e,
uint64_t time)
usec_t time)
{
struct tablet_dispatch *tablet = tablet_dispatch(dispatch);
@ -2391,7 +2395,7 @@ static void
tablet_process(struct evdev_dispatch *dispatch,
struct evdev_device *device,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
size_t nevents;
struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
@ -2406,7 +2410,7 @@ tablet_suspend(struct evdev_dispatch *dispatch, struct evdev_device *device)
{
struct tablet_dispatch *tablet = tablet_dispatch(dispatch);
struct libinput *li = tablet_libinput_context(tablet);
uint64_t now = libinput_now(li);
usec_t now = libinput_now(li);
tablet_set_touch_device_enabled(tablet, ARBITRATION_NOT_ACTIVE, NULL, now);
@ -2447,6 +2451,8 @@ tablet_destroy(struct evdev_dispatch *dispatch)
struct libinput *li = tablet_libinput_context(tablet);
list_for_each_safe(tool, &tablet->tool_list, link) {
list_remove(&tool->link);
list_init(&tool->link); /* unref may list_remove() too */
libinput_tablet_tool_unref(tool);
}
@ -2788,7 +2794,7 @@ tablet_init_left_handed(struct evdev_device *device, WacomDevice *wacom)
{
bool has_left_handed = true;
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
has_left_handed = !wacom || libwacom_is_reversible(wacom);
#endif
if (has_left_handed)
@ -2798,7 +2804,7 @@ tablet_init_left_handed(struct evdev_device *device, WacomDevice *wacom)
static inline bool
tablet_is_display_tablet(WacomDevice *wacom)
{
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
return !wacom ||
(libwacom_get_integration_flags(wacom) &
(WACOM_DEVICE_INTEGRATED_SYSTEM | WACOM_DEVICE_INTEGRATED_DISPLAY));
@ -2810,7 +2816,7 @@ tablet_is_display_tablet(WacomDevice *wacom)
static inline bool
tablet_is_aes(struct evdev_device *device, WacomDevice *wacom)
{
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
int vid = evdev_device_get_id_vendor(device);
/* Wacom-specific check for whether smoothing is required:
* libwacom keeps all the AES pens in a single group, so any device
@ -2943,7 +2949,7 @@ tablet_init(struct tablet_dispatch *tablet, struct evdev_device *device)
enum libinput_tablet_tool_axis axis;
int rc = -1;
WacomDevice *wacom = NULL;
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
WacomDeviceDatabase *db = libinput_libwacom_ref(li);
if (db) {
char event_path[64];
@ -3018,7 +3024,7 @@ tablet_init(struct tablet_dispatch *tablet, struct evdev_device *device)
rc = 0;
out:
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
if (wacom)
libwacom_destroy(wacom);
if (db)

View file

@ -27,7 +27,7 @@
#include "evdev.h"
#if !HAVE_LIBWACOM
#ifndef HAVE_LIBWACOM
typedef void *WacomDevice;
#endif

View file

@ -110,7 +110,7 @@ totem_new_tool(struct totem_dispatch *totem)
static inline void
totem_set_touch_device_enabled(struct totem_dispatch *totem,
bool enable_touch_device,
uint64_t time)
usec_t time)
{
struct evdev_device *touch_device = totem->touch_device;
struct evdev_dispatch *dispatch;
@ -183,7 +183,7 @@ static void
totem_process_key(struct totem_dispatch *totem,
struct evdev_device *device,
struct evdev_event *e,
uint64_t time)
usec_t time)
{
/* ignore kernel key repeat */
if (e->value == 2)
@ -205,7 +205,7 @@ static void
totem_process_abs(struct totem_dispatch *totem,
struct evdev_device *device,
struct evdev_event *e,
uint64_t time)
usec_t time)
{
struct totem_slot *slot = &totem->slots[totem->slot];
@ -263,7 +263,7 @@ totem_slot_fetch_axes(struct totem_dispatch *totem,
struct totem_slot *slot,
struct libinput_tablet_tool *tool,
struct tablet_axes *axes_out,
uint64_t time)
usec_t time)
{
struct evdev_device *device = totem->device;
const char tmp[sizeof(slot->changed_axes)] = { 0 };
@ -358,7 +358,7 @@ slot_axes_initialize(struct totem_dispatch *totem, struct totem_slot *slot)
static enum totem_slot_state
totem_handle_slot_state(struct totem_dispatch *totem,
struct totem_slot *slot,
uint64_t time)
usec_t time)
{
struct evdev_device *device = totem->device;
struct tablet_axes axes;
@ -493,7 +493,7 @@ totem_handle_slot_state(struct totem_dispatch *totem,
}
static enum totem_slot_state
totem_handle_state(struct totem_dispatch *totem, uint64_t time)
totem_handle_state(struct totem_dispatch *totem, usec_t time)
{
enum totem_slot_state global_state = SLOT_STATE_NONE;
@ -514,7 +514,7 @@ static void
totem_process_event(struct evdev_dispatch *dispatch,
struct evdev_device *device,
struct evdev_event *e,
uint64_t time)
usec_t time)
{
struct totem_dispatch *totem = totem_dispatch(dispatch);
enum totem_slot_state global_state;
@ -549,7 +549,7 @@ static void
totem_interface_process(struct evdev_dispatch *dispatch,
struct evdev_device *device,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
size_t nevents;
struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
@ -563,7 +563,7 @@ static void
totem_interface_suspend(struct evdev_dispatch *dispatch, struct evdev_device *device)
{
struct totem_dispatch *totem = totem_dispatch(dispatch);
uint64_t now = libinput_now(evdev_libinput_context(device));
usec_t now = libinput_now(evdev_libinput_context(device));
for (size_t i = 0; i < totem->nslots; i++) {
struct totem_slot *slot = &totem->slots[i];
@ -682,7 +682,7 @@ totem_interface_initial_proximity(struct evdev_device *device,
struct evdev_dispatch *dispatch)
{
struct totem_dispatch *totem = totem_dispatch(dispatch);
uint64_t now = libinput_now(evdev_libinput_context(device));
usec_t now = libinput_now(evdev_libinput_context(device));
bool enable_touch = true;
for (size_t i = 0; i < totem->nslots; i++) {

View file

@ -48,12 +48,12 @@
#include "linux/input.h"
#include "quirks.h"
#if HAVE_LIBWACOM
#ifdef HAVE_LIBWACOM
#include <libwacom/libwacom.h>
#endif
#define DEFAULT_WHEEL_CLICK_ANGLE 15
#define DEFAULT_BUTTON_SCROLL_TIMEOUT ms2us(200)
#define DEFAULT_BUTTON_SCROLL_TIMEOUT usec_from_millis(200)
enum evdev_device_udev_tags {
EVDEV_UDEV_TAG_NONE = 0,
@ -167,7 +167,7 @@ evdev_device_switch_get_state(struct evdev_device *device, enum libinput_switch
void
evdev_pointer_notify_physical_button(struct evdev_device *device,
uint64_t time,
usec_t time,
evdev_usage_t button,
enum libinput_button_state state)
{
@ -179,7 +179,7 @@ evdev_pointer_notify_physical_button(struct evdev_device *device,
static void
evdev_pointer_post_button(struct evdev_device *device,
uint64_t time,
usec_t time,
evdev_usage_t button,
enum libinput_button_state state)
{
@ -205,7 +205,7 @@ evdev_pointer_post_button(struct evdev_device *device,
}
static void
evdev_button_scroll_timeout(uint64_t time, void *data)
evdev_button_scroll_timeout(usec_t time, void *data)
{
struct evdev_device *device = data;
@ -213,7 +213,7 @@ evdev_button_scroll_timeout(uint64_t time, void *data)
}
static void
evdev_button_scroll_button(struct evdev_device *device, uint64_t time, int is_press)
evdev_button_scroll_button(struct evdev_device *device, usec_t time, int is_press)
{
/* Where the button lock is enabled, we wrap the buttons into
their own little state machine and filter out the events.
@ -265,8 +265,9 @@ evdev_button_scroll_button(struct evdev_device *device, uint64_t time, int is_pr
flags = TIMER_FLAG_ALLOW_NEGATIVE;
}
libinput_timer_set_flags(&device->scroll.timer,
time + DEFAULT_BUTTON_SCROLL_TIMEOUT,
libinput_timer_set_flags(
&device->scroll.timer,
usec_add(time, DEFAULT_BUTTON_SCROLL_TIMEOUT),
flags);
} else {
/* For extra mouse buttons numbered 6 or more (0x115+) we assume
@ -314,7 +315,7 @@ evdev_button_scroll_button(struct evdev_device *device, uint64_t time, int is_pr
void
evdev_pointer_notify_button(struct evdev_device *device,
uint64_t time,
usec_t time,
evdev_usage_t button,
enum libinput_button_state state)
{
@ -394,7 +395,7 @@ evdev_device_transform_y(struct evdev_device *device, double y, uint32_t height)
void
evdev_notify_axis_legacy_wheel(struct evdev_device *device,
uint64_t time,
usec_t time,
uint32_t axes,
const struct normalized_coords *delta_in,
const struct discrete_coords *discrete_in)
@ -419,7 +420,7 @@ evdev_notify_axis_legacy_wheel(struct evdev_device *device,
void
evdev_notify_axis_wheel(struct evdev_device *device,
uint64_t time,
usec_t time,
uint32_t axes,
const struct normalized_coords *delta_in,
const struct wheel_v120 *v120_in)
@ -444,7 +445,7 @@ evdev_notify_axis_wheel(struct evdev_device *device,
void
evdev_notify_axis_finger(struct evdev_device *device,
uint64_t time,
usec_t time,
uint32_t axes,
const struct normalized_coords *delta_in)
{
@ -460,7 +461,7 @@ evdev_notify_axis_finger(struct evdev_device *device,
void
evdev_notify_axis_continous(struct evdev_device *device,
uint64_t time,
usec_t time,
uint32_t axes,
const struct normalized_coords *delta_in)
{
@ -1014,7 +1015,7 @@ evdev_note_time_delay(struct evdev_device *device, const struct input_event *ev)
{
struct libinput *libinput = evdev_libinput_context(device);
uint32_t tdelta;
uint64_t eventtime = input_event_time(ev);
usec_t eventtime = input_event_time(ev);
/* if we have a current libinput_dispatch() snapshot, compare our
* event time with the one from the snapshot. If we have more than
@ -1022,10 +1023,11 @@ evdev_note_time_delay(struct evdev_device *device, const struct input_event *ev)
* where there is no steady event flow and thus SYN_DROPPED may not
* get hit by the kernel despite us being too slow.
*/
if (libinput->dispatch_time == 0 || eventtime > libinput->dispatch_time)
if (usec_is_zero(libinput->dispatch_time) ||
usec_cmp(eventtime, libinput->dispatch_time) > 0)
return;
tdelta = us2ms(libinput->dispatch_time - eventtime);
tdelta = usec_to_millis(usec_delta(libinput->dispatch_time, eventtime));
if (tdelta > 20) {
evdev_log_bug_client_ratelimit(
device,
@ -1084,15 +1086,7 @@ evdev_device_dispatch(void *data)
"event frame overflow, discarding events.\n");
}
if (ev.type == EV_SYN && ev.code == SYN_REPORT) {
/* A SYN_REPORT 1 event is a kernel-inserted
* auto-repeat. Nothing in libinput cares about kernel
* repeats and the inserted frame causes issues with
* timestamp deltas (see e.g. #1145)
*/
if (ev.value != 1)
evdev_device_dispatch_frame(libinput,
device,
frame);
evdev_device_dispatch_frame(libinput, device, frame);
evdev_frame_reset(frame);
}
} else if (rc == -ENODEV) {
@ -2007,6 +2001,11 @@ evdev_configure_device(struct evdev_device *device,
}
}
if (libevdev_has_event_code(evdev, EV_SW, SW_KEYPAD_SLIDE)) {
device->seat_caps |= EVDEV_DEVICE_SWITCH;
device->tags |= EVDEV_TAG_KEYPAD_SLIDE_SWITCH;
}
if (device->seat_caps & EVDEV_DEVICE_SWITCH)
evdev_log_info(device, "device is a switch device\n");
}
@ -2316,11 +2315,11 @@ evdev_device_create(struct libinput_seat *seat, struct udev_device *udev_device)
device->dpi = DEFAULT_MOUSE_DPI;
/* at most 5 SYN_DROPPED log-messages per 30s */
ratelimit_init(&device->syn_drop_limit, s2us(30), 5);
ratelimit_init(&device->syn_drop_limit, usec_from_seconds(30), 5);
/* at most 5 "delayed processing" log messages per hour */
ratelimit_init(&device->delay_warning_limit, s2us(60 * 60), 5);
ratelimit_init(&device->delay_warning_limit, usec_from_hours(1), 5);
/* at most 5 log-messages per 5s */
ratelimit_init(&device->nonpointer_rel_limit, s2us(5), 5);
ratelimit_init(&device->nonpointer_rel_limit, usec_from_seconds(5), 5);
matrix_init_identity(&device->abs.calibration);
matrix_init_identity(&device->abs.usermatrix);
@ -2678,6 +2677,9 @@ evdev_device_has_switch(struct evdev_device *device, enum libinput_switch sw)
case LIBINPUT_SWITCH_TABLET_MODE:
code = SW_TABLET_MODE;
break;
case LIBINPUT_SWITCH_KEYPAD_SLIDE:
code = SW_KEYPAD_SLIDE;
break;
default:
return -1;
}
@ -2705,7 +2707,7 @@ evdev_start_scrolling(struct evdev_device *device, enum libinput_pointer_axis ax
void
evdev_post_scroll(struct evdev_device *device,
uint64_t time,
usec_t time,
enum libinput_pointer_axis_source source,
const struct normalized_coords *delta)
{
@ -2779,7 +2781,7 @@ evdev_post_scroll(struct evdev_device *device,
void
evdev_stop_scroll(struct evdev_device *device,
uint64_t time,
usec_t time,
enum libinput_pointer_axis_source source)
{
const struct normalized_coords zero = { 0.0, 0.0 };

View file

@ -36,6 +36,7 @@
#include "evdev-frame.h"
#include "filter.h"
#include "libinput-feature.h"
#include "libinput-private.h"
#include "linux/input.h"
#include "quirks.h"
@ -79,6 +80,7 @@ enum evdev_device_tags {
EVDEV_TAG_TABLET_MODE_SWITCH = bit(8),
EVDEV_TAG_TABLET_TOUCHPAD = bit(9),
EVDEV_TAG_VIRTUAL = bit(10),
EVDEV_TAG_KEYPAD_SLIDE_SWITCH = bit(11),
};
enum evdev_middlebutton_state {
@ -217,7 +219,7 @@ struct evdev_device {
/* Currently enabled method, button */
enum libinput_config_scroll_method method;
evdev_usage_t button;
uint64_t button_down_time;
usec_t button_down_time;
/* set during device init, used at runtime to delay changes
* until all buttons are up */
@ -277,7 +279,7 @@ struct evdev_device {
enum evdev_middlebutton_state state;
struct libinput_timer timer;
uint32_t button_mask;
uint64_t first_event_time;
usec_t first_event_time;
} middlebutton;
};
@ -296,7 +298,7 @@ struct evdev_dispatch_interface {
void (*process)(struct evdev_dispatch *dispatch,
struct evdev_device *device,
struct evdev_frame *frame,
uint64_t time);
usec_t time);
/* Device is being suspended */
void (*suspend)(struct evdev_dispatch *dispatch, struct evdev_device *device);
@ -335,7 +337,7 @@ struct evdev_dispatch_interface {
struct evdev_device *device,
enum evdev_arbitration_state which,
const struct phys_rect *rect, /* may be NULL */
uint64_t now);
usec_t now);
/* Called when touch arbitration is on, updates the area where touch
* arbitration should apply.
@ -343,7 +345,7 @@ struct evdev_dispatch_interface {
void (*touch_arbitration_update_rect)(struct evdev_dispatch *dispatch,
struct evdev_device *device,
const struct phys_rect *rect,
uint64_t now);
usec_t now);
/* Return the state of the given switch */
enum libinput_switch_state (*get_switch_state)(struct evdev_dispatch *dispatch,
@ -352,6 +354,9 @@ struct evdev_dispatch_interface {
void (*left_handed_toggle)(struct evdev_dispatch *dispatch,
struct evdev_device *device,
bool left_handed_enabled);
void (*disable_feature)(struct evdev_dispatch *dispatch,
enum libinput_feature feature);
};
enum evdev_dispatch_type {
@ -389,6 +394,14 @@ evdev_libinput_context(const struct evdev_device *device)
return device->base.seat->libinput;
}
static inline void
evdev_device_disable_feature(struct evdev_device *device, enum libinput_feature feature)
{
if (device->dispatch && device->dispatch->interface &&
device->dispatch->interface->disable_feature)
device->dispatch->interface->disable_feature(device->dispatch, feature);
}
static inline bool
evdev_device_has_model_quirk(struct evdev_device *device, enum quirk model_quirk)
{
@ -540,12 +553,12 @@ evdev_notify_resumed_device(struct evdev_device *device);
void
evdev_pointer_notify_button(struct evdev_device *device,
uint64_t time,
usec_t time,
evdev_usage_t button,
enum libinput_button_state state);
void
evdev_pointer_notify_physical_button(struct evdev_device *device,
uint64_t time,
usec_t time,
evdev_usage_t button,
enum libinput_button_state state);
@ -566,36 +579,36 @@ evdev_update_key_down_count(struct evdev_device *device,
void
evdev_notify_axis_legacy_wheel(struct evdev_device *device,
uint64_t time,
usec_t time,
uint32_t axes,
const struct normalized_coords *delta_in,
const struct discrete_coords *discrete_in);
void
evdev_notify_axis_wheel(struct evdev_device *device,
uint64_t time,
usec_t time,
uint32_t axes,
const struct normalized_coords *delta_in,
const struct wheel_v120 *v120_in);
void
evdev_notify_axis_finger(struct evdev_device *device,
uint64_t time,
usec_t time,
uint32_t axes,
const struct normalized_coords *delta_in);
void
evdev_notify_axis_continous(struct evdev_device *device,
uint64_t time,
usec_t time,
uint32_t axes,
const struct normalized_coords *delta_in);
void
evdev_post_scroll(struct evdev_device *device,
uint64_t time,
usec_t time,
enum libinput_pointer_axis_source source,
const struct normalized_coords *delta);
void
evdev_stop_scroll(struct evdev_device *device,
uint64_t time,
usec_t time,
enum libinput_pointer_axis_source source);
void
@ -606,7 +619,7 @@ evdev_device_destroy(struct evdev_device *device);
bool
evdev_middlebutton_filter_button(struct evdev_device *device,
uint64_t time,
usec_t time,
evdev_usage_t button,
enum libinput_button_state state);
@ -957,7 +970,9 @@ evdev_device_init_abs_range_warnings(struct evdev_device *device)
device->abs.warning_range.max.y = y->maximum + 0.05 * height;
/* One warning every 5 min is enough */
ratelimit_init(&device->abs.warning_range.range_warn_limit, s2us(3000), 1);
ratelimit_init(&device->abs.warning_range.range_warn_limit,
usec_from_seconds(3000),
1);
}
static inline void

View file

@ -29,11 +29,12 @@
#include "filter-private.h"
#include "filter.h"
#define MOTION_TIMEOUT ms2us(1000)
#define FIRST_MOTION_TIME_INTERVAL ms2us(7) /* random but good enough interval for very first event */
#define MOTION_TIMEOUT usec_from_millis(1000)
#define FIRST_MOTION_TIME_INTERVAL usec_from_millis(7) /* random but good enough interval for very first event */
struct custom_accel_function {
uint64_t last_time;
usec_t last_time;
usec_t last_delta_time;
double step;
size_t npoints;
double points[];
@ -57,7 +58,8 @@ create_custom_accel_function(double step, const double *points, size_t npoints)
struct custom_accel_function *cf =
zalloc(sizeof(*cf) + npoints * sizeof(*points));
cf->last_time = 0;
cf->last_time = usec_from_uint64_t(0);
cf->last_delta_time = FIRST_MOTION_TIME_INTERVAL;
cf->step = step;
cf->npoints = npoints;
memcpy(cf->points, points, sizeof(*points) * npoints);
@ -77,7 +79,7 @@ custom_accel_function_destroy(struct custom_accel_function *cf)
static double
custom_accel_function_calculate_speed(struct custom_accel_function *cf,
const struct device_float_coords *unaccelerated,
uint64_t time)
usec_t time)
{
/* Although most devices have a constant polling rate, and for fast
* movements these distances do represent the actual speed,
@ -110,13 +112,26 @@ custom_accel_function_calculate_speed(struct custom_accel_function *cf,
/* calculate speed based on time passed since last event */
double distance = hypot(unaccelerated->x, unaccelerated->y);
/* delta_time can be zero when:
* - the fallback acceleration function is used for multiple movement types
* (for example, pointer motion and wheel scrolling simultaneously)
* - two different methods produce the same movement at the same time
* (for example, button-scrolling and wheel-scrolling)
*
* Reusing the last delta_time is a graceful fallback even if there are
* duplicate events or event-ordering bugs.
*/
usec_t delta_time = usec_cmp(time, cf->last_time) > 0
? usec_delta(time, cf->last_time)
: cf->last_delta_time;
/* handle first event in a motion */
if (time - cf->last_time > MOTION_TIMEOUT)
cf->last_time = time - FIRST_MOTION_TIME_INTERVAL;
if (usec_cmp(delta_time, MOTION_TIMEOUT) > 0)
delta_time = FIRST_MOTION_TIME_INTERVAL;
double dt = us2ms_f(time - cf->last_time);
double speed = distance / dt; /* speed is in device-units per ms */
/* speed is in device-units per ms */
double speed = distance / us2ms_f(delta_time);
cf->last_time = time;
cf->last_delta_time = delta_time;
return speed;
}
@ -182,7 +197,7 @@ custom_accel_function_profile(struct custom_accel_function *cf, double speed_in)
static struct normalized_coords
custom_accel_function_filter(struct custom_accel_function *cf,
const struct device_float_coords *unaccelerated,
uint64_t time)
usec_t time)
{
double speed = custom_accel_function_calculate_speed(cf, unaccelerated, time);
@ -238,7 +253,7 @@ static struct normalized_coords
custom_accelerator_filter(enum libinput_config_accel_type accel_type,
struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
uint64_t time)
usec_t time)
{
struct custom_accelerator *f = (struct custom_accelerator *)filter;
struct custom_accel_function *cf;
@ -249,7 +264,7 @@ custom_accelerator_filter(enum libinput_config_accel_type accel_type,
}
static void
custom_accelerator_restart(struct motion_filter *filter, void *data, uint64_t time)
custom_accelerator_restart(struct motion_filter *filter, void *data, usec_t time)
{
/* noop, this function has no effect in the custom interface */
}
@ -333,7 +348,7 @@ double
custom_accel_profile_fallback(struct motion_filter *filter,
void *data,
double speed_in,
uint64_t time)
usec_t time)
{
return custom_accelerator_profile(LIBINPUT_ACCEL_TYPE_FALLBACK,
filter,
@ -344,7 +359,7 @@ static struct normalized_coords
custom_accelerator_filter_fallback(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
return custom_accelerator_filter(LIBINPUT_ACCEL_TYPE_FALLBACK,
filter,
@ -356,7 +371,7 @@ double
custom_accel_profile_motion(struct motion_filter *filter,
void *data,
double speed_in,
uint64_t time)
usec_t time)
{
return custom_accelerator_profile(LIBINPUT_ACCEL_TYPE_MOTION, filter, speed_in);
}
@ -365,7 +380,7 @@ static struct normalized_coords
custom_accelerator_filter_motion(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
return custom_accelerator_filter(LIBINPUT_ACCEL_TYPE_MOTION,
filter,
@ -377,7 +392,7 @@ double
custom_accel_profile_scroll(struct motion_filter *filter,
void *data,
double speed_in,
uint64_t time)
usec_t time)
{
return custom_accelerator_profile(LIBINPUT_ACCEL_TYPE_SCROLL, filter, speed_in);
}
@ -386,7 +401,8 @@ static struct normalized_coords
custom_accelerator_filter_scroll(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time,
enum filter_scroll_type type)
{
return custom_accelerator_filter(LIBINPUT_ACCEL_TYPE_SCROLL,
filter,

View file

@ -45,7 +45,7 @@ static struct normalized_coords
accelerator_filter_flat(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
struct pointer_accelerator_flat *accel_filter =
(struct pointer_accelerator_flat *)filter;
@ -62,10 +62,10 @@ accelerator_filter_flat(struct motion_filter *filter,
}
static struct normalized_coords
accelerator_filter_noop_flat(struct motion_filter *filter,
accelerator_filter_constant_flat(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
/* We map the unaccelerated flat filter to have the same behavior as
* the "accelerated" flat filter.
@ -81,6 +81,27 @@ accelerator_filter_noop_flat(struct motion_filter *filter,
return accelerator_filter_flat(filter, unaccelerated, data, time);
}
static struct normalized_coords
accelerator_filter_scroll_flat(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
usec_t time,
enum filter_scroll_type type)
{
/* Scroll wheels were not historically accelerated and have different
* units than button scrolling. Maintain the status quo and do not
* accelerate wheel events.
*/
if (type == FILTER_SCROLL_TYPE_WHEEL) {
return (struct normalized_coords){
.x = unaccelerated->x,
.y = unaccelerated->y,
};
}
return accelerator_filter_constant_flat(filter, unaccelerated, data, time);
}
static bool
accelerator_set_speed_flat(struct motion_filter *filter, double speed_adjustment)
{
@ -112,8 +133,8 @@ accelerator_destroy_flat(struct motion_filter *filter)
static const struct motion_filter_interface accelerator_interface_flat = {
.type = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT,
.filter = accelerator_filter_flat,
.filter_constant = accelerator_filter_noop_flat,
.filter_scroll = accelerator_filter_noop_flat,
.filter_constant = accelerator_filter_constant_flat,
.filter_scroll = accelerator_filter_scroll_flat,
.restart = NULL,
.destroy = accelerator_destroy_flat,
.set_speed = accelerator_set_speed_flat,

View file

@ -38,8 +38,8 @@
* Default parameters for pointer acceleration profiles.
*/
#define DEFAULT_THRESHOLD v_ms2us(0.4) /* in units/us */
#define MINIMUM_THRESHOLD v_ms2us(0.2) /* in units/us */
#define DEFAULT_THRESHOLD v_usec_from_millis(0.4) /* in units/us */
#define MINIMUM_THRESHOLD v_usec_from_millis(0.2) /* in units/us */
#define DEFAULT_ACCELERATION 2.0 /* unitless factor */
#define DEFAULT_INCLINE 1.1 /* unitless factor */
@ -74,7 +74,7 @@ double
pointer_accel_profile_linear_low_dpi(struct motion_filter *filter,
void *data,
double speed_in, /* in device units (units/us) */
uint64_t time)
usec_t time)
{
struct pointer_accelerator_low_dpi *accel_filter =
(struct pointer_accelerator_low_dpi *)filter;
@ -107,7 +107,7 @@ static inline double
calculate_acceleration_factor(struct pointer_accelerator_low_dpi *accel,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
double velocity; /* units/us in device-native dpi*/
double accel_factor;
@ -129,7 +129,7 @@ static struct normalized_coords
accelerator_filter_low_dpi(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
struct pointer_accelerator_low_dpi *accel =
(struct pointer_accelerator_low_dpi *)filter;
@ -145,10 +145,10 @@ accelerator_filter_low_dpi(struct motion_filter *filter,
}
static struct normalized_coords
accelerator_filter_noop(struct motion_filter *filter,
accelerator_filter_constant(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
const struct normalized_coords normalized = {
.x = unaccelerated->x,
@ -157,8 +157,18 @@ accelerator_filter_noop(struct motion_filter *filter,
return normalized;
}
static struct normalized_coords
accelerator_filter_scroll(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
usec_t time,
enum filter_scroll_type type)
{
return accelerator_filter_constant(filter, unaccelerated, data, time);
}
static void
accelerator_restart(struct motion_filter *filter, void *data, uint64_t time)
accelerator_restart(struct motion_filter *filter, void *data, usec_t time)
{
struct pointer_accelerator_low_dpi *accel =
(struct pointer_accelerator_low_dpi *)filter;
@ -188,7 +198,8 @@ accelerator_set_speed(struct motion_filter *filter, double speed_adjustment)
don't read more into them other than "they mostly worked ok" */
/* delay when accel kicks in */
accel_filter->threshold = DEFAULT_THRESHOLD - v_ms2us(0.25) * speed_adjustment;
accel_filter->threshold =
DEFAULT_THRESHOLD - v_usec_from_millis(0.25) * speed_adjustment;
if (accel_filter->threshold < MINIMUM_THRESHOLD)
accel_filter->threshold = MINIMUM_THRESHOLD;
@ -205,8 +216,8 @@ accelerator_set_speed(struct motion_filter *filter, double speed_adjustment)
static const struct motion_filter_interface accelerator_interface_low_dpi = {
.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
.filter = accelerator_filter_low_dpi,
.filter_constant = accelerator_filter_noop,
.filter_scroll = accelerator_filter_noop,
.filter_constant = accelerator_filter_constant,
.filter_scroll = accelerator_filter_scroll,
.restart = accelerator_restart,
.destroy = accelerator_destroy,
.set_speed = accelerator_set_speed,

View file

@ -38,8 +38,8 @@
* Default parameters for pointer acceleration profiles.
*/
#define DEFAULT_THRESHOLD v_ms2us(0.4) /* in 1000dpi units/us */
#define MINIMUM_THRESHOLD v_ms2us(0.2) /* in 1000dpi units/us */
#define DEFAULT_THRESHOLD v_usec_from_millis(0.4) /* in 1000dpi units/us */
#define MINIMUM_THRESHOLD v_usec_from_millis(0.2) /* in 1000dpi units/us */
#define DEFAULT_ACCELERATION 2.0 /* unitless factor */
#define DEFAULT_INCLINE 1.1 /* unitless factor */
@ -74,7 +74,7 @@ static inline double
calculate_acceleration_factor(struct pointer_accelerator *accel,
const struct normalized_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
double velocity; /* units/us in normalized 1000dpi units*/
double accel_factor;
@ -104,7 +104,7 @@ static struct normalized_coords
accelerator_filter_linear(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
struct pointer_accelerator *accel = (struct pointer_accelerator *)filter;
@ -133,18 +133,39 @@ accelerator_filter_linear(struct motion_filter *filter,
* motion
*/
static struct normalized_coords
accelerator_filter_noop(struct motion_filter *filter,
accelerator_filter_constant(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
struct pointer_accelerator *accel = (struct pointer_accelerator *)filter;
return normalize_for_dpi(unaccelerated, accel->dpi);
}
static struct normalized_coords
accelerator_filter_scroll(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
usec_t time,
enum filter_scroll_type type)
{
/* Scroll wheels were not historically accelerated and have different
* units than button scrolling. Maintain the status quo and do not
* accelerate wheel events.
*/
if (type == FILTER_SCROLL_TYPE_WHEEL) {
return (struct normalized_coords){
.x = unaccelerated->x,
.y = unaccelerated->y,
};
}
return accelerator_filter_constant(filter, unaccelerated, data, time);
}
static void
accelerator_restart(struct motion_filter *filter, void *data, uint64_t time)
accelerator_restart(struct motion_filter *filter, void *data, usec_t time)
{
struct pointer_accelerator *accel = (struct pointer_accelerator *)filter;
@ -171,7 +192,8 @@ accelerator_set_speed(struct motion_filter *filter, double speed_adjustment)
don't read more into them other than "they mostly worked ok" */
/* delay when accel kicks in */
accel_filter->threshold = DEFAULT_THRESHOLD - v_ms2us(0.25) * speed_adjustment;
accel_filter->threshold =
DEFAULT_THRESHOLD - v_usec_from_millis(0.25) * speed_adjustment;
if (accel_filter->threshold < MINIMUM_THRESHOLD)
accel_filter->threshold = MINIMUM_THRESHOLD;
@ -189,7 +211,7 @@ double
pointer_accel_profile_linear(struct motion_filter *filter,
void *data,
double speed_in, /* in normalized units */
uint64_t time)
usec_t time)
{
struct pointer_accelerator *accel_filter = (struct pointer_accelerator *)filter;
const double max_accel = accel_filter->accel; /* unitless factor */
@ -260,8 +282,8 @@ pointer_accel_profile_linear(struct motion_filter *filter,
static const struct motion_filter_interface accelerator_interface = {
.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
.filter = accelerator_filter_linear,
.filter_constant = accelerator_filter_noop,
.filter_scroll = accelerator_filter_noop,
.filter_constant = accelerator_filter_constant,
.filter_scroll = accelerator_filter_scroll,
.restart = accelerator_restart,
.destroy = accelerator_destroy,
.set_speed = accelerator_set_speed,

View file

@ -34,18 +34,19 @@ struct motion_filter_interface {
struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time);
usec_t time);
struct normalized_coords (*filter_constant)(
struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time);
usec_t time);
struct normalized_coords (*filter_scroll)(
struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time);
void (*restart)(struct motion_filter *filter, void *data, uint64_t time);
usec_t time,
enum filter_scroll_type type);
void (*restart)(struct motion_filter *filter, void *data, usec_t time);
void (*destroy)(struct motion_filter *filter);
bool (*set_speed)(struct motion_filter *filter, double speed_adjustment);
bool (*set_accel_config)(struct motion_filter *filter,
@ -59,19 +60,19 @@ struct motion_filter {
struct pointer_tracker {
struct device_float_coords delta; /* delta to most recent event */
uint64_t time; /* us */
usec_t time; /* us */
uint32_t dir;
};
/* For smoothing timestamps from devices with unreliable timing */
struct pointer_delta_smoothener {
uint64_t threshold;
uint64_t value;
usec_t threshold;
usec_t value;
};
static inline struct pointer_delta_smoothener *
pointer_delta_smoothener_create(uint64_t event_delta_smooth_threshold,
uint64_t event_delta_smooth_value)
pointer_delta_smoothener_create(usec_t event_delta_smooth_threshold,
usec_t event_delta_smooth_value)
{
struct pointer_delta_smoothener *s = zalloc(sizeof(*s));
s->threshold = event_delta_smooth_threshold;
@ -99,17 +100,17 @@ void
trackers_free(struct pointer_trackers *trackers);
void
trackers_reset(struct pointer_trackers *trackers, uint64_t time);
trackers_reset(struct pointer_trackers *trackers, usec_t time);
void
trackers_feed(struct pointer_trackers *trackers,
const struct device_float_coords *delta,
uint64_t time);
usec_t time);
struct pointer_tracker *
trackers_by_offset(struct pointer_trackers *trackers, unsigned int offset);
double
trackers_velocity(struct pointer_trackers *trackers, uint64_t time);
trackers_velocity(struct pointer_trackers *trackers, usec_t time);
double
calculate_acceleration_simpsons(struct motion_filter *filter,
@ -117,7 +118,7 @@ calculate_acceleration_simpsons(struct motion_filter *filter,
void *data,
double velocity,
double last_velocity,
uint64_t time);
usec_t time);
/* Convert speed/velocity from units/us to units/ms */
static inline double
@ -134,7 +135,7 @@ v_us2s(double units_per_us)
/* Convert speed/velocity from units/ms to units/us */
static inline double
v_ms2us(double units_per_ms)
v_usec_from_millis(double units_per_ms)
{
return units_per_ms / 1000.0;
}

View file

@ -96,7 +96,7 @@ static struct normalized_coords
tablet_accelerator_filter_flat(struct motion_filter *filter,
const struct device_float_coords *units,
void *data,
uint64_t time)
usec_t time)
{
struct tablet_accelerator_flat *accel_filter =
(struct tablet_accelerator_flat *)filter;

View file

@ -47,7 +47,7 @@ static struct normalized_coords
accelerator_filter_touchpad_flat(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
struct touchpad_accelerator_flat *accel =
(struct touchpad_accelerator_flat *)filter;
@ -65,10 +65,11 @@ accelerator_filter_touchpad_flat(struct motion_filter *filter,
}
static struct normalized_coords
accelerator_filter_noop_touchpad_flat(struct motion_filter *filter,
accelerator_filter_constant_touchpad_flat(
struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
/* We map the unaccelerated flat filter to have the same behavior as
* the "accelerated" flat filter.
@ -84,6 +85,30 @@ accelerator_filter_noop_touchpad_flat(struct motion_filter *filter,
return accelerator_filter_touchpad_flat(filter, unaccelerated, data, time);
}
static struct normalized_coords
accelerator_filter_scroll_touchpad_flat(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
usec_t time,
enum filter_scroll_type type)
{
/* Scroll wheels were not historically accelerated and have different
* units than button scrolling. Maintain the status quo and do not
* accelerate wheel events.
*/
if (type == FILTER_SCROLL_TYPE_WHEEL) {
return (struct normalized_coords){
.x = unaccelerated->x,
.y = unaccelerated->y,
};
}
return accelerator_filter_constant_touchpad_flat(filter,
unaccelerated,
data,
time);
}
static bool
accelerator_set_speed_touchpad_flat(struct motion_filter *filter,
double speed_adjustment)
@ -111,8 +136,8 @@ accelerator_destroy_touchpad_flat(struct motion_filter *filter)
static const struct motion_filter_interface accelerator_interface_touchpad_flat = {
.type = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT,
.filter = accelerator_filter_touchpad_flat,
.filter_constant = accelerator_filter_noop_touchpad_flat,
.filter_scroll = accelerator_filter_noop_touchpad_flat,
.filter_constant = accelerator_filter_constant_touchpad_flat,
.filter_scroll = accelerator_filter_scroll_touchpad_flat,
.restart = NULL,
.destroy = accelerator_destroy_touchpad_flat,
.set_speed = accelerator_set_speed_touchpad_flat,

View file

@ -45,13 +45,13 @@
* Default parameters for pointer acceleration profiles.
*/
#define DEFAULT_THRESHOLD v_ms2us(0.4) /* in units/us */
#define MINIMUM_THRESHOLD v_ms2us(0.2) /* in units/us */
#define DEFAULT_THRESHOLD v_usec_from_millis(0.4) /* in units/us */
#define MINIMUM_THRESHOLD v_usec_from_millis(0.2) /* in units/us */
#define DEFAULT_ACCELERATION 2.0 /* unitless factor */
#define DEFAULT_INCLINE 1.1 /* unitless factor */
/* for the Lenovo x230 custom accel. do not touch */
#define X230_THRESHOLD v_ms2us(0.4) /* in units/us */
#define X230_THRESHOLD v_usec_from_millis(0.4) /* in units/us */
#define X230_ACCELERATION 2.0 /* unitless factor */
#define X230_INCLINE 1.1 /* unitless factor */
#define X230_MAGIC_SLOWDOWN 0.4 /* unitless */
@ -88,7 +88,7 @@ static double
acceleration_profile(struct pointer_accelerator_x230 *accel,
void *data,
double velocity,
uint64_t time)
usec_t time)
{
return accel->profile(&accel->base, data, velocity, time);
}
@ -110,7 +110,7 @@ calculate_acceleration(struct pointer_accelerator_x230 *accel,
void *data,
double velocity,
double last_velocity,
uint64_t time)
usec_t time)
{
double factor;
@ -131,7 +131,7 @@ static struct normalized_coords
accelerator_filter_x230(struct motion_filter *filter,
const struct device_float_coords *raw,
void *data,
uint64_t time)
usec_t time)
{
struct pointer_accelerator_x230 *accel =
(struct pointer_accelerator_x230 *)filter;
@ -171,7 +171,7 @@ static struct normalized_coords
accelerator_filter_constant_x230(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
struct pointer_accelerator_x230 *accel =
(struct pointer_accelerator_x230 *)filter;
@ -185,8 +185,29 @@ accelerator_filter_constant_x230(struct motion_filter *filter,
return normalized;
}
static struct normalized_coords
accelerator_filter_scroll_x230(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
usec_t time,
enum filter_scroll_type type)
{
/* Scroll wheels were not historically accelerated and have different
* units than button scrolling. Maintain the status quo and do not
* accelerate wheel events.
*/
if (type == FILTER_SCROLL_TYPE_WHEEL) {
return (struct normalized_coords){
.x = unaccelerated->x,
.y = unaccelerated->y,
};
}
return accelerator_filter_constant_x230(filter, unaccelerated, data, time);
}
static void
accelerator_restart_x230(struct motion_filter *filter, void *data, uint64_t time)
accelerator_restart_x230(struct motion_filter *filter, void *data, usec_t time)
{
struct pointer_accelerator_x230 *accel =
(struct pointer_accelerator_x230 *)filter;
@ -195,7 +216,7 @@ accelerator_restart_x230(struct motion_filter *filter, void *data, uint64_t time
for (offset = 1; offset < accel->trackers.ntrackers; offset++) {
tracker = trackers_by_offset(&accel->trackers, offset);
tracker->time = 0;
tracker->time = usec_from_uint64_t(0);
tracker->dir = 0;
tracker->delta.x = 0;
tracker->delta.y = 0;
@ -228,7 +249,8 @@ accelerator_set_speed_x230(struct motion_filter *filter, double speed_adjustment
don't read more into them other than "they mostly worked ok" */
/* delay when accel kicks in */
accel_filter->threshold = DEFAULT_THRESHOLD - v_ms2us(0.25) * speed_adjustment;
accel_filter->threshold =
DEFAULT_THRESHOLD - v_usec_from_millis(0.25) * speed_adjustment;
if (accel_filter->threshold < MINIMUM_THRESHOLD)
accel_filter->threshold = MINIMUM_THRESHOLD;
@ -246,7 +268,7 @@ double
touchpad_lenovo_x230_accel_profile(struct motion_filter *filter,
void *data,
double speed_in, /* 1000dpi-units/µs */
uint64_t time)
usec_t time)
{
/* Those touchpads presents an actual lower resolution that what is
* advertised. We see some jumps from the cursor due to the big steps
@ -285,7 +307,7 @@ static const struct motion_filter_interface accelerator_interface_x230 = {
.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
.filter = accelerator_filter_x230,
.filter_constant = accelerator_filter_constant_x230,
.filter_scroll = accelerator_filter_constant_x230,
.filter_scroll = accelerator_filter_scroll_x230,
.restart = accelerator_restart_x230,
.destroy = accelerator_destroy_x230,
.set_speed = accelerator_set_speed_x230,

View file

@ -72,7 +72,7 @@ static inline double
calculate_acceleration_factor(struct touchpad_accelerator *accel,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
double velocity; /* units/us in device-native dpi*/
double accel_factor;
@ -94,7 +94,7 @@ static struct normalized_coords
accelerator_filter_touchpad(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
struct touchpad_accelerator *accel = (struct touchpad_accelerator *)filter;
@ -141,7 +141,7 @@ static struct normalized_coords
touchpad_constant_filter(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
struct touchpad_accelerator *accel = (struct touchpad_accelerator *)filter;
struct normalized_coords normalized;
@ -162,8 +162,29 @@ touchpad_constant_filter(struct motion_filter *filter,
return normalized;
}
static struct normalized_coords
touchpad_scroll_filter(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
usec_t time,
enum filter_scroll_type type)
{
/* Scroll wheels were not historically accelerated and have different
* units than button scrolling. Maintain the status quo and do not
* accelerate wheel events.
*/
if (type == FILTER_SCROLL_TYPE_WHEEL) {
return (struct normalized_coords){
.x = unaccelerated->x,
.y = unaccelerated->y,
};
}
return touchpad_constant_filter(filter, unaccelerated, data, time);
}
static void
touchpad_accelerator_restart(struct motion_filter *filter, void *data, uint64_t time)
touchpad_accelerator_restart(struct motion_filter *filter, void *data, usec_t time)
{
struct touchpad_accelerator *accel = (struct touchpad_accelerator *)filter;
@ -183,7 +204,7 @@ double
touchpad_accel_profile_linear(struct motion_filter *filter,
void *data,
double speed_in, /* in device units/µs */
uint64_t time)
usec_t time)
{
struct touchpad_accelerator *accel_filter =
(struct touchpad_accelerator *)filter;
@ -272,7 +293,7 @@ static const struct motion_filter_interface accelerator_interface_touchpad = {
.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
.filter = accelerator_filter_touchpad,
.filter_constant = touchpad_constant_filter,
.filter_scroll = touchpad_constant_filter,
.filter_scroll = touchpad_scroll_filter,
.restart = touchpad_accelerator_restart,
.destroy = touchpad_accelerator_destroy,
.set_speed = touchpad_accelerator_set_speed,
@ -280,8 +301,8 @@ static const struct motion_filter_interface accelerator_interface_touchpad = {
struct motion_filter *
create_pointer_accelerator_filter_touchpad(int dpi,
uint64_t event_delta_smooth_threshold,
uint64_t event_delta_smooth_value,
usec_t event_delta_smooth_threshold,
usec_t event_delta_smooth_value,
bool use_velocity_averaging)
{
struct touchpad_accelerator *filter;

View file

@ -45,7 +45,7 @@ static struct normalized_coords
trackpoint_flat_filter(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
struct trackpoint_flat_accelerator *accel_filter =
(struct trackpoint_flat_accelerator *)filter;
@ -60,10 +60,10 @@ trackpoint_flat_filter(struct motion_filter *filter,
}
static struct normalized_coords
trackpoint_flat_filter_noop(struct motion_filter *filter,
trackpoint_flat_filter_constant(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
/* We map the unaccelerated flat filter to have the same behavior as
* the "accelerated" flat filter.
@ -79,6 +79,27 @@ trackpoint_flat_filter_noop(struct motion_filter *filter,
return trackpoint_flat_filter(filter, unaccelerated, data, time);
}
static struct normalized_coords
trackpoint_flat_filter_scroll(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
usec_t time,
enum filter_scroll_type type)
{
/* Scroll wheels were not historically accelerated and have different
* units than button scrolling. Maintain the status quo and do not
* accelerate wheel events.
*/
if (type == FILTER_SCROLL_TYPE_WHEEL) {
return (struct normalized_coords){
.x = unaccelerated->x,
.y = unaccelerated->y,
};
}
return trackpoint_flat_filter_constant(filter, unaccelerated, data, time);
}
/* Maps the [-1, 1] speed setting into a constant acceleration
* range. This isn't a linear scale, we keep 0 as the 'optimized'
* mid-point and scale down to 0 for setting -1 and up to 5 for
@ -128,8 +149,8 @@ trackpoint_flat_destroy(struct motion_filter *filter)
static struct motion_filter_interface accelerator_interface_flat = {
.type = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT,
.filter = trackpoint_flat_filter,
.filter_constant = trackpoint_flat_filter_noop,
.filter_scroll = trackpoint_flat_filter_noop,
.filter_constant = trackpoint_flat_filter_constant,
.filter_scroll = trackpoint_flat_filter_scroll,
.restart = NULL,
.destroy = trackpoint_flat_destroy,
.set_speed = trackpoint_flat_set_speed,

View file

@ -48,7 +48,7 @@ double
trackpoint_accel_profile(struct motion_filter *filter,
void *data,
double velocity,
uint64_t time)
usec_t time)
{
struct trackpoint_accelerator *accel_filter =
(struct trackpoint_accelerator *)filter;
@ -74,7 +74,7 @@ static struct normalized_coords
trackpoint_accelerator_filter(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
struct trackpoint_accelerator *accel_filter =
(struct trackpoint_accelerator *)filter;
@ -97,10 +97,10 @@ trackpoint_accelerator_filter(struct motion_filter *filter,
}
static struct normalized_coords
trackpoint_accelerator_filter_noop(struct motion_filter *filter,
trackpoint_accelerator_filter_constant(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
struct trackpoint_accelerator *accel_filter =
(struct trackpoint_accelerator *)filter;
@ -112,6 +112,30 @@ trackpoint_accelerator_filter_noop(struct motion_filter *filter,
return coords;
}
static struct normalized_coords
trackpoint_accelerator_filter_scroll(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
usec_t time,
enum filter_scroll_type type)
{
/* Scroll wheels were not historically accelerated and have different
* units than button scrolling. Maintain the status quo and do not
* accelerate wheel events.
*/
if (type == FILTER_SCROLL_TYPE_WHEEL) {
return (struct normalized_coords){
.x = unaccelerated->x,
.y = unaccelerated->y,
};
}
return trackpoint_accelerator_filter_constant(filter,
unaccelerated,
data,
time);
}
/* Maps the [-1, 1] speed setting into a constant acceleration
* range. This isn't a linear scale, we keep 0 as the 'optimized'
* mid-point and scale down to 0 for setting -1 and up to 5 for
@ -150,7 +174,7 @@ trackpoint_accelerator_set_speed(struct motion_filter *filter, double speed_adju
}
static void
trackpoint_accelerator_restart(struct motion_filter *filter, void *data, uint64_t time)
trackpoint_accelerator_restart(struct motion_filter *filter, void *data, usec_t time)
{
struct trackpoint_accelerator *accel = (struct trackpoint_accelerator *)filter;
@ -170,8 +194,8 @@ trackpoint_accelerator_destroy(struct motion_filter *filter)
static const struct motion_filter_interface accelerator_interface_trackpoint = {
.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
.filter = trackpoint_accelerator_filter,
.filter_constant = trackpoint_accelerator_filter_noop,
.filter_scroll = trackpoint_accelerator_filter_noop,
.filter_constant = trackpoint_accelerator_filter_constant,
.filter_scroll = trackpoint_accelerator_filter_scroll,
.restart = trackpoint_accelerator_restart,
.destroy = trackpoint_accelerator_destroy,
.set_speed = trackpoint_accelerator_set_speed,
@ -207,7 +231,8 @@ create_pointer_accelerator_filter_trackpoint(double multiplier,
filter->base.interface = &accelerator_interface_trackpoint;
filter->trackers.smoothener =
pointer_delta_smoothener_create(ms2us(10), ms2us(10));
pointer_delta_smoothener_create(usec_from_millis(10),
usec_from_millis(10));
return &filter->base;
}

View file

@ -35,13 +35,13 @@
#include "filter.h"
#include "libinput-util.h"
#define MOTION_TIMEOUT ms2us(1000)
#define MOTION_TIMEOUT usec_from_millis(1000)
struct normalized_coords
filter_dispatch(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
return filter->interface->filter(filter, unaccelerated, data, time);
}
@ -50,7 +50,7 @@ struct normalized_coords
filter_dispatch_constant(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time)
{
return filter->interface->filter_constant(filter, unaccelerated, data, time);
}
@ -59,13 +59,18 @@ struct normalized_coords
filter_dispatch_scroll(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time)
usec_t time,
enum filter_scroll_type type)
{
return filter->interface->filter_scroll(filter, unaccelerated, data, time);
return filter->interface->filter_scroll(filter,
unaccelerated,
data,
time,
type);
}
void
filter_restart(struct motion_filter *filter, void *data, uint64_t time)
filter_restart(struct motion_filter *filter, void *data, usec_t time)
{
if (filter->interface->restart)
filter->interface->restart(filter, data, time);
@ -127,14 +132,14 @@ trackers_free(struct pointer_trackers *trackers)
}
void
trackers_reset(struct pointer_trackers *trackers, uint64_t time)
trackers_reset(struct pointer_trackers *trackers, usec_t time)
{
unsigned int offset;
struct pointer_tracker *tracker;
for (offset = 1; offset < trackers->ntrackers; offset++) {
tracker = trackers_by_offset(trackers, offset);
tracker->time = 0;
tracker->time = usec_from_uint64_t(0);
tracker->dir = 0;
tracker->delta.x = 0;
tracker->delta.y = 0;
@ -148,7 +153,7 @@ trackers_reset(struct pointer_trackers *trackers, uint64_t time)
void
trackers_feed(struct pointer_trackers *trackers,
const struct device_float_coords *delta,
uint64_t time)
usec_t time)
{
unsigned int i, current;
struct pointer_tracker *ts = trackers->trackers;
@ -179,16 +184,17 @@ trackers_by_offset(struct pointer_trackers *trackers, unsigned int offset)
static double
calculate_trackers_velocity(const struct pointer_tracker *tracker,
uint64_t time,
usec_t time,
struct pointer_delta_smoothener *smoothener)
{
uint64_t tdelta = time - tracker->time + 1;
usec_t tdelta = usec_delta(time, tracker->time);
tdelta = usec_add(tdelta, usec_from_uint64_t(1));
if (smoothener && tdelta < smoothener->threshold)
if (smoothener && usec_cmp(tdelta, smoothener->threshold) < 0)
tdelta = smoothener->value;
return hypot(tracker->delta.x, tracker->delta.y) /
(double)tdelta; /* units/us */
(double)usec_as_uint64_t(tdelta); /* units/us */
}
static double
@ -207,7 +213,7 @@ trackers_velocity_after_timeout(const struct pointer_tracker *tracker,
* movement in normal use-cases (pause, move, pause, move)
*/
return calculate_trackers_velocity(tracker,
tracker->time + MOTION_TIMEOUT,
usec_add(tracker->time, MOTION_TIMEOUT),
smoothener);
}
@ -219,9 +225,9 @@ trackers_velocity_after_timeout(const struct pointer_tracker *tracker,
* change between events.
*/
double
trackers_velocity(struct pointer_trackers *trackers, uint64_t time)
trackers_velocity(struct pointer_trackers *trackers, usec_t time)
{
const double MAX_VELOCITY_DIFF = v_ms2us(1); /* units/us */
const double MAX_VELOCITY_DIFF = v_usec_from_millis(1); /* units/us */
double result = 0.0;
double initial_velocity = 0.0;
@ -234,11 +240,12 @@ trackers_velocity(struct pointer_trackers *trackers, uint64_t time)
trackers_by_offset(trackers, offset);
/* Bug: time running backwards */
if (tracker->time > time)
if (usec_cmp(tracker->time, time) > 0)
break;
/* Stop if too far away in time */
if (time - tracker->time > MOTION_TIMEOUT) {
usec_t tdelta = usec_delta(time, tracker->time);
if (usec_cmp(tdelta, MOTION_TIMEOUT) > 0) {
if (offset == 1)
result = trackers_velocity_after_timeout(
tracker,
@ -297,7 +304,7 @@ calculate_acceleration_simpsons(struct motion_filter *filter,
void *data,
double velocity,
double last_velocity,
uint64_t time)
usec_t time)
{
double factor;

View file

@ -34,6 +34,12 @@
struct motion_filter;
enum filter_scroll_type {
FILTER_SCROLL_TYPE_CONTINUOUS,
FILTER_SCROLL_TYPE_WHEEL,
FILTER_SCROLL_TYPE_FINGER,
};
/**
* Accelerate the given coordinates.
* Takes a set of unaccelerated deltas and accelerates them based on the
@ -60,7 +66,7 @@ struct normalized_coords
filter_dispatch(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time);
usec_t time);
/**
* Apply constant motion filters, but no acceleration.
@ -82,7 +88,7 @@ struct normalized_coords
filter_dispatch_constant(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time);
usec_t time);
/**
* Apply a scroll filter.
@ -98,6 +104,7 @@ filter_dispatch_constant(struct motion_filter *filter,
* originally provided resolution.
* @param data Custom data
* @param time The time of the delta
* @param type The type of scroll event
*
* @see filter_dispatch
*/
@ -105,10 +112,11 @@ struct normalized_coords
filter_dispatch_scroll(struct motion_filter *filter,
const struct device_float_coords *unaccelerated,
void *data,
uint64_t time);
usec_t time,
enum filter_scroll_type type);
void
filter_restart(struct motion_filter *filter, void *data, uint64_t time);
filter_restart(struct motion_filter *filter, void *data, usec_t time);
void
filter_destroy(struct motion_filter *filter);
@ -124,7 +132,7 @@ filter_get_type(struct motion_filter *filter);
typedef double (*accel_profile_func_t)(struct motion_filter *filter,
void *data,
double velocity,
uint64_t time);
usec_t time);
bool
filter_set_accel_config(struct motion_filter *filter,
@ -142,8 +150,8 @@ create_pointer_accelerator_filter_linear_low_dpi(int dpi, bool use_velocity_aver
struct motion_filter *
create_pointer_accelerator_filter_touchpad(int dpi,
uint64_t event_delta_smooth_threshold,
uint64_t event_delta_smooth_value,
usec_t event_delta_smooth_threshold,
usec_t event_delta_smooth_value,
bool use_velocity_averaging);
struct motion_filter *
@ -173,40 +181,40 @@ double
pointer_accel_profile_linear_low_dpi(struct motion_filter *filter,
void *data,
double speed_in,
uint64_t time);
usec_t time);
double
pointer_accel_profile_linear(struct motion_filter *filter,
void *data,
double speed_in,
uint64_t time);
usec_t time);
double
touchpad_accel_profile_linear(struct motion_filter *filter,
void *data,
double speed_in,
uint64_t time);
usec_t time);
double
touchpad_lenovo_x230_accel_profile(struct motion_filter *filter,
void *data,
double speed_in,
uint64_t time);
usec_t time);
double
trackpoint_accel_profile(struct motion_filter *filter,
void *data,
double velocity,
uint64_t time);
usec_t time);
double
custom_accel_profile_fallback(struct motion_filter *filter,
void *data,
double speed_in,
uint64_t time);
usec_t time);
double
custom_accel_profile_motion(struct motion_filter *filter,
void *data,
double speed_in,
uint64_t time);
usec_t time);
double
custom_accel_profile_scroll(struct motion_filter *filter,
void *data,
double speed_in,
uint64_t time);
usec_t time);
#endif /* FILTER_H */

34
src/libinput-feature.h Normal file
View file

@ -0,0 +1,34 @@
/*
* Copyright © 2025 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.
*/
#pragma once
enum libinput_feature {
LIBINPUT_FEATURE_BUTTON_DEBOUNCING = 1,
LIBINPUT_FEATURE_WHEEL_DEBOUNCING,
LIBINPUT_FEATURE_TOUCHPAD_JUMP_DETECTION,
LIBINPUT_FEATURE_TOUCHPAD_HYSTERESIS,
LIBINPUT_FEATURE_TOUCHPAD_PALM_DETECTION,
_LIBINPUT_N_FEATURES
};

View file

@ -30,9 +30,11 @@
#include "util-strings.h"
#include "evdev-frame.h"
#include "libinput-feature.h"
#include "libinput-log.h"
#include "libinput-plugin-button-debounce.h"
#include "libinput-plugin.h"
#include "libinput-private.h"
#include "libinput-util.h"
#include "quirks.h"
#include "timer.h"
@ -134,10 +136,12 @@ struct plugin_device {
struct plugin_data *parent;
evdev_usage_t button_usage;
uint64_t button_time;
usec_t button_time;
enum debounce_state state;
bool spurious_enabled;
bool want_feature_disabled;
struct libinput_plugin_timer *timer;
struct libinput_plugin_timer *timer_short;
};
@ -175,7 +179,6 @@ plugin_data_destroy(void *d)
free(data);
}
DEFINE_DESTROY_CLEANUP_FUNC(plugin_data);
static void
plugin_destroy(struct libinput_plugin *libinput_plugin)
{
@ -192,6 +195,23 @@ log_debounce_bug(struct plugin_device *device, enum debounce_event event)
debounce_state_to_str(device->state));
}
static inline void
debounce_maybe_disable(struct plugin_device *device)
{
if (device->state != DEBOUNCE_STATE_IS_UP)
return;
if (device->want_feature_disabled) {
plugin_log_debug(device->parent->plugin,
"%s: disabled button debouncing on request\n",
libinput_device_get_name(device->device));
device->state = DEBOUNCE_STATE_DISABLED;
libinput_plugin_enable_device_event_frame(device->parent->plugin,
device->device,
false);
}
}
static inline void
debounce_set_state(struct plugin_device *device, enum debounce_state new_state)
{
@ -202,20 +222,21 @@ debounce_set_state(struct plugin_device *device, enum debounce_state new_state)
}
static inline void
debounce_set_timer(struct plugin_device *device, uint64_t time)
debounce_set_timer(struct plugin_device *device, usec_t time)
{
const int DEBOUNCE_TIMEOUT_BOUNCE = ms2us(25);
const usec_t DEBOUNCE_TIMEOUT_BOUNCE = usec_from_millis(25);
libinput_plugin_timer_set(device->timer, time + DEBOUNCE_TIMEOUT_BOUNCE);
libinput_plugin_timer_set(device->timer,
usec_add(time, DEBOUNCE_TIMEOUT_BOUNCE));
}
static inline void
debounce_set_timer_short(struct plugin_device *device, uint64_t time)
debounce_set_timer_short(struct plugin_device *device, usec_t time)
{
const int DEBOUNCE_TIMEOUT_SPURIOUS = ms2us(12);
const usec_t DEBOUNCE_TIMEOUT_SPURIOUS = usec_from_millis(12);
libinput_plugin_timer_set(device->timer_short,
time + DEBOUNCE_TIMEOUT_SPURIOUS);
usec_add(time, DEBOUNCE_TIMEOUT_SPURIOUS));
}
static inline void
@ -264,13 +285,18 @@ debounce_notify_button(struct plugin_device *device,
libinput_plugin_prepend_evdev_frame(device->parent->plugin,
device->device,
frame);
/* If we used the original frame, reset it to avoid re-sending any
* non-button events that may be present in this frame */
if (button_frame == NULL)
evdev_frame_reset(frame);
}
static void
debounce_is_up_handle_event(struct plugin_device *device,
enum debounce_event event,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
switch (event) {
case DEBOUNCE_EVENT_PRESS:
@ -293,7 +319,7 @@ static void
debounce_is_down_handle_event(struct plugin_device *device,
enum debounce_event event,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
switch (event) {
case DEBOUNCE_EVENT_PRESS:
@ -329,7 +355,7 @@ static void
debounce_is_down_waiting_handle_event(struct plugin_device *device,
enum debounce_event event,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
switch (event) {
case DEBOUNCE_EVENT_PRESS:
@ -358,7 +384,7 @@ static void
debounce_is_up_delaying_handle_event(struct plugin_device *device,
enum debounce_event event,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
switch (event) {
case DEBOUNCE_EVENT_PRESS:
@ -372,6 +398,7 @@ debounce_is_up_delaying_handle_event(struct plugin_device *device,
case DEBOUNCE_EVENT_TIMEOUT:
case DEBOUNCE_EVENT_OTHERBUTTON:
debounce_set_state(device, DEBOUNCE_STATE_IS_UP);
debounce_maybe_disable(device);
debounce_notify_button(device, frame, LIBINPUT_BUTTON_STATE_RELEASED);
break;
}
@ -381,7 +408,7 @@ static void
debounce_is_up_delaying_spurious_handle_event(struct plugin_device *device,
enum debounce_event event,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
switch (event) {
case DEBOUNCE_EVENT_PRESS:
@ -399,6 +426,7 @@ debounce_is_up_delaying_spurious_handle_event(struct plugin_device *device,
break;
case DEBOUNCE_EVENT_OTHERBUTTON:
debounce_set_state(device, DEBOUNCE_STATE_IS_UP);
debounce_maybe_disable(device);
debounce_notify_button(device, frame, LIBINPUT_BUTTON_STATE_RELEASED);
break;
}
@ -408,7 +436,7 @@ static void
debounce_is_up_detecting_spurious_handle_event(struct plugin_device *device,
enum debounce_event event,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
switch (event) {
case DEBOUNCE_EVENT_PRESS:
@ -424,12 +452,14 @@ debounce_is_up_detecting_spurious_handle_event(struct plugin_device *device,
break;
case DEBOUNCE_EVENT_TIMEOUT:
debounce_set_state(device, DEBOUNCE_STATE_IS_UP);
debounce_maybe_disable(device);
break;
case DEBOUNCE_EVENT_TIMEOUT_SHORT:
debounce_set_state(device, DEBOUNCE_STATE_IS_UP_WAITING);
break;
case DEBOUNCE_EVENT_OTHERBUTTON:
debounce_set_state(device, DEBOUNCE_STATE_IS_UP);
debounce_maybe_disable(device);
break;
}
}
@ -438,7 +468,7 @@ static void
debounce_is_down_detecting_spurious_handle_event(struct plugin_device *device,
enum debounce_event event,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
switch (event) {
case DEBOUNCE_EVENT_PRESS:
@ -467,7 +497,7 @@ static void
debounce_is_up_waiting_handle_event(struct plugin_device *device,
enum debounce_event event,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
switch (event) {
case DEBOUNCE_EVENT_PRESS:
@ -484,6 +514,7 @@ debounce_is_up_waiting_handle_event(struct plugin_device *device,
case DEBOUNCE_EVENT_TIMEOUT:
case DEBOUNCE_EVENT_OTHERBUTTON:
debounce_set_state(device, DEBOUNCE_STATE_IS_UP);
debounce_maybe_disable(device);
break;
}
}
@ -492,7 +523,7 @@ static void
debounce_is_down_delaying_handle_event(struct plugin_device *device,
enum debounce_event event,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
switch (event) {
case DEBOUNCE_EVENT_PRESS:
@ -517,7 +548,7 @@ static void
debounce_disabled_handle_event(struct plugin_device *device,
enum debounce_event event,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
switch (event) {
case DEBOUNCE_EVENT_PRESS:
@ -541,7 +572,7 @@ static void
debounce_handle_event(struct plugin_device *device,
enum debounce_event event,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
enum debounce_state current = device->state;
@ -602,7 +633,7 @@ debounce_handle_event(struct plugin_device *device,
static void
debounce_plugin_handle_frame(struct plugin_device *device,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
size_t nchanged = 0;
bool flushed = false;
@ -661,6 +692,7 @@ debounce_plugin_handle_frame(struct plugin_device *device,
!is_down ? DEBOUNCE_STATE_IS_DOWN
: DEBOUNCE_STATE_IS_UP);
flushed = false;
debounce_maybe_disable(device);
}
device->button_usage = e->usage;
@ -703,7 +735,7 @@ debounce_plugin_evdev_frame(struct libinput_plugin *libinput_plugin,
}
static void
debounce_timeout(struct libinput_plugin *plugin, uint64_t now, void *data)
debounce_timeout(struct libinput_plugin *plugin, usec_t now, void *data)
{
struct plugin_device *device = data;
@ -711,7 +743,7 @@ debounce_timeout(struct libinput_plugin *plugin, uint64_t now, void *data)
}
static void
debounce_timeout_short(struct libinput_plugin *plugin, uint64_t now, void *data)
debounce_timeout_short(struct libinput_plugin *plugin, usec_t now, void *data)
{
struct plugin_device *device = data;
@ -725,6 +757,9 @@ debounce_plugin_device_added(struct libinput_plugin *libinput_plugin,
if (!libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER))
return;
if (libinput_device_is_virtual(device))
return;
_unref_(udev_device) *udev_device = libinput_device_get_udev_device(device);
if (udev_device) {
const char *prop = udev_device_get_property_value(udev_device,
@ -743,6 +778,15 @@ debounce_plugin_device_added(struct libinput_plugin *libinput_plugin,
libinput_plugin_enable_device_event_frame(libinput_plugin, device, true);
/* We don't care about BTN_TRIGGER_HAPPY_* */
for (unsigned int code = BTN_0; code <= KEY_OK; code++) {
evdev_usage_t usage = evdev_usage_from_code(EV_KEY, code);
if (evdev_usage_is_button(usage)) {
libinput_plugin_enable_evdev_usage(libinput_plugin,
evdev_usage_enum(usage));
}
}
struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
struct plugin_device *pd = zalloc(sizeof(*pd));
pd->device = libinput_device_ref(device);
@ -779,6 +823,24 @@ debounce_plugin_device_removed(struct libinput_plugin *libinput_plugin,
}
}
static void
debounce_plugin_feature_disabled(struct libinput_plugin *libinput_plugin,
struct libinput_device *device,
enum libinput_feature feature)
{
if (feature != LIBINPUT_FEATURE_BUTTON_DEBOUNCING)
return;
struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
struct plugin_device *pd;
list_for_each(pd, &plugin->devices, link) {
if (pd->device == device) {
pd->want_feature_disabled = true;
return;
}
}
}
static const struct libinput_plugin_interface interface = {
.run = NULL,
.destroy = plugin_destroy,
@ -787,6 +849,7 @@ static const struct libinput_plugin_interface interface = {
.device_added = debounce_plugin_device_added,
.device_removed = debounce_plugin_device_removed,
.evdev_frame = debounce_plugin_evdev_frame,
.feature_disabled = debounce_plugin_feature_disabled,
};
void

1360
src/libinput-plugin-lua.c Normal file

File diff suppressed because it is too large Load diff

32
src/libinput-plugin-lua.h Normal file
View file

@ -0,0 +1,32 @@
/*
* Copyright © 2025 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.
*/
#pragma once
#include "config.h"
#include "libinput-plugin.h"
#include "libinput.h"
struct libinput_plugin *
libinput_lua_plugin_new_from_path(struct libinput *libinput, const char *path);

View file

@ -0,0 +1,123 @@
/*
* Copyright © 2025 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 <libevdev/libevdev.h>
#include "evdev.h"
#include "libinput-plugin-mouse-wheel-lowres.h"
#include "libinput-plugin.h"
#include "src/evdev-frame.h"
static void
wheel_plugin_device_new(struct libinput_plugin *libinput_plugin,
struct libinput_device *device,
struct libevdev *libevdev,
struct udev_device *udev_device)
{
struct evdev_device *evdev = evdev_device(device);
if (libevdev_has_event_code(libevdev, EV_REL, REL_WHEEL_HI_RES) ||
libevdev_has_event_code(libevdev, EV_REL, REL_HWHEEL_HI_RES))
return;
if (libevdev_has_event_code(libevdev, EV_REL, REL_WHEEL) ||
libevdev_has_event_code(libevdev, EV_REL, REL_HWHEEL))
evdev_log_info(evdev,
"emulating high-resolution scroll wheel events.\n");
if (libevdev_has_event_code(libevdev, EV_REL, REL_WHEEL))
libevdev_enable_event_code(libevdev, EV_REL, REL_WHEEL_HI_RES, NULL);
if (libevdev_has_event_code(libevdev, EV_REL, REL_HWHEEL))
libevdev_enable_event_code(libevdev, EV_REL, REL_HWHEEL_HI_RES, NULL);
libinput_plugin_enable_device_event_frame(libinput_plugin, device, true);
libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_REL_WHEEL);
libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_REL_HWHEEL);
/* A device may have those disabled via a quirk but we just re-enabled it
* above. Make sure we get those events too to filter them out */
libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_REL_WHEEL_HI_RES);
libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_REL_HWHEEL_HI_RES);
}
static void
wheel_plugin_evdev_frame(struct libinput_plugin *libinput_plugin,
struct libinput_device *device,
struct evdev_frame *frame)
{
size_t nevents;
struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
_unref_(evdev_frame) *filtered_frame = evdev_frame_new(nevents + 2);
for (size_t i = 0; i < nevents; i++) {
struct evdev_event *e = &events[i];
switch (evdev_usage_enum(e->usage)) {
case EVDEV_REL_WHEEL_HI_RES:
case EVDEV_REL_HWHEEL_HI_RES:
/* In the uncommon case that our device sends high-res events
* filter those out. This can happen on devices that have the
* highres scroll axes disabled via quirks. The device still
* sends events so when we re-enable the axis in
* wheel_plugin_device_new we get the device events again,
* effectively duplicating the high resolution scroll events.
*/
break;
case EVDEV_REL_WHEEL:
evdev_frame_append(filtered_frame, e, 1);
evdev_frame_append_one(filtered_frame,
evdev_usage_from(EVDEV_REL_WHEEL_HI_RES),
e->value * 120);
break;
case EVDEV_REL_HWHEEL:
evdev_frame_append(filtered_frame, e, 1);
evdev_frame_append_one(
filtered_frame,
evdev_usage_from(EVDEV_REL_HWHEEL_HI_RES),
e->value * 120);
break;
default:
evdev_frame_append(filtered_frame, e, 1);
break;
}
}
evdev_frame_set(frame,
evdev_frame_get_events(filtered_frame, NULL),
evdev_frame_get_count(filtered_frame));
}
static const struct libinput_plugin_interface interface = {
.device_new = wheel_plugin_device_new,
.evdev_frame = wheel_plugin_evdev_frame,
};
void
libinput_mouse_plugin_wheel_lowres(struct libinput *libinput)
{
_unref_(libinput_plugin) *p =
libinput_plugin_new(libinput, "mouse-wheel-lowres", &interface, NULL);
}

View file

@ -0,0 +1,30 @@
/*
* Copyright © 2025 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 "libinput-plugin.h"
#include "libinput.h"
void
libinput_mouse_plugin_wheel_lowres(struct libinput *libinput);

View file

@ -31,14 +31,16 @@
#include "evdev-fallback.h"
#include "evdev.h"
#include "libinput-feature.h"
#include "libinput-log.h"
#include "libinput-plugin-mouse-wheel.h"
#include "libinput-plugin.h"
#include "libinput-private.h"
#include "libinput-util.h"
#define ACC_V120_TRIGGER_THRESHOLD 30 /* 1/4 of a wheel detent */
#define ACC_V120_THRESHOLD 59
#define WHEEL_SCROLL_TIMEOUT ms2us(500)
#define ACC_V120_THRESHOLD 47 /* Good for both high-ish multipliers (8/120) and the rest of the mice (30/120, 40/120, etc) */
const usec_t WHEEL_SCROLL_TIMEOUT = { 500 * 1000 };
enum wheel_state {
WHEEL_STATE_NONE,
@ -62,8 +64,7 @@ enum wheel_event {
};
enum ignore_strategy {
MAYBE, /* use heuristics but don't yet accumulate */
PASSTHROUGH, /* do not accumulate, pass through */
MAYBE = 1, /* use heuristics but don't yet accumulate */
ACCUMULATE, /* accumulate scroll wheel events */
ALWAYS_ACCUMULATE, /* always accumulate wheel events */
};
@ -83,6 +84,8 @@ struct plugin_device {
int min_movement;
struct ratelimit hires_warning_limit;
bool want_feature_disabled;
};
struct plugin_data {
@ -123,12 +126,13 @@ log_wheel_bug(struct plugin_device *pd, enum wheel_event event)
}
static inline void
wheel_set_scroll_timer(struct plugin_device *pd, uint64_t time)
wheel_set_scroll_timer(struct plugin_device *pd, usec_t time)
{
if (!pd->scroll_timer)
return;
libinput_plugin_timer_set(pd->scroll_timer, time + WHEEL_SCROLL_TIMEOUT);
libinput_plugin_timer_set(pd->scroll_timer,
usec_add(time, WHEEL_SCROLL_TIMEOUT));
}
static inline void
@ -140,10 +144,29 @@ wheel_cancel_scroll_timer(struct plugin_device *pd)
libinput_plugin_timer_cancel(pd->scroll_timer);
}
static inline void
wheel_maybe_disable(struct plugin_device *device)
{
if (device->state != WHEEL_STATE_NONE)
return;
if (device->want_feature_disabled) {
plugin_log_debug(device->parent->plugin,
"%s: disabled wheel debouncing on request\n",
libinput_device_get_name(device->device));
libinput_plugin_enable_device_event_frame(device->parent->plugin,
device->device,
false);
libinput_plugin_timer_cancel(device->scroll_timer);
device->scroll_timer =
libinput_plugin_timer_unref(device->scroll_timer);
}
}
static void
wheel_handle_event_on_state_none(struct plugin_device *pd,
enum wheel_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case WHEEL_EVENT_SCROLL:
@ -152,7 +175,6 @@ wheel_handle_event_on_state_none(struct plugin_device *pd,
case ALWAYS_ACCUMULATE:
pd->state = WHEEL_STATE_ACCUMULATING_SCROLL;
break;
case PASSTHROUGH:
case MAYBE:
pd->state = WHEEL_STATE_SCROLLING;
break;
@ -170,7 +192,7 @@ wheel_handle_event_on_state_none(struct plugin_device *pd,
static void
wheel_handle_event_on_state_accumulating_scroll(struct plugin_device *pd,
enum wheel_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case WHEEL_EVENT_SCROLL_ACCUMULATED:
@ -192,7 +214,7 @@ wheel_handle_event_on_state_accumulating_scroll(struct plugin_device *pd,
static void
wheel_handle_event_on_state_scrolling(struct plugin_device *pd,
enum wheel_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case WHEEL_EVENT_SCROLL:
@ -212,7 +234,7 @@ wheel_handle_event_on_state_scrolling(struct plugin_device *pd,
}
static void
wheel_handle_event(struct plugin_device *pd, enum wheel_event event, uint64_t time)
wheel_handle_event(struct plugin_device *pd, enum wheel_event event, usec_t time)
{
enum wheel_state oldstate = pd->state;
@ -298,14 +320,14 @@ wheel_queue_scroll_events(struct plugin_device *pd, struct evdev_frame *frame)
static void
wheel_handle_state_none(struct plugin_device *pd,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
}
static void
wheel_handle_state_accumulating_scroll(struct plugin_device *pd,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
wheel_remove_scroll_events(frame);
@ -319,7 +341,7 @@ wheel_handle_state_accumulating_scroll(struct plugin_device *pd,
static void
wheel_handle_state_scrolling(struct plugin_device *pd,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
wheel_remove_scroll_events(frame);
wheel_queue_scroll_events(pd, frame);
@ -328,7 +350,7 @@ wheel_handle_state_scrolling(struct plugin_device *pd,
static void
wheel_handle_direction_change(struct plugin_device *pd,
struct evdev_event *e,
uint64_t time)
usec_t time)
{
enum wheel_direction new_dir = WHEEL_DIR_UNKNOW;
@ -365,7 +387,7 @@ wheel_update_strategy(struct plugin_device *pd, int32_t value)
}
static void
wheel_process_relative(struct plugin_device *pd, struct evdev_event *e, uint64_t time)
wheel_process_relative(struct plugin_device *pd, struct evdev_event *e, usec_t time)
{
switch (evdev_usage_enum(e->usage)) {
case EVDEV_REL_WHEEL:
@ -396,7 +418,7 @@ wheel_process_relative(struct plugin_device *pd, struct evdev_event *e, uint64_t
}
static void
wheel_handle_state(struct plugin_device *pd, struct evdev_frame *frame, uint64_t time)
wheel_handle_state(struct plugin_device *pd, struct evdev_frame *frame, usec_t time)
{
struct evdev_device *evdev = evdev_device(pd->device);
@ -422,10 +444,12 @@ wheel_handle_state(struct plugin_device *pd, struct evdev_frame *frame, uint64_t
wheel_handle_state_scrolling(pd, frame, time);
break;
}
wheel_maybe_disable(pd);
}
static void
wheel_on_scroll_timer_timeout(struct libinput_plugin *plugin, uint64_t now, void *data)
wheel_on_scroll_timer_timeout(struct libinput_plugin *plugin, usec_t now, void *data)
{
struct plugin_device *pd = data;
@ -437,32 +461,27 @@ wheel_plugin_device_create(struct libinput_plugin *libinput_plugin,
struct plugin_data *plugin,
struct libinput_device *device)
{
struct evdev_device *evdev = evdev_device(device);
struct plugin_device *pd = zalloc(sizeof(*pd));
if (libinput_device_is_virtual(device))
return NULL;
struct plugin_device *pd = zalloc(sizeof(*pd));
pd->parent = plugin;
pd->device = libinput_device_ref(device);
pd->state = WHEEL_STATE_NONE;
pd->dir = WHEEL_DIR_UNKNOW;
pd->min_movement = ACC_V120_THRESHOLD;
ratelimit_init(&pd->hires_warning_limit, s2us(24 * 60 * 60), 1);
ratelimit_init(&pd->hires_warning_limit, usec_from_hours(24), 1);
if (evdev_device_is_virtual(evdev))
pd->ignore_small_hi_res_movements = PASSTHROUGH;
else if (libinput_device_has_model_quirk(device,
QUIRK_MODEL_LOGITECH_MX_MASTER_3))
if (libinput_device_has_model_quirk(device, QUIRK_MODEL_SCROLL_ON_MIDDLE_CLICK))
pd->ignore_small_hi_res_movements = ALWAYS_ACCUMULATE;
else
pd->ignore_small_hi_res_movements = MAYBE;
if (pd->ignore_small_hi_res_movements != PASSTHROUGH) {
pd->scroll_timer =
libinput_plugin_timer_new(libinput_plugin,
libinput_device_get_sysname(device),
wheel_on_scroll_timer_timeout,
pd);
}
return pd;
}
@ -504,12 +523,20 @@ wheel_plugin_device_new(struct libinput_plugin *libinput_plugin,
!libevdev_has_event_code(libevdev, EV_REL, REL_HWHEEL_HI_RES))
return;
libinput_plugin_enable_device_event_frame(libinput_plugin, device, true);
struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
struct plugin_device *pd =
wheel_plugin_device_create(libinput_plugin, plugin, device);
if (!pd)
return;
list_take_append(&plugin->devices, pd, link);
libinput_plugin_enable_device_event_frame(libinput_plugin, device, true);
libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_REL_WHEEL);
libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_REL_WHEEL_HI_RES);
libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_REL_HWHEEL);
libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_REL_HWHEEL_HI_RES);
}
static void
@ -549,7 +576,7 @@ wheel_plugin_device_removed(struct libinput_plugin *libinput_plugin,
}
static void
wheel_handle_frame(struct plugin_device *pd, struct evdev_frame *frame, uint64_t time)
wheel_handle_frame(struct plugin_device *pd, struct evdev_frame *frame, usec_t time)
{
size_t nevents;
struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
@ -576,7 +603,7 @@ wheel_plugin_evdev_frame(struct libinput_plugin *libinput_plugin,
{
struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
struct plugin_device *pd;
uint64_t time = evdev_frame_get_time(frame);
usec_t time = evdev_frame_get_time(frame);
list_for_each(pd, &plugin->devices, link) {
if (pd->device == device) {
@ -586,6 +613,24 @@ wheel_plugin_evdev_frame(struct libinput_plugin *libinput_plugin,
}
}
static void
wheel_plugin_feature_disabled(struct libinput_plugin *libinput_plugin,
struct libinput_device *device,
enum libinput_feature feature)
{
if (feature != LIBINPUT_FEATURE_WHEEL_DEBOUNCING)
return;
struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
struct plugin_device *pd;
list_for_each(pd, &plugin->devices, link) {
if (pd->device == device) {
pd->want_feature_disabled = true;
return;
}
}
}
static const struct libinput_plugin_interface interface = {
.run = NULL,
.destroy = wheel_plugin_destroy,
@ -594,6 +639,7 @@ static const struct libinput_plugin_interface interface = {
.device_added = wheel_plugin_device_added,
.device_removed = wheel_plugin_device_removed,
.evdev_frame = wheel_plugin_evdev_frame,
.feature_disabled = wheel_plugin_feature_disabled,
};
void

View file

@ -89,7 +89,7 @@ mtdev_plugin_device_handle_frame(struct libinput_plugin *libinput_plugin,
struct plugin_device *device,
struct evdev_frame *frame)
{
uint64_t time = evdev_frame_get_time(frame);
usec_t time = evdev_frame_get_time(frame);
size_t nevents;
struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
for (size_t i = 0; i < nevents; i++) {

View file

@ -47,8 +47,3 @@ libinput_plugin_notify_device_ignored(struct libinput_plugin *plugin,
void
libinput_plugin_notify_device_removed(struct libinput_plugin *plugin,
struct libinput_device *device);
void
libinput_plugin_notify_evdev_frame(struct libinput_plugin *plugin,
struct libinput_device *device,
struct evdev_frame *frame);

View file

@ -34,6 +34,7 @@
#include "evdev-frame.h"
#include "libinput.h"
enum libinput_feature;
struct libinput;
struct libinput_plugin;
@ -41,6 +42,7 @@ struct libinput_plugin_system {
char **directories; /* NULL once loaded == true */
bool loaded;
bool autoload;
struct list plugins;
struct list removed_plugins;
@ -52,8 +54,7 @@ void
libinput_plugin_system_init(struct libinput_plugin_system *system);
void
libinput_plugin_system_load_internal_plugins(struct libinput *libinput,
struct libinput_plugin_system *system);
libinput_plugin_system_autoload(struct libinput *libinput);
void
libinput_plugin_system_destroy(struct libinput_plugin_system *system);
@ -95,3 +96,9 @@ void
libinput_plugin_system_notify_evdev_frame(struct libinput_plugin_system *system,
struct libinput_device *device,
struct evdev_frame *frame);
void
libinput_plugin_system_notify_device_feature_disabled(
struct libinput_plugin_system *system,
struct libinput_device *device,
enum libinput_feature feature);

View file

@ -180,7 +180,7 @@ double_tool_plugin_device_handle_frame(struct libinput_plugin *libinput_plugin,
bool eraser_toggled = eraser_toggle != NULL;
bool pen_toggled = pen_toggle != NULL;
#if EVENT_DEBUGGING
#ifdef EVENT_DEBUGGING
plugin_log_debug(libinput_plugin,
"device %s: tool state: pen:%s eraser:%s\n",
libinput_device_get_name(device->device),
@ -217,6 +217,9 @@ double_tool_plugin_device_handle_frame(struct libinput_plugin *libinput_plugin,
libinput_plugin,
"device %s: device is fine, unregistering device\n",
libinput_device_get_name(device->device));
libinput_plugin_enable_device_event_frame(libinput_plugin,
device->device,
false);
plugin_device_destroy(device);
return;
}
@ -339,6 +342,8 @@ double_tool_plugin_device_added(struct libinput_plugin *libinput_plugin,
return;
libinput_plugin_enable_device_event_frame(libinput_plugin, device, true);
libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_BTN_TOOL_PEN);
libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_BTN_TOOL_RUBBER);
struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
struct plugin_device *pd = zalloc(sizeof(*pd));

View file

@ -35,7 +35,7 @@
#include "libinput-plugin.h"
#include "libinput-util.h"
static int ERASER_BUTTON_DELAY = 30 * 1000; /* µs */
static usec_t ERASER_BUTTON_DELAY = { 30 * 1000 }; /* µs */
enum frame_filter_state {
DISCARD,
@ -146,9 +146,9 @@ eraser_button_set_state(struct plugin_device *device, enum eraser_button_state t
}
static void
eraser_button_set_timer(struct plugin_device *device, uint64_t time)
eraser_button_set_timer(struct plugin_device *device, usec_t time)
{
libinput_plugin_timer_set(device->timer, time + ERASER_BUTTON_DELAY);
libinput_plugin_timer_set(device->timer, usec_add(time, ERASER_BUTTON_DELAY));
}
static void
@ -235,7 +235,7 @@ static enum frame_filter_state
eraser_button_neutral_handle_event(struct plugin_device *device,
struct evdev_frame *frame,
enum eraser_button_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case ERASER_EVENT_PEN_ENTERING_PROX:
@ -266,7 +266,7 @@ static enum frame_filter_state
eraser_button_pending_eraser_handle_event(struct plugin_device *device,
struct evdev_frame *frame,
enum eraser_button_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case ERASER_EVENT_PEN_ENTERING_PROX:
@ -306,7 +306,7 @@ static enum frame_filter_state
eraser_button_button_held_handle_event(struct plugin_device *device,
struct evdev_frame *frame,
enum eraser_button_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case ERASER_EVENT_PEN_ENTERING_PROX:
@ -339,7 +339,7 @@ static enum frame_filter_state
eraser_button_button_released_handle_event(struct plugin_device *device,
struct evdev_frame *frame,
enum eraser_button_event event,
uint64_t time)
usec_t time)
{
switch (event) {
case ERASER_EVENT_PEN_ENTERING_PROX:
@ -379,7 +379,7 @@ static enum frame_filter_state
eraser_button_handle_state(struct plugin_device *device,
struct evdev_frame *frame,
enum eraser_button_event event,
uint64_t time)
usec_t time)
{
enum eraser_button_state state = device->state;
enum frame_filter_state ret = PROCESS;
@ -426,7 +426,7 @@ eraser_button_handle_state(struct plugin_device *device,
static void
eraser_button_handle_frame(struct plugin_device *device,
struct evdev_frame *frame,
uint64_t time)
usec_t time)
{
if (device->mode == LIBINPUT_CONFIG_ERASER_BUTTON_DEFAULT)
return;
@ -505,7 +505,7 @@ eraser_button_plugin_evdev_frame(struct libinput_plugin *libinput_plugin,
{
struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
struct plugin_device *pd;
uint64_t time = evdev_frame_get_time(frame);
usec_t time = evdev_frame_get_time(frame);
list_for_each(pd, &plugin->devices, link) {
if (pd->device == device) {
@ -516,7 +516,7 @@ eraser_button_plugin_evdev_frame(struct libinput_plugin *libinput_plugin,
}
static void
eraser_button_timer_func(struct libinput_plugin *plugin, uint64_t now, void *d)
eraser_button_timer_func(struct libinput_plugin *plugin, usec_t now, void *d)
{
struct plugin_device *device = d;
@ -541,6 +541,13 @@ eraser_button_plugin_device_added(struct libinput_plugin *libinput_plugin,
return;
libinput_plugin_enable_device_event_frame(libinput_plugin, device, true);
libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_BTN_TOOL_PEN);
libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_BTN_TOOL_RUBBER);
libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_BTN_TOUCH);
/* These are the only ones we allow setting the eraser button to */
libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_BTN_STYLUS);
libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_BTN_STYLUS2);
libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_BTN_STYLUS3);
struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
struct plugin_device *pd = zalloc(sizeof(*pd));
@ -600,7 +607,7 @@ void
libinput_tablet_plugin_eraser_button(struct libinput *libinput)
{
if (getenv("LIBINPUT_RUNNING_TEST_SUITE"))
ERASER_BUTTON_DELAY = ms2us(150);
ERASER_BUTTON_DELAY = usec_from_millis(150);
_destroy_(plugin_data) *plugin = zalloc(sizeof(*plugin));
list_init(&plugin->devices);

View file

@ -53,6 +53,8 @@ struct plugin_device {
struct list link;
struct libinput_device *device;
bitmask_t tool_state;
bool pen_forced_into_proximity;
size_t pen_prox_out_events;
};
struct plugin_data {
@ -89,6 +91,18 @@ plugin_destroy(struct libinput_plugin *libinput_plugin)
plugin_data_destroy(plugin);
}
static void
forced_tool_plugin_force_pen_out(struct libinput_plugin *libinput_plugin,
struct libinput_device *device,
struct evdev_frame *frame)
{
_unref_(evdev_frame) *prox_out_frame = evdev_frame_new(2);
evdev_frame_append_one(prox_out_frame, evdev_usage_from(EVDEV_BTN_TOOL_PEN), 0);
evdev_frame_set_time(prox_out_frame, evdev_frame_get_time(frame));
libinput_plugin_prepend_evdev_frame(libinput_plugin, device, prox_out_frame);
}
static void
forced_tool_plugin_device_handle_frame(struct libinput_plugin *libinput_plugin,
struct plugin_device *device,
@ -103,21 +117,51 @@ forced_tool_plugin_device_handle_frame(struct libinput_plugin *libinput_plugin,
struct evdev_event *event = &events[i];
switch (evdev_usage_enum(event->usage)) {
case EVDEV_BTN_TOOL_PEN:
if (event->value == 1) {
bitmask_set_bit(&device->tool_state, BTN_TOOL_PEN);
} else {
bitmask_clear_bit(&device->tool_state, BTN_TOOL_PEN);
device->pen_forced_into_proximity = false;
/* If we get three valid pen proximity out events, let's
* assume this device works fine and disable our plugin
*/
if (++device->pen_prox_out_events > 2) {
plugin_log_debug(
libinput_plugin,
"%s: forced tool handling unloaded\n",
libinput_device_get_name(
device->device));
libinput_plugin_enable_device_event_frame(
libinput_plugin,
device->device,
false);
plugin_device_destroy(device);
return;
}
}
return; /* Nothing to do */
case EVDEV_BTN_TOOL_RUBBER:
case EVDEV_BTN_TOOL_BRUSH:
case EVDEV_BTN_TOOL_PENCIL:
case EVDEV_BTN_TOOL_AIRBRUSH:
case EVDEV_BTN_TOOL_MOUSE:
case EVDEV_BTN_TOOL_LENS:
case EVDEV_BTN_TOOL_LENS: {
int code = evdev_event_code(event) - BTN_TOOL_PEN;
if (event->value == 1) {
bitmask_set_bit(&device->tool_state,
evdev_event_code(event) - BTN_TOOL_PEN);
} else {
bitmask_clear_bit(&device->tool_state,
evdev_event_code(event) -
BTN_TOOL_PEN);
bitmask_set_bit(&device->tool_state, code);
if (device->pen_forced_into_proximity) {
forced_tool_plugin_force_pen_out(
libinput_plugin,
device->device,
frame);
device->pen_forced_into_proximity = false;
}
} else {
bitmask_clear_bit(&device->tool_state, code);
}
return; /* Keep the frame as-is */
}
return; /* Nothing to do */
case EVDEV_ABS_X:
case EVDEV_ABS_Y:
case EVDEV_ABS_Z: /* rotation */
@ -155,6 +199,7 @@ forced_tool_plugin_device_handle_frame(struct libinput_plugin *libinput_plugin,
evdev_frame_append_one(frame,
evdev_usage_from(EVDEV_BTN_TOOL_PEN),
1); /* libinput's event frame will have space */
device->pen_forced_into_proximity = true;
}
static void

View file

@ -39,14 +39,14 @@
/* The tablet sends events every ~2ms , 50ms should be plenty enough to
detect out-of-range.
This value is higher during test suite runs */
static int FORCED_PROXOUT_TIMEOUT = 50 * 1000; /* µs */
static usec_t FORCED_PROXOUT_TIMEOUT = { 50 * 1000 };
struct plugin_device {
struct list link;
struct libinput_plugin_timer *prox_out_timer;
bool proximity_out_forced;
uint64_t last_event_time;
usec_t last_event_time;
bool pen_state;
bitmask_t button_state;
@ -86,18 +86,16 @@ plugin_data_destroy(void *d)
free(data);
}
DEFINE_DESTROY_CLEANUP_FUNC(plugin_data);
static inline void
proximity_timer_plugin_set_timer(struct plugin_device *device, uint64_t time)
proximity_timer_plugin_set_timer(struct plugin_device *device, usec_t time)
{
libinput_plugin_timer_set(device->prox_out_timer,
time + FORCED_PROXOUT_TIMEOUT);
usec_add(time, FORCED_PROXOUT_TIMEOUT));
}
static void
tablet_proximity_out_quirk_timer_func(struct libinput_plugin *plugin,
uint64_t now,
usec_t now,
void *data)
{
struct plugin_device *device = data;
@ -107,7 +105,8 @@ tablet_proximity_out_quirk_timer_func(struct libinput_plugin *plugin,
return;
}
if (device->last_event_time > now - FORCED_PROXOUT_TIMEOUT) {
usec_t proxout_time = usec_sub(now, FORCED_PROXOUT_TIMEOUT);
if (usec_cmp(device->last_event_time, proxout_time) > 0) {
proximity_timer_plugin_set_timer(device, device->last_event_time);
return;
}
@ -146,11 +145,10 @@ proximity_timer_plugin_device_handle_frame(struct libinput_plugin *libinput_plug
struct plugin_device *device,
struct evdev_frame *frame)
{
uint64_t time = evdev_frame_get_time(frame);
usec_t time = evdev_frame_get_time(frame);
/* First event after adding a device - by definition the pen
*
* is in proximity if we get this one */
if (device->last_event_time == 0)
if (usec_is_zero(device->last_event_time))
proximity_timer_plugin_set_timer(device, time);
device->last_event_time = time;
@ -188,6 +186,9 @@ proximity_timer_plugin_device_handle_frame(struct libinput_plugin *libinput_plug
case EVDEV_BTN_TOOL_FINGER:
case EVDEV_BTN_TOOL_MOUSE:
case EVDEV_BTN_TOOL_LENS:
libinput_plugin_enable_device_event_frame(libinput_plugin,
device->device,
false);
plugin_device_destroy(device);
return;
default:
@ -207,6 +208,9 @@ proximity_timer_plugin_device_handle_frame(struct libinput_plugin *libinput_plug
plugin_log_debug(libinput_plugin,
"%s: proximity out timer unloaded\n",
libinput_device_get_name(device->device));
libinput_plugin_enable_device_event_frame(libinput_plugin,
device->device,
false);
plugin_device_destroy(device);
return;
}
@ -299,7 +303,7 @@ libinput_tablet_plugin_proximity_timer(struct libinput *libinput)
/* Stop false positives caused by the forced proximity code */
if (getenv("LIBINPUT_RUNNING_TEST_SUITE"))
FORCED_PROXOUT_TIMEOUT = 150 * 1000; /* µs */
FORCED_PROXOUT_TIMEOUT = usec_from_millis(150);
_unref_(libinput_plugin) *p = libinput_plugin_new(libinput,
"tablet-proximity-timer",

View file

@ -28,11 +28,16 @@
#include "util-files.h"
#include "util-list.h"
#include "evdev-frame.h"
#include "evdev-plugin.h"
#include "libinput-feature.h"
#include "libinput-plugin-button-debounce.h"
#include "libinput-plugin-lua.h"
#include "libinput-plugin-mouse-wheel-lowres.h"
#include "libinput-plugin-mouse-wheel.h"
#include "libinput-plugin-mtdev.h"
#include "libinput-plugin-private.h"
#include "libinput-plugin-system.h"
#include "libinput-plugin-tablet-double-tool.h"
#include "libinput-plugin-tablet-eraser-button.h"
#include "libinput-plugin-tablet-forced-tool.h"
@ -60,6 +65,8 @@ struct libinput_plugin {
struct list *after;
struct list *before;
} event_queue;
struct evdev_mask *mask;
};
struct libinput_plugin_timer {
@ -67,7 +74,7 @@ struct libinput_plugin_timer {
struct list link;
struct libinput_plugin *plugin;
struct libinput_timer timer;
void (*func)(struct libinput_plugin *plugin, uint64_t now, void *user_data);
void (*func)(struct libinput_plugin *plugin, usec_t now, void *user_data);
void *user_data;
};
@ -91,6 +98,10 @@ plugin_log_msg(struct libinput_plugin *plugin,
log_msg(plugin->libinput, priority, "%s%s", prefix, message);
}
static void
libinput_plugin_system_load_internal_plugins(struct libinput *libinput,
struct libinput_plugin_system *system);
struct libinput_plugin *
libinput_plugin_new(struct libinput *libinput,
const char *name,
@ -151,6 +162,7 @@ libinput_plugin_unref(struct libinput_plugin *plugin)
if (plugin->interface->destroy)
plugin->interface->destroy(plugin);
free(plugin->name);
evdev_mask_destroy(plugin->mask);
free(plugin);
}
return NULL;
@ -192,6 +204,32 @@ libinput_plugin_enable_device_event_frame(struct libinput_plugin *plugin,
}
}
void
libinput_plugin_enable_evdev_usage(struct libinput_plugin *plugin,
enum evdev_usage usage)
{
if (!plugin->mask)
plugin->mask = evdev_mask_new();
evdev_mask_set_usage(plugin->mask, evdev_usage_from(usage));
}
void
libinput_plugin_disable_device_feature(struct libinput_plugin *plugin,
struct libinput_device *device,
enum libinput_feature feature)
{
struct libinput *libinput = plugin->libinput;
/* During device-added, only some plugins are loaded so this notifies
* some of the plugins. All plugins are notified once device-added is
* complete. */
libinput_plugin_system_notify_device_feature_disabled(&libinput->plugin_system,
device,
feature);
bitmask_set_bit(&device->disabled_features, feature);
}
struct plugin_queued_event {
struct list link;
struct evdev_frame *frame; /* owns a ref */
@ -312,13 +350,92 @@ libinput_plugin_notify_device_removed(struct libinput_plugin *plugin,
plugin->interface->device_removed(plugin, device);
}
void
libinput_plugin_notify_evdev_frame(struct libinput_plugin *plugin,
struct libinput_device *device,
struct evdev_frame *frame)
static void
plugin_system_append_path(struct libinput_plugin_system *plugin_system,
const char *path)
{
if (plugin->interface->evdev_frame)
plugin->interface->evdev_frame(plugin, device, frame);
if (strv_find(plugin_system->directories, path, NULL))
return;
plugin_system->directories =
strv_append_strdup(plugin_system->directories, path);
}
LIBINPUT_EXPORT void
libinput_plugin_system_append_path(struct libinput *libinput, const char *path)
{
if (libinput->plugin_system.loaded) {
log_bug_client(libinput, "plugin system already initialized\n");
return;
}
libinput->plugin_system.autoload = false;
plugin_system_append_path(&libinput->plugin_system, path);
}
LIBINPUT_EXPORT void
libinput_plugin_system_append_default_paths(struct libinput *libinput)
{
if (libinput->plugin_system.loaded) {
log_bug_client(libinput, "plugin system already initialized\n");
return;
}
libinput->plugin_system.autoload = false;
plugin_system_append_path(&libinput->plugin_system, LIBINPUT_PLUGIN_ETCDIR);
plugin_system_append_path(&libinput->plugin_system, LIBINPUT_PLUGIN_LIBDIR);
}
void
libinput_plugin_system_autoload(struct libinput *libinput)
{
if (libinput->plugin_system.loaded)
return;
if (libinput->plugin_system.autoload) {
libinput_plugin_system_append_default_paths(libinput);
libinput_plugin_system_load_plugins(libinput,
LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
} else {
libinput_plugin_system_load_internal_plugins(libinput,
&libinput->plugin_system);
}
}
LIBINPUT_EXPORT int
libinput_plugin_system_load_plugins(struct libinput *libinput,
enum libinput_plugin_system_flags flags)
{
if (libinput->plugin_system.loaded) {
log_bug_client(libinput, "%s() called twice\n", __func__);
return 0;
}
#ifdef HAVE_LUA
_autostrvfree_ char **directories = steal(&libinput->plugin_system.directories);
size_t nfiles = 0;
_autostrvfree_ char **plugin_files =
list_files((const char **)directories, ".lua", &nfiles);
for (size_t i = 0; i < nfiles; i++) {
char *path = plugin_files[i];
log_debug(libinput, "Loading plugin from %s\n", path);
libinput_lua_plugin_new_from_path(libinput, path);
}
#endif
libinput_plugin_system_load_internal_plugins(libinput,
&libinput->plugin_system);
libinput->plugin_system.loaded = true;
libinput_plugin_system_run(&libinput->plugin_system);
#ifdef HAVE_PLUGINS
return 0;
#else
return -ENOSYS;
#endif
}
void
@ -367,11 +484,16 @@ void
libinput_plugin_system_init(struct libinput_plugin_system *system)
{
system->loaded = false;
#ifdef AUTOLOAD_PLUGINS
system->autoload = true;
#else
system->autoload = false;
#endif
list_init(&system->plugins);
list_init(&system->removed_plugins);
}
void
static void
libinput_plugin_system_load_internal_plugins(struct libinput *libinput,
struct libinput_plugin_system *system)
{
@ -380,9 +502,7 @@ libinput_plugin_system_load_internal_plugins(struct libinput *libinput,
system->loaded = true;
/* FIXME: this should really be one of the first in the sequence
* so plugins don't have to take care of this? */
#if HAVE_MTDEV
#ifdef HAVE_MTDEV
libinput_mtdev_plugin(libinput);
#endif
libinput_tablet_plugin_forced_tool(libinput);
@ -390,6 +510,7 @@ libinput_plugin_system_load_internal_plugins(struct libinput *libinput,
libinput_tablet_plugin_proximity_timer(libinput);
libinput_tablet_plugin_eraser_button(libinput);
libinput_debounce_plugin(libinput);
libinput_mouse_plugin_wheel_lowres(libinput);
libinput_mouse_plugin_wheel(libinput);
/* Our own event dispatch is implemented as mini-plugin,
@ -398,6 +519,7 @@ libinput_plugin_system_load_internal_plugins(struct libinput *libinput,
* actually connected to anything yet */
libinput_evdev_dispatch_plugin(libinput);
}
void
libinput_plugin_system_destroy(struct libinput_plugin_system *system)
{
@ -433,6 +555,21 @@ libinput_plugin_system_notify_device_added(struct libinput_plugin_system *system
libinput_plugin_notify_device_added(plugin, device);
}
libinput_plugin_system_drop_unregistered_plugins(system);
/* Now that we added all our devices in all our plugins, notify
* all plugins about disabled features. Some plugins may get
* this notification twice but they should be able to handle that
* case.
*/
enum libinput_feature feature = _LIBINPUT_N_FEATURES;
while (--feature > 0) {
if (bitmask_bit_is_set(device->disabled_features, feature)) {
libinput_plugin_system_notify_device_feature_disabled(system,
device,
feature);
}
}
}
void
@ -470,6 +607,22 @@ libinput_plugin_system_notify_tablet_tool_configured(
libinput_plugin_system_drop_unregistered_plugins(system);
}
void
libinput_plugin_system_notify_device_feature_disabled(
struct libinput_plugin_system *system,
struct libinput_device *device,
enum libinput_feature feature)
{
libinput_device_disable_feature(device, feature);
struct libinput_plugin *plugin;
list_for_each_safe(plugin, &system->plugins, link) {
if (plugin->interface->feature_disabled)
plugin->interface->feature_disabled(plugin, device, feature);
}
libinput_plugin_system_drop_unregistered_plugins(system);
}
static void
libinput_plugin_process_frame(struct libinput_plugin *plugin,
struct libinput_device *device,
@ -504,7 +657,7 @@ print_frame(struct libinput *libinput, struct evdev_frame *frame, const char *pr
{
static uint32_t offset = 0;
static uint32_t last_time = 0;
uint32_t time = evdev_frame_get_time(frame) / 1000;
uint32_t time = usec_to_millis(evdev_frame_get_time(frame));
if (offset == 0) {
offset = time;
@ -517,9 +670,17 @@ print_frame(struct libinput *libinput, struct evdev_frame *frame, const char *pr
struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
for (size_t i = 0; i < nevents; i++) {
struct evdev_event *e = &events[i];
struct evdev_event e = events[i];
enum evdev_usage usage = evdev_usage_enum(e.usage);
switch (evdev_usage_enum(e->usage)) {
if ((usage > EVDEV_KEY_ESC && usage < EVDEV_KEY_CAPSLOCK) ||
(usage >= EVDEV_KEY_KP7 && usage <= EVDEV_KEY_KPDOT)) {
e.usage = evdev_usage_from(EVDEV_KEY_A);
} else if (usage == EVDEV_MSC_SCAN) {
e.value = 30; /* KEY_A scancode */
}
switch (evdev_usage_enum(e.usage)) {
case EVDEV_SYN_REPORT:
log_debug(
libinput,
@ -537,9 +698,9 @@ print_frame(struct libinput *libinput, struct evdev_frame *frame, const char *pr
prefix,
time / 1000,
time % 1000,
evdev_event_get_type_name(e),
evdev_event_get_code_name(e),
e->value);
evdev_event_get_type_name(&e),
evdev_event_get_code_name(&e),
e.value);
break;
default:
log_debug(libinput,
@ -547,14 +708,35 @@ print_frame(struct libinput *libinput, struct evdev_frame *frame, const char *pr
prefix,
time / 1000,
time % 1000,
evdev_event_get_type_name(e),
evdev_event_get_code_name(e),
e->value);
evdev_event_get_type_name(&e),
evdev_event_get_code_name(&e),
e.value);
break;
}
}
}
static bool
plugin_has_mask(struct libinput_plugin *plugin, struct evdev_frame *frame)
{
/* A plugin without a mask wants all events */
if (plugin->mask == NULL)
return true;
size_t nevents;
struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
/* nevents - 1 because we don't check the SYN_REPORT */
for (size_t i = 0; i < nevents - 1; i++) {
struct evdev_event *e = &events[i];
if (evdev_mask_is_set(plugin->mask, e->usage))
return true;
}
return false;
}
static void
plugin_system_notify_evdev_frame(struct libinput_plugin_system *system,
struct libinput_device *device,
@ -574,7 +756,7 @@ plugin_system_notify_evdev_frame(struct libinput_plugin_system *system,
struct list queued_events = LIST_INIT(queued_events);
list_take_insert(&queued_events, our_event, link);
uint64_t frame_time = evdev_frame_get_time(frame);
usec_t frame_time = evdev_frame_get_time(frame);
bool delay = !!sender_plugin;
@ -602,20 +784,22 @@ plugin_system_notify_evdev_frame(struct libinput_plugin_system *system,
list_for_each_safe(event, &queued_events, link) {
struct list next = LIST_INIT(next);
if (evdev_frame_get_time(event->frame) == 0)
if (usec_is_zero(evdev_frame_get_time(event->frame)))
evdev_frame_set_time(event->frame, frame_time);
if (!bitmask_bit_is_set(device->plugin_frame_callbacks,
plugin->index)) {
plugin->index) ||
!plugin_has_mask(plugin, event->frame)) {
list_remove(&event->link);
list_append(&next_events, &event->link);
continue;
}
#ifdef EVENT_DEBUGGING
_autofree_ char *prefix =
strdup_printf("plugin %-25s - %s:",
plugin->name,
libinput_device_get_name(event->device));
_autofree_ char *prefix = strdup_printf(
"%7s: plugin %-22s - ",
libinput_device_get_sysname(event->device),
plugin->name);
print_frame(libinput_device_get_context(device),
event->frame,
prefix);
@ -664,7 +848,7 @@ libinput_plugin_system_notify_evdev_frame(struct libinput_plugin_system *system,
}
static void
plugin_timer_func(uint64_t now, void *data)
plugin_timer_func(usec_t now, void *data)
{
struct libinput_plugin_timer *timer = data;
struct libinput_plugin *plugin = timer->plugin;
@ -698,7 +882,7 @@ struct libinput_plugin_timer *
libinput_plugin_timer_new(struct libinput_plugin *plugin,
const char *name,
void (*func)(struct libinput_plugin *plugin,
uint64_t now,
usec_t now,
void *data),
void *data)
{
@ -758,7 +942,7 @@ libinput_plugin_timer_unref(struct libinput_plugin_timer *timer)
/* Set timer expire time, in absolute us CLOCK_MONOTONIC */
void
libinput_plugin_timer_set(struct libinput_plugin_timer *timer, uint64_t expire)
libinput_plugin_timer_set(struct libinput_plugin_timer *timer, usec_t expire)
{
libinput_timer_set(&timer->timer, expire);
}

Some files were not shown because too many files have changed in this diff Show more