doc/proto: address a few documentation issues

A few clarifications as suggested by Benjamin Tissoires.
This commit is contained in:
Peter Hutterer 2023-03-13 11:53:56 +10:00
parent 150ac5b4ac
commit c5aa2c0b60
5 changed files with 96 additions and 43 deletions

View file

@ -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

View file

@ -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()

View file

@ -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

View file

@ -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:

View file

@ -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.