diff --git a/doc/architecture.dox b/doc/architecture.dox new file mode 100644 index 00000000..9abf15e5 --- /dev/null +++ b/doc/architecture.dox @@ -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-.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 + +*/ diff --git a/doc/page-hierarchy.dox b/doc/page-hierarchy.dox index 5d412536..b54d3adf 100644 --- a/doc/page-hierarchy.dox +++ b/doc/page-hierarchy.dox @@ -46,6 +46,7 @@ specific documentation. - @subpage what_is_libinput - @subpage contributing +- @subpage architecture - @subpage building_libinput - @subpage test-suite - @subpage tools diff --git a/meson.build b/meson.build index 8dff9e9d..66695537 100644 --- a/meson.build +++ b/meson.build @@ -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',