mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-03-21 14:30:36 +01:00
doc: add a description of the internal architecture
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
parent
b2fb2adefa
commit
108b85cae0
3 changed files with 284 additions and 0 deletions
282
doc/architecture.dox
Normal file
282
doc/architecture.dox
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
/**
|
||||
@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
|
||||
|
||||
*/
|
||||
|
|
@ -46,6 +46,7 @@ specific documentation.
|
|||
|
||||
- @subpage what_is_libinput
|
||||
- @subpage contributing
|
||||
- @subpage architecture
|
||||
- @subpage building_libinput
|
||||
- @subpage test-suite
|
||||
- @subpage tools
|
||||
|
|
|
|||
|
|
@ -291,6 +291,7 @@ if get_option('documentation')
|
|||
# written docs
|
||||
meson.source_root() + '/doc/absolute-axes.dox',
|
||||
meson.source_root() + '/doc/absolute-coordinate-ranges.dox',
|
||||
meson.source_root() + '/doc/architecture.dox',
|
||||
meson.source_root() + '/doc/building.dox',
|
||||
meson.source_root() + '/doc/button_debouncing.dox',
|
||||
meson.source_root() + '/doc/clickpad-softbuttons.dox',
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue