1.. _architecture: 2 3============================================================================== 4libinput's internal architecture 5============================================================================== 6 7This page provides an outline of libinput's internal architecture. The goal 8here is to get the high-level picture across and point out the components 9and their interplay to new developers. 10 11The public facing API is in ``libinput.c``, this file is thus the entry point 12for almost all API calls. General device handling is in ``evdev.c`` with the 13device-type-specific implementations in ``evdev-<type>.c``. It is not 14necessary to understand all of libinput to contribute a patch. 15 16:ref:`architecture-contexts` is the only user-visible implementation detail, 17everything else is purely internal implementation and may change when 18required. 19 20.. _architecture-contexts: 21 22------------------------------------------------------------------------------ 23The udev and path contexts 24------------------------------------------------------------------------------ 25 26The first building block is the "context" which can be one of 27two types, "path" and "udev". See **libinput_path_create_context()** and 28**libinput_udev_create_context()**. The path/udev specific bits are in 29``path-seat.c`` and ``udev-seat.c``. This includes the functions that add new 30devices to a context. 31 32 33.. graphviz:: 34 35 36 digraph context 37 { 38 compound=true; 39 rankdir="LR"; 40 node [ 41 shape="box"; 42 ] 43 44 libudev [label="libudev 'add' event"] 45 udev [label="**libinput_udev_create_context()**"]; 46 udev_backend [label="udev-specific backend"]; 47 context [label="libinput context"] 48 udev -> udev_backend; 49 libudev -> udev_backend; 50 udev_backend -> context; 51 } 52 53 54The udev context provides automatic device hotplugging as udev's "add" 55events are handled directly by libinput. The path context requires that the 56caller adds devices. 57 58 59.. graphviz:: 60 61 62 digraph context 63 { 64 compound=true; 65 rankdir="LR"; 66 node [ 67 shape="box"; 68 ] 69 70 path [label="**libinput_path_create_context()**"]; 71 path_backend [label="path-specific backend"]; 72 xdriver [label="**libinput_path_add_device()**"] 73 context [label="libinput context"] 74 path -> path_backend; 75 xdriver -> path_backend; 76 path_backend -> context; 77 } 78 79 80As a general rule: all Wayland compositors use a udev context, the X.org 81stack uses a path context. 82 83Which context was initialized only matters for creating/destroying a context 84and adding devices. The device handling itself is the same for both types of 85context. 86 87.. _architecture-device: 88 89------------------------------------------------------------------------------ 90Device initialization 91------------------------------------------------------------------------------ 92 93libinput only supports evdev devices, all the device initialization is done 94in ``evdev.c``. Much of the libinput public API is also a thin wrapper around 95the matching implementation in the evdev device. 96 97There is a 1:1 mapping between libinput devices and ``/dev/input/eventX`` 98device nodes. 99 100 101 102.. graphviz:: 103 104 105 digraph context 106 { 107 compound=true; 108 rankdir="LR"; 109 node [ 110 shape="box"; 111 ] 112 113 devnode [label="/dev/input/event0"] 114 115 libudev [label="libudev 'add' event"] 116 xdriver [label="**libinput_path_add_device()**"] 117 context [label="libinput context"] 118 119 evdev [label="evdev_device_create()"] 120 121 devnode -> xdriver; 122 devnode -> libudev; 123 xdriver -> context; 124 libudev -> context; 125 126 context->evdev; 127 128 } 129 130 131Entry point for all devices is ``evdev_device_create()``, this function 132decides to create a ``struct evdev_device`` for the given device node. 133Based on the udev tags (e.g. ``ID_INPUT_TOUCHPAD``), a 134:ref:`architecture-dispatch` is initialized. All event handling is then in this 135dispatch. 136 137Rejection of devices and the application of quirks is generally handled in 138``evdev.c`` as well. Common functionality shared across multiple device types 139(like button-scrolling) is also handled here. 140 141.. _architecture-dispatch: 142 143------------------------------------------------------------------------------ 144Device-type specific event dispatch 145------------------------------------------------------------------------------ 146 147Depending on the device type, ``evdev_configure_device`` creates the matching 148``struct evdev_dispatch``. This dispatch interface contains the function 149pointers to handle events. Four such dispatch methods are currently 150implemented: touchpad, tablet, tablet pad, and the fallback dispatch which 151handles mice, keyboards and touchscreens. 152 153 154.. graphviz:: 155 156 157 digraph context 158 { 159 compound=true; 160 rankdir="LR"; 161 node [ 162 shape="box"; 163 ] 164 165 evdev [label="evdev_device_create()"] 166 167 fallback [label="evdev-fallback.c"] 168 touchpad [label="evdev-mt-touchpad.c"] 169 tablet [label="evdev-tablet.c"] 170 pad [label="evdev-tablet-pad.c"] 171 172 evdev -> fallback; 173 evdev -> touchpad; 174 evdev -> tablet; 175 evdev -> pad; 176 177 } 178 179 180While ``evdev.c`` pulls the event out of libevdev, the actual handling of the 181events is performed within the dispatch method. 182 183 184.. graphviz:: 185 186 187 digraph context 188 { 189 compound=true; 190 rankdir="LR"; 191 node [ 192 shape="box"; 193 ] 194 195 evdev [label="evdev_device_dispatch()"] 196 197 fallback [label="fallback_interface_process()"]; 198 touchpad [label="tp_interface_process()"] 199 tablet [label="tablet_process()"] 200 pad [label="pad_process()"] 201 202 evdev -> fallback; 203 evdev -> touchpad; 204 evdev -> tablet; 205 evdev -> pad; 206 } 207 208 209The dispatch methods then look at the ``struct input_event`` and proceed to 210update the state. Note: the serialized nature of the kernel evdev protocol 211requires that the device updates the state with each event but to delay 212processing until the ``SYN_REPORT`` event is received. 213 214.. _architecture-configuration: 215 216------------------------------------------------------------------------------ 217Device configuration 218------------------------------------------------------------------------------ 219 220All device-specific configuration is handled through ``struct 221libinput_device_config_FOO`` instances. These are set up during device init 222and provide the function pointers for the ``get``, ``set``, ``get_default`` 223triplet of configuration queries (or more, where applicable). 224 225For example, the ``struct tablet_dispatch`` for tablet devices has a 226``struct libinput_device_config_accel``. This struct is set up with the 227required function pointers to change the profiles. 228 229 230.. graphviz:: 231 232 233 digraph context 234 { 235 compound=true; 236 rankdir="LR"; 237 node [ 238 shape="box"; 239 ] 240 241 tablet [label="struct tablet_dispatch"] 242 config [label="struct libinput_device_config_accel"]; 243 tablet_config [label="tablet_accel_config_set_profile()"]; 244 tablet->config; 245 config->tablet_config; 246 } 247 248 249When the matching ``**libinput_device_config_set_FOO()**`` is called, this goes 250through to the config struct and invokes the function there. Thus, it is 251possible to have different configuration functions for a mouse vs a 252touchpad, even though the interface is the same. 253 254 255.. graphviz:: 256 257 258 digraph context 259 { 260 compound=true; 261 rankdir="LR"; 262 node [ 263 shape="box"; 264 ] 265 266 libinput [label="**libinput_device_config_accel_set_profile()**"]; 267 tablet_config [label="tablet_accel_config_set_profile()"]; 268 libinput->tablet_config; 269 } 270 271 272.. _architecture-filter: 273 274------------------------------------------------------------------------------ 275Pointer acceleration filters 276------------------------------------------------------------------------------ 277 278All pointer acceleration is handled in the ``filter.c`` file and its 279associated files. 280 281The ``struct motion_filter`` is initialized during device init, whenever 282deltas are available they are passed to ``filter_dispatch()``. This function 283returns a set of :ref:`normalized coordinates <motion_normalization_customization>`. 284 285All actual acceleration is handled within the filter, the device itself has 286no further knowledge. Thus it is possible to have different acceleration 287filters for the same device types (e.g. the Lenovo X230 touchpad has a 288custom filter). 289 290 291.. graphviz:: 292 293 294 digraph context 295 { 296 compound=true; 297 rankdir="LR"; 298 node [ 299 shape="box"; 300 ] 301 302 fallback [label="fallback deltas"]; 303 touchpad [label="touchpad deltas"]; 304 tablet [label="tablet deltas"]; 305 306 filter [label="filter_dispatch"]; 307 308 fallback->filter; 309 touchpad->filter; 310 tablet->filter; 311 312 flat [label="accelerator_interface_flat()"]; 313 x230 [label="accelerator_filter_x230()"]; 314 pen [label="tablet_accelerator_filter_flat_pen()"]; 315 316 filter->flat; 317 filter->x230; 318 filter->pen; 319 320 } 321 322 323Most filters convert the deltas (incl. timestamps) to a motion speed and 324then apply a so-called profile function. This function returns a factor that 325is then applied to the current delta, converting it into an accelerated 326delta. See :ref:`pointer-acceleration` for more details. 327the current 328