mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2025-12-20 09:10:04 +01:00
282 lines
7.5 KiB
Text
282 lines
7.5 KiB
Text
/**
|
|
@page architecture libinput's internal architecture
|
|
|
|
This page provides an outline of libinput's internal architecture. The goal
|
|
here is to get the high-level picture across and point out the components
|
|
and their interplay to new developers.
|
|
|
|
The public facing API is in `libinput.c`, this file is thus the entry point
|
|
for almost all API calls. General device handling is in `evdev.c` with the
|
|
device-type-specific implementations in `evdev-<type>.c`. It is not
|
|
necessary to understand all of libinput to contribute a patch.
|
|
|
|
@ref architecture-contexts is the only user-visible implementation detail,
|
|
everything else is purely internal implementation and may change when
|
|
required.
|
|
|
|
@section architecture-contexts The udev and path contexts
|
|
|
|
The first building block is the "context" which can be one of
|
|
two types, "path" and "udev". See libinput_path_create_context() and
|
|
libinput_udev_create_context(). The path/udev specific bits are in
|
|
`path-seat.c` and `udev-seat.c`. This includes the functions that add new
|
|
devices to a context.
|
|
|
|
@dot
|
|
digraph context
|
|
{
|
|
compound=true;
|
|
rankdir="LR";
|
|
node [
|
|
shape="box";
|
|
]
|
|
|
|
libudev [label="libudev 'add' event"]
|
|
udev [label="libinput_udev_create_context()"];
|
|
udev_backend [label="udev-specific backend"];
|
|
context [label="libinput context"]
|
|
udev -> udev_backend;
|
|
libudev -> udev_backend;
|
|
udev_backend -> context;
|
|
}
|
|
@enddot
|
|
|
|
The udev context provides automatic device hotplugging as udev's "add"
|
|
events are handled directly by libinput. The path context requires that the
|
|
caller adds devices.
|
|
|
|
@dot
|
|
digraph context
|
|
{
|
|
compound=true;
|
|
rankdir="LR";
|
|
node [
|
|
shape="box";
|
|
]
|
|
|
|
path [label="libinput_path_create_context()"];
|
|
path_backend [label="path-specific backend"];
|
|
xdriver [label="libinput_path_add_device()"]
|
|
context [label="libinput context"]
|
|
path -> path_backend;
|
|
xdriver -> path_backend;
|
|
path_backend -> context;
|
|
}
|
|
@enddot
|
|
|
|
As a general rule: all Wayland compositors use a udev context, the X.org
|
|
stack uses a path context.
|
|
|
|
Which context was initialized only matters for creating/destroying a context
|
|
and adding devices. The device handling itself is the same for both types of
|
|
context.
|
|
|
|
@section architecture-device Device initialization
|
|
|
|
libinput only supports evdev devices, all the device initialization is done
|
|
in `evdev.c`. Much of the libinput public API is also a thin wrapper around
|
|
the matching implementation in the evdev device.
|
|
|
|
There is a 1:1 mapping between libinput devices and `/dev/input/eventX`
|
|
device nodes.
|
|
|
|
|
|
@dot
|
|
digraph context
|
|
{
|
|
compound=true;
|
|
rankdir="LR";
|
|
node [
|
|
shape="box";
|
|
]
|
|
|
|
devnode [label="/dev/input/event0"]
|
|
|
|
libudev [label="libudev 'add' event"]
|
|
xdriver [label="libinput_path_add_device()"]
|
|
context [label="libinput context"]
|
|
|
|
evdev [label="evdev_device_create()"]
|
|
|
|
devnode -> xdriver;
|
|
devnode -> libudev;
|
|
xdriver -> context;
|
|
libudev -> context;
|
|
|
|
context->evdev;
|
|
|
|
}
|
|
@enddot
|
|
|
|
Entry point for all devices is `evdev_device_create()`, this function
|
|
decides to create a `struct evdev_device` for the given device node.
|
|
Based on the udev tags (e.g. `ID_INPUT_TOUCHPAD`), a @ref
|
|
architecture-dispatch is initialized. All event handling is then in this
|
|
dispatch.
|
|
|
|
Rejection of devices and the application of quirks is generally handled in
|
|
`evdev.c` as well. Common functionality shared across multiple device types
|
|
(like button-scrolling) is also handled here.
|
|
|
|
@section architecture-dispatch Device-type specific event dispatch
|
|
|
|
Depending on the device type, `evdev_configure_device` creates the matching
|
|
`struct evdev_dispatch`. This dispatch interface contains the function
|
|
pointers to handle events. Four such dispatch methods are currently
|
|
implemented: touchpad, tablet, tablet pad, and the fallback dispatch which
|
|
handles mice, keyboards and touchscreens.
|
|
|
|
@dot
|
|
digraph context
|
|
{
|
|
compound=true;
|
|
rankdir="LR";
|
|
node [
|
|
shape="box";
|
|
]
|
|
|
|
evdev [label="evdev_device_create()"]
|
|
|
|
fallback [label="evdev-fallback.c"]
|
|
touchpad [label="evdev-mt-touchpad.c"]
|
|
tablet [label="evdev-tablet.c"]
|
|
pad [label="evdev-tablet-pad.c"]
|
|
|
|
evdev -> fallback;
|
|
evdev -> touchpad;
|
|
evdev -> tablet;
|
|
evdev -> pad;
|
|
|
|
}
|
|
@enddot
|
|
|
|
While `evdev.c` pulls the event out of libevdev, the actual handling of the
|
|
events is performed within the dispatch method.
|
|
|
|
@dot
|
|
digraph context
|
|
{
|
|
compound=true;
|
|
rankdir="LR";
|
|
node [
|
|
shape="box";
|
|
]
|
|
|
|
evdev [label="evdev_device_dispatch()"]
|
|
|
|
fallback [label="fallback_interface_process()"];
|
|
touchpad [label="tp_interface_process()"]
|
|
tablet [label="tablet_process()"]
|
|
pad [label="pad_process()"]
|
|
|
|
evdev -> fallback;
|
|
evdev -> touchpad;
|
|
evdev -> tablet;
|
|
evdev -> pad;
|
|
}
|
|
@enddot
|
|
|
|
The dispatch methods then look at the `struct input_event` and proceed to
|
|
update the state. Note: the serialized nature of the kernel evdev protocol
|
|
requires that the device updates the state with each event but to delay
|
|
processing until the `SYN_REPORT` event is received.
|
|
|
|
@section architecture-configuration Device configuration
|
|
|
|
All device-specific configuration is handled through `struct
|
|
libinput_device_config_FOO` instances. These are set up during device init
|
|
and provide the function pointers for the `get`, `set`, `get_default`
|
|
triplet of configuration queries (or more, where applicable).
|
|
|
|
For example, the `struct tablet_dispatch` for tablet devices has a
|
|
`struct libinput_device_config_accel`. This struct is set up with the
|
|
required function pointers to change the profiles.
|
|
|
|
@dot
|
|
digraph context
|
|
{
|
|
compound=true;
|
|
rankdir="LR";
|
|
node [
|
|
shape="box";
|
|
]
|
|
|
|
tablet [label="struct tablet_dispatch"]
|
|
config [label="struct libinput_device_config_accel"];
|
|
tablet_config [label="tablet_accel_config_set_profile()"];
|
|
tablet->config;
|
|
config->tablet_config;
|
|
}
|
|
@enddot
|
|
|
|
When the matching `libinput_device_config_set_FOO()` is called, this goes
|
|
through to the config struct and invokes the function there. Thus, it is
|
|
possible to have different configuration functions for a mouse vs a
|
|
touchpad, even though the interface is the same.
|
|
|
|
@dot
|
|
digraph context
|
|
{
|
|
compound=true;
|
|
rankdir="LR";
|
|
node [
|
|
shape="box";
|
|
]
|
|
|
|
libinput [label="libinput_device_config_accel_set_profile()"];
|
|
tablet_config [label="tablet_accel_config_set_profile()"];
|
|
libinput->tablet_config;
|
|
}
|
|
@enddot
|
|
|
|
@section architecture-filter Pointer acceleration filters
|
|
|
|
All pointer acceleration is handled in the `filter.c` file and its
|
|
associated files.
|
|
|
|
The `struct motion_filter` is initialized during device init, whenever
|
|
deltas are available they are passed to `filter_dispatch()`. This function
|
|
returns a set of @ref motion_normalization_customization "normalized coordinates".
|
|
|
|
All actual acceleration is handled within the filter, the device itself has
|
|
no further knowledge. Thus it is possible to have different acceleration
|
|
filters for the same device types (e.g. the Lenovo X230 touchpad has a
|
|
custom filter).
|
|
|
|
@dot
|
|
digraph context
|
|
{
|
|
compound=true;
|
|
rankdir="LR";
|
|
node [
|
|
shape="box";
|
|
]
|
|
|
|
fallback [label="fallback deltas"];
|
|
touchpad [label="touchpad deltas"];
|
|
tablet [label="tablet deltas"];
|
|
|
|
filter [label="filter_dispatch"];
|
|
|
|
fallback->filter;
|
|
touchpad->filter;
|
|
tablet->filter;
|
|
|
|
flat [label="accelerator_interface_flat()"];
|
|
x230 [label="accelerator_filter_x230()"];
|
|
pen [label="tablet_accelerator_filter_flat_pen()"];
|
|
|
|
filter->flat;
|
|
filter->x230;
|
|
filter->pen;
|
|
|
|
}
|
|
@enddot
|
|
|
|
Most filters convert the deltas (incl. timestamps) to a motion speed and
|
|
then apply a so-called profile function. This function returns a factor that
|
|
is then applied to the current delta, converting it into an accelerated
|
|
delta. See @ref pointer-acceleration for more details.
|
|
the current
|
|
|
|
*/
|