From c5aa2c0b609863f4290423efa5edd5f3a79dcc79 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 13 Mar 2023 11:53:56 +1000 Subject: [PATCH] doc/proto: address a few documentation issues A few clarifications as suggested by Benjamin Tissoires. --- doc/protocol/_index.md | 32 +++++++++++-- doc/protocol/doc/initial-handshake.md | 11 +++++ doc/protocol/doc/overview.md | 69 ++++++++++++++++----------- doc/protocol/doc/specification.md | 2 +- doc/protocol/doc/types.md | 25 +++++----- 5 files changed, 96 insertions(+), 43 deletions(-) diff --git a/doc/protocol/_index.md b/doc/protocol/_index.md index b1d1a1c..b186cb0 100644 --- a/doc/protocol/_index.md +++ b/doc/protocol/_index.md @@ -14,21 +14,32 @@ server side, typically a Wayland compositor, is called the **"EIS Implementation This documentation details the protocol to communicate between the client side and the EIS implementation. -A typical setup using the [C libraries]({{< ref "libraries" >}}) looks like this: +A typical Compositor setup using the `libei` and `libeis` [C libraries]({{< ref "libraries" >}}) looks like this: {{< mermaid >}} -graph LR; +flowchart LR; libwayland-server --> c1 libwayland-server --> c2 /dev/input/event0 ---> libinput - libeis-- ei protocol ---libei + /dev/input/event1 ---> libinput + libei -.-> libeis + libinput --> inputstack + inputstack --> libwayland-server + libeis -.-> inputstack + subgraph Kernel + /dev/input/event0 + /dev/input/event1 + end subgraph Wayland Compositor libwayland-server + inputstack[input stack] libinput libeis end - subgraph Wayland client A + subgraph EI client libei + end + subgraph Wayland client A c1[libwayland-client] end subgraph Wayland client B @@ -36,6 +47,19 @@ graph LR; end {{< /mermaid >}} +Note how the EI client is roughly equivalent to a physical input device coming +from the kernel and its events feed into the normal input stack. +However, the events are distinguishable inside the compositor to allow for +fine-grained access control on which events may be emulated and when emulation is +permitted. + +Events from the EIS implementation would usually feed into the input stack in the +same way as input events from physical devices. To Wayland clients, they are +indistinguishable from real devices. + +The EI client may be a Wayland client itself. + +## EI Protocol The ei protocol is a public protocol that may be used directly by clients or EIS implementations. This documentation describes the protocol, its interfaces diff --git a/doc/protocol/doc/initial-handshake.md b/doc/protocol/doc/initial-handshake.md index 56583b7..930b75b 100644 --- a/doc/protocol/doc/initial-handshake.md +++ b/doc/protocol/doc/initial-handshake.md @@ -59,10 +59,21 @@ sequenceDiagram Note over client, EIS: ei_handshake object is destroyed EIS-->>client: ei_connection.seat(new_id, version) + EIS-->>client: ei_seat.name(some seat) + EIS-->>client: ei_seat.capabilities() + EIS-->>client: ei_seat.done() + client->>EIS: ei_seat.bind() EIS-->>client: ei_seat.device(new_id, version) + EIS-->>client: ei_device.name(some name) + EIS-->>client: ei_device.device_type() + EIS-->>client: ei_device.capabilities(some name) EIS-->>client: ei_device.pointer(new_id, version) EIS-->>client: ei_device.keyboard(new_id, version) EIS-->>client: ei_seat.device(new_id, version) + EIS-->>client: ei_device.name(some name) + EIS-->>client: ei_device.device_type() + EIS-->>client: ei_device.capabilities(some name) + EIS-->>client: ei_device.region() EIS-->>client: ei_device.touchscreen(new_id, version) EIS-->>client: ei_device.resume() diff --git a/doc/protocol/doc/overview.md b/doc/protocol/doc/overview.md index 3b39d7c..fe94bdb 100644 --- a/doc/protocol/doc/overview.md +++ b/doc/protocol/doc/overview.md @@ -7,52 +7,67 @@ weight: 1 ## Protocol Components -The ei protocol has three components: **objects**, **requests** and -**events**. It is designed to be connect two processes over a UNIX socket - -an ei client and an EIS implementation (typically a Wayland compositor). The -protocol is asynchronous and object-based. +The protocol is designed to connect two processes over a UNIX socket - an ei +client and an EIS implementation (typically a Wayland compositor). -Whenever a message (request or event) is sent, that message carries an -identifier for the object. The type of an object is defined by its interface - -the interfaces are detailed here in this protocol. Thus, when a message for an -object arrives, the client or EIS implementation can invoke the corresponding -function on the object. +{{< mermaid >}} +flowchart LR; + subgraph EIS implementation + socket[[ei.socket]] + end + c1[ei client 1] -- ei protocol --> socket + c2[ei client 2] -- ei protocol --> socket +{{< /mermaid >}} -An object has exactly one interface but there may be multiple objects with -the same interface (e.g. multiple devices all use the ei_device interface). +The protocol is asynchronous and object-oriented. Each object on the wire supports +zero or more **requests** and zero or more **events**. Requests are messages +sent from an ei client to an EIS implementation, events are messages sent from +the EIS implementation to the client. -Requests are messages sent from an ei client to an EIS implementation, events -are messages sent from the EIS implementation to the client. This is the same -nomenclature that the Wayland protocol uses. +{{< mermaid >}} +flowchart LR; + ei -- request --> eis + eis -- event --> ei +{{< /mermaid >}} + +Objects are identified by a unique object ID, assigned at creation of the object. +The type of an object is defined by its [interface]({{< relref "interfaces" >}}) +and agreed on at object creation. Each object has exactly one interface, but +there may be multiple objects with that interface. For example, a compositor +may create multiple objects with the [`ei_device`]({{< relref "interfaces/ei_device" >}}) +interface. + +All data on the protocol (e.g. object IDs) is private to that client's +connection. The ei protocol is modelled closely after the Wayland protocol, but it is not binary compatible. ## Wire Format -The wire format consists of a 3-element header comprising the `sender-id` of +The wire format consists of a 3-element header comprising the `object-id` of the object, the length of the message and the opcode representing the message itself. ``` byte: |0 |4 |8 |12 |16 -content: |sender-id |length |opcode |... +content: |object-id |length |opcode |... ``` Where: -- `sender-id` is one 64-bit unsigned integer -- `length` and `opcode` are 32-bit unsigned integers -- all integers are in the EIS implementation's native byte order. -- `length` is the length of the message in bytes, including the 16 header bytes - for `sender-id`, `length` and `opcode`. -- `sender-id` is the id of the object sending the request/event. The sender-id +- `object-id` is one 64-bit unsigned integer that uniquely identifies + the object sending the request/event. The `object-id` 0 is reserved for the special [`ei_handshake`]({{< relref "interfaces/ei_handshake" >}}) object. -- `opcode` is the event or request-specific opcode, starting at 0. - requests and events have overlapping opcode ranges, i.e. the first request - and the first event both have opcode 0. +- `length` is a 32-bit integer that specifies the length of the message in + bytes, including the 16 header bytes for `object-id`, `length` and `opcode`. +- `opcode` is a 32-bit integer that specifies the event or request-specific + opcode, starting at 0. Requests and events have overlapping opcode ranges, + i.e. the first request and the first event both have opcode 0. -The header is followed by the message-specific arguments. All arguments are 4 bytes or -padded to a multiple of 4 bytes. +The header is followed by the message-specific arguments (if any). All +arguments are 4 bytes or padded to a multiple of 4 bytes. + +All integers are in the EIS implementation's native byte order. ## Version negotiation diff --git a/doc/protocol/doc/specification.md b/doc/protocol/doc/specification.md index 63055b9..63d1526 100644 --- a/doc/protocol/doc/specification.md +++ b/doc/protocol/doc/specification.md @@ -5,7 +5,7 @@ archetype: "chapter" weight: 3 --- -The current protocol specification is available [in xml format here](https://gitlab.freedesktop.org/libinput/libei/-/tree/main/proto/protocol.xml) and the corresponding [XML DTD here](https://gitlab.freedesktop.org/libinput/libei/-/tree/main/proto/protocol.dtd). +The current protocol specification is available [in XML format here](https://gitlab.freedesktop.org/libinput/libei/-/tree/main/proto/protocol.xml) and the corresponding [XML DTD here](https://gitlab.freedesktop.org/libinput/libei/-/tree/main/proto/protocol.dtd). In that protocol specification: diff --git a/doc/protocol/doc/types.md b/doc/protocol/doc/types.md index 296fbc7..a11f1ed 100644 --- a/doc/protocol/doc/types.md +++ b/doc/protocol/doc/types.md @@ -7,7 +7,7 @@ weight: 2 ## Protocol Types -The protocol types are described as used in the [protocol specification](https://gitlab.freedesktop.org/libinput/libei/-/tree/main/proto/protocol.xml) +The protocol types are described as used in the [protocol specification](https://gitlab.freedesktop.org/libinput/libei/-/tree/main/proto/protocol.xml). All types are encoded in the EIS implementation's native byte order. @@ -20,7 +20,7 @@ All types are encoded in the EIS implementation's native byte order. | int64 | 64 | signed integer | `int64_t` | | | float | 32 | IEEE-754 float | `float` | | | fd | 0 | file descriptor | `int` | see [^1] | -| string | 4 + N | length + string | `int`, `char *` | see [String Encoding]({{< ref "#string-encoding" >}}) | +| string | 32 + N| length + string | `int`, `char[]` | see [String Encoding]({{< ref "#string-encoding" >}}) | | new_id | 64 | object id allocated by the caller | `uint64_t` | see [Object IDs]({{< ref "#object-ids" >}}) | | object_id | 64 | previously allocated object id | `uint64_t` | see [Object IDs]({{< ref "#object-ids" >}}) | @@ -57,7 +57,7 @@ length field thus takes 12 bytes in total. Full (le) encoding: Object IDs are unique, monotonically increasing 64-bit integers that must not be re-used by the client or the EIS implementation. Object IDs created by the -client at 1 and must be less than `0xff00000000000000`. Object IDs allocated by +client start at 1 and must be less than `0xff00000000000000`. Object IDs allocated by the EIS implementation start at and must not be less than `0xff00000000000000`. Where a request or event specifies a `new_id` argument, it is the caller's responsibility @@ -73,17 +73,20 @@ and must not be re-used. Some requests and events, for example `ei_handshake.interface_version` require arguments that represent interface names. Interface names on the wire are always in the -form "`ei_foo`, i.e. lowercase snake_case spelling, as used in the [protocol specification](https://gitlab.freedesktop.org/libinput/libei/-/tree/main/proto/protocol.xml). +form "`ei_foo`", i.e. lowercase snake_case spelling, as used in the [protocol specification](https://gitlab.freedesktop.org/libinput/libei/-/tree/main/proto/protocol.xml). ### Serial numbers -Some events have serial numbers assigned by the EIS implementation. A serial is -a monotonically increasing unsigned 32-bit number (that wraps, clients must handle -this). Clients must use the most recently seen EIS serial number in requests that have -a last_serial argument. +Some events have a `serial` argument corresponding to a serial number assigned +by the EIS implementation. The serial number is a monotonically increasing +unsigned 32-bit number (that wraps, clients must handle this). Some requests +have a `last_serial` argument that clients must set to the most recently seen +EIS serial number. The serial number aims to help a client track failure cases and the EIS implementation to detect errors caused by the protocol's asynchronicity. -For example, if the EIS implementation removes a device at the serial number N, -client-initiated events for N-1 are caused by the client lagging behind and are not -a protocol violation. +For example, if the EIS implementation pauses a device at the serial number N and +subsequently receives an event with a `last_serial` of N-1, the EIS implementation +knows that the events were caused by the client lagging behind and are not +a protocol violation. The events can thus be discarded but the client does not +need to be disconnected.