doc: add a description of the internal architecture
authorPeter Hutterer <peter.hutterer@who-t.net>
Mon, 16 Apr 2018 01:25:15 +0000 (11:25 +1000)
committerPeter Hutterer <peter.hutterer@who-t.net>
Mon, 16 Apr 2018 05:14:23 +0000 (15:14 +1000)
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
doc/architecture.dox [new file with mode: 0644]
doc/page-hierarchy.dox
meson.build

diff --git a/doc/architecture.dox b/doc/architecture.dox
new file mode 100644 (file)
index 0000000..9abf15e
--- /dev/null
@@ -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
+
+*/
index 5d41253..b54d3ad 100644 (file)
@@ -46,6 +46,7 @@ specific documentation.
 
 - @subpage what_is_libinput
 - @subpage contributing
+- @subpage architecture
 - @subpage building_libinput
 - @subpage test-suite
 - @subpage tools
index 8dff9e9..6669553 100644 (file)
@@ -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',