2020-07-14 14:41:32 +10:00
|
|
|
libei
|
|
|
|
|
=====
|
|
|
|
|
|
|
|
|
|
**libei** is a library for Emulated Input, primarily aimed at the Wayland
|
2020-08-12 14:09:22 +10:00
|
|
|
stack. It provides three parts:
|
|
|
|
|
- 🥚 EI (Emulated Input) for the client side (`libei`)
|
|
|
|
|
- 🍦 EIS (Emulated Input Server) for the server side (`libeis`)
|
|
|
|
|
- 🍚 REIS (Restrictions for the EIS) for the portal in between (`libreis`)
|
2020-07-14 14:41:32 +10:00
|
|
|
|
2020-08-12 14:09:22 +10:00
|
|
|
The communication between these parts is an implementation detail, neither
|
2020-07-14 14:41:32 +10:00
|
|
|
client nor server need to care about the details. Let's call it the BRidge
|
|
|
|
|
for EI, or 🥣 brei.
|
|
|
|
|
|
|
|
|
|
For the purpose of this document, **libei** refers to the project,
|
2020-08-12 14:09:22 +10:00
|
|
|
`libei`/`libeis`/`libreis` to the libraries provided.
|
2020-07-14 14:41:32 +10:00
|
|
|
|
|
|
|
|
In the Wayland stack, the EIS server component is part of the
|
|
|
|
|
compositor, the EI client component is part of the Wayland client.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
+--------------------+ +------------------+
|
|
|
|
|
| Wayland compositor |---wayland---| Wayland client B |
|
|
|
|
|
+--------------------+\ +------------------+
|
|
|
|
|
| libinput | libeis | \_wayland______
|
|
|
|
|
+----------+---------+ \
|
|
|
|
|
| | +-------+------------------+
|
|
|
|
|
/dev/input/ +---brei----| libei | Wayland client A |
|
|
|
|
|
+-------+------------------+
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The use-cases **libei** attempts to solve are:
|
|
|
|
|
- on-demand input device emulation, e.g. `xdotool` or more generically the
|
|
|
|
|
XTEST extension
|
|
|
|
|
- input forwarding, e.g. `synergy`
|
|
|
|
|
|
|
|
|
|
**libei** provides three benefits:
|
|
|
|
|
- separation
|
|
|
|
|
- distinction
|
|
|
|
|
- control
|
|
|
|
|
|
|
|
|
|
**libei** provides **separation** of emulated input from normal input.
|
|
|
|
|
Emulated input is a distinct channel for the compositor and can thus be
|
2020-07-29 20:21:27 +10:00
|
|
|
handled accordingly. For example, the compositor may show a warning sign in
|
|
|
|
|
the task bar while emulated input is active.
|
2020-07-14 14:41:32 +10:00
|
|
|
|
|
|
|
|
The second benefit is **distinction**. Each **libei** client has its own
|
|
|
|
|
input device set, the server is always aware of which client is requesting
|
|
|
|
|
input at any time. It is possible for the server to treat input from
|
|
|
|
|
different emulated input devices differently.
|
|
|
|
|
|
|
|
|
|
The server is in **control** of emulated input - it can filter input or
|
|
|
|
|
discard at will. For example, if the current focus window is a password
|
|
|
|
|
prompt, the server can simply discard any emulated input. If the screen is
|
2020-07-29 20:21:27 +10:00
|
|
|
locked, the server can suspend all emulated input devices.
|
2020-07-14 14:41:32 +10:00
|
|
|
|
2020-07-29 20:21:27 +10:00
|
|
|
Why not $foo?
|
|
|
|
|
-------------
|
|
|
|
|
|
|
|
|
|
We start from the baseline of: "there is no emulated input in Wayland (the
|
|
|
|
|
protocol)".
|
|
|
|
|
|
|
|
|
|
There is emulated input in X through XTEST but it provides neither
|
|
|
|
|
separation, distinction nor control in a useful manner. There are however
|
|
|
|
|
many X clients that require XTEST to work.
|
|
|
|
|
|
|
|
|
|
There are several suggestions that overlap with **libei**, with the main
|
|
|
|
|
proposals being:
|
|
|
|
|
- a Wayland protocol for virtual input
|
|
|
|
|
- a (compositor-specific) DBus interface for virtual input
|
|
|
|
|
|
|
|
|
|
Emulated input is not specifically Wayland-y. Clients that emulate input
|
|
|
|
|
generally don't care about Wayland itself. It's not needed to emulate
|
|
|
|
|
events on their own surfaces and Wayland does not provide global state. The
|
|
|
|
|
only connection to Wayland is merely that input events are *received*
|
|
|
|
|
through the Wayland protocol. So a Wayland protocol for emulating input is
|
|
|
|
|
not a great fit, it merely ticks the convenient box of "we already have IPC
|
|
|
|
|
through the wayland protocol, why not just do it there".
|
|
|
|
|
|
|
|
|
|
DBus is the most prevalent generic IPC channel on the Linux desktop but it's
|
|
|
|
|
not available in some compositors. Any other specific side-channel requires
|
|
|
|
|
an IPC mechanism to be implemented in the sender and receiver.
|
|
|
|
|
|
|
|
|
|
The current situation looks like that neither proposal will be universally
|
|
|
|
|
available. Wayland clients (including Xwayland) would need to support any
|
|
|
|
|
combination of methods.
|
|
|
|
|
|
|
|
|
|
**libei** side-steps this issue by making the *communication* itself a
|
|
|
|
|
an implementation detail and providing different *negotiation* backends.
|
|
|
|
|
A client can attempt to establish a **libei** context through a Flatpak
|
|
|
|
|
Portal first and all back onto a public DBus interface and fall back onto
|
|
|
|
|
e.g. a named UNIX socket. All with a few lines of code only. There is only
|
|
|
|
|
one spot the client has to care about this, the actual emulation of input is
|
|
|
|
|
identical regardless of backend.
|
|
|
|
|
|
2020-07-14 14:41:32 +10:00
|
|
|
High-level summary
|
|
|
|
|
------------------
|
|
|
|
|
|
|
|
|
|
A pseudo-code implementation for server and client are available in
|
2020-09-25 11:35:56 +10:00
|
|
|
the [`examples/`](https://gitlab.freedesktop.org/libinput/libei/-/tree/master/examples)
|
2020-07-14 14:41:32 +10:00
|
|
|
directory.
|
|
|
|
|
|
|
|
|
|
The server starts a `libeis` context (which can be integrated with flatpak
|
|
|
|
|
portals) and uses the `libeis` file descriptor to monitor for
|
|
|
|
|
client requests.
|
|
|
|
|
|
|
|
|
|
A client starts a `libei` context and connects to the server - either
|
|
|
|
|
directly, via DBus or via a portal. The server (or the portal) approves or
|
|
|
|
|
denies the client. After successful authentications the client can request
|
|
|
|
|
the creation of a device with capabilities `pointer`, `keyboard` or `touch`.
|
|
|
|
|
|
|
|
|
|
The client triggers input events on this device, the server receives those
|
|
|
|
|
as events through `libeis` and can forwards them as if they were libinput
|
|
|
|
|
events. The server has control of the client stream. If the stream is
|
|
|
|
|
paused, events from the client are discarded. If the stream is resumed, the
|
|
|
|
|
server will receive the events (but may discard them anyway depending on
|
|
|
|
|
local state).
|
|
|
|
|
|
|
|
|
|
The above caters for the `xdotool` use-case.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
For a `synergy` use-case, the setup requires:
|
2020-09-23 09:59:06 +10:00
|
|
|
- `synergy-client` on host A capturing mouse and keyboard events via an
|
|
|
|
|
unspecified protocol
|
2020-07-14 14:41:32 +10:00
|
|
|
- `synergy-server` on host B requesting a mouse/keyboard capability device
|
2020-09-23 09:59:06 +10:00
|
|
|
from the compositor
|
|
|
|
|
- when `synergy-client` receives events via from compositor A it
|
2020-07-14 14:41:32 +10:00
|
|
|
forwards those to the remote `synergy-server` which sends them via `libei`
|
|
|
|
|
to the compositor B.
|
|
|
|
|
|
2020-09-23 09:59:06 +10:00
|
|
|
**libei** does not provide a method for capturing events, see the
|
|
|
|
|
"Capability monitoring" section below.
|
2020-07-14 14:41:32 +10:00
|
|
|
|
2020-08-12 14:09:22 +10:00
|
|
|
Using REIS
|
|
|
|
|
----------
|
|
|
|
|
|
|
|
|
|
`libreis` is designed to allow a third-party that does not have a full
|
|
|
|
|
context to manage restrictions. This is aimed at portals that only have the
|
|
|
|
|
file descriptor to the EIS implementation but cannot initiate a full EI
|
|
|
|
|
context.
|
|
|
|
|
|
|
|
|
|
`libreis` works so that this third-party can configure the EIS
|
|
|
|
|
implementation to restrict the abilities of an EI client (later) connected
|
|
|
|
|
to this implementation. For example, a portal can set the client name
|
|
|
|
|
using `libreis` based on the app-id. A client cannot override this name
|
|
|
|
|
later.
|
|
|
|
|
|
2020-08-28 13:11:39 +10:00
|
|
|
Differences between XTest vs libei
|
|
|
|
|
----------------------------------
|
|
|
|
|
|
|
|
|
|
**libei** functionality is a superset of XTest's input emulation which
|
|
|
|
|
consists of a single request, `XTestFakeInput`. This request allows
|
|
|
|
|
emulation of button, key and motion events, including X Input 1.x events
|
|
|
|
|
(but not XI2). So **libei** can be a drop-in replacement since it supports
|
|
|
|
|
the same functionality and more.
|
|
|
|
|
|
|
|
|
|
However, XTest is an X protocol extension and users of XTest usually obtain
|
|
|
|
|
more information out-of-band ("out-of-band" here means "not through XTest
|
|
|
|
|
but instead other X protocol requests").
|
|
|
|
|
|
|
|
|
|
One example is `xdotool` which does window focus and modifier mangling (see
|
|
|
|
|
below). Window focus notification is not available to a pure **libei**
|
|
|
|
|
client and would have to be obtained or handled on a separate channel, e.g.
|
|
|
|
|
X or Wayland. Having said that, a Wayland client does not usually have acess
|
|
|
|
|
to query or modifiy the window focus.
|
|
|
|
|
|
|
|
|
|
Modifiers in `xdotool` are handled by obtaining the modifier mask from the X
|
|
|
|
|
server, identifying any difference to the intended mask and emulating key
|
|
|
|
|
events to change the modifier state to the intended one. For example, if
|
|
|
|
|
capslock is on, xdotool would send a capslock key event first (thus
|
|
|
|
|
disabling capslock) and then the actual key sequence. This is followed by
|
|
|
|
|
another capslock key event to restore the modifier mask.
|
|
|
|
|
|
|
|
|
|
This is not possible for a pure **libei** client as the modifier state is
|
|
|
|
|
maintained by the windowing system (if any). A client can obtain the
|
|
|
|
|
modifier state on Wayland on `wl_keyboard.enter` but when the client is
|
|
|
|
|
in-focus, there is rarely a need to emulate events.
|
|
|
|
|
|
|
|
|
|
Overall, it is best to think of **libei** devices as virtual equivalents to
|
|
|
|
|
a hardware device.
|
2020-07-14 14:41:32 +10:00
|
|
|
|
|
|
|
|
Open questions
|
|
|
|
|
--------------
|
|
|
|
|
|
|
|
|
|
### Flatpak integration
|
|
|
|
|
|
2020-08-11 20:54:01 +10:00
|
|
|
Where flatpak portals are in use, `libei` can communicate with
|
|
|
|
|
the portal through a custom backend. The above diagram modified for
|
|
|
|
|
Flatpak would be:
|
2020-07-14 14:41:32 +10:00
|
|
|
|
2020-08-11 20:54:01 +10:00
|
|
|
```
|
|
|
|
|
+--------------------+
|
|
|
|
|
| Wayland compositor |_
|
|
|
|
|
+--------------------+ \
|
|
|
|
|
| libinput | libeis | \_wayland______
|
|
|
|
|
+----------+---------+ \
|
|
|
|
|
| [eis-0.socket] \
|
|
|
|
|
/dev/input/ / \\ +-------+------------------+
|
|
|
|
|
| ======>| libei | Wayland client A |
|
|
|
|
|
| after +-------+------------------+
|
|
|
|
|
initial| handover /
|
|
|
|
|
connection| / initial request
|
|
|
|
|
| / dbus[org.freedesktop.portal.EmulatedInput]
|
|
|
|
|
+--------------------+
|
|
|
|
|
| xdg-desktop-portal |
|
|
|
|
|
+--------------------+
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The current approach works so that
|
|
|
|
|
- the compositor starts an `libeis` socket backend at `$XDG_RUNTIME_DIR/eis-0`
|
|
|
|
|
- `xdg-desktop-portal` provides `org.freedesktop.portal.EmulatedInput`
|
|
|
|
|
- a client connects to the `xdg-desktop-portal` to request emulated input
|
|
|
|
|
- `xdg-desktop-portal` authenticates a client and opens the initial
|
|
|
|
|
connection to the `libeis` socket. It restricts the capabilities available
|
|
|
|
|
on that socket (e.g. sets the client name based on `app-id` using
|
|
|
|
|
`libreis`).
|
|
|
|
|
- `xdg-desktop-portal` hands over the file descriptor to the client which
|
|
|
|
|
can initialize a `libei` context
|
|
|
|
|
- from then on, `libei` and `libeis` talk directly to each other, the portal
|
|
|
|
|
has no further influence.
|
|
|
|
|
|
|
|
|
|
This describes the **current** implementation. Changes to this approach are
|
|
|
|
|
likely, e.g. the portal **may** control suspending/resuming devices (in addition to the
|
2020-07-14 14:41:32 +10:00
|
|
|
server). The UI for this is not yet sorted.
|
|
|
|
|
|
|
|
|
|
### Authentication
|
|
|
|
|
|
|
|
|
|
Sandboxing is addressed via flatpak portals but a further level is likely
|
|
|
|
|
desirable, esp. outside flatpak. The simplest solution is the client
|
|
|
|
|
announcing the name so the UI can be adjusted accordingly. API wise-maybe an
|
|
|
|
|
opaque key/value system so the exact auth can be left to the implementation.
|
|
|
|
|
|
2020-09-23 09:59:06 +10:00
|
|
|
### Capability monitoring
|
|
|
|
|
|
|
|
|
|
For the use-case of fowarding input (e.g. `synergy`) we need a method of
|
|
|
|
|
capturing input as well as forwarding input. An initial idea was for
|
|
|
|
|
**libei** to provide capability monitoring, i.e. a client requests all
|
|
|
|
|
events from a specific capability. As with input emulation same benefits
|
|
|
|
|
would apply - input can only be forwarded if the compositor explicitly does so.
|
2020-07-14 14:41:32 +10:00
|
|
|
|
2020-09-23 09:59:06 +10:00
|
|
|
However, this fails in the details. For example, for `synergy` we need
|
|
|
|
|
capability monitoring started by triggers, e.g. the client requests a
|
|
|
|
|
pointer capability monitoring when the real pointer hits
|
2020-07-14 14:41:32 +10:00
|
|
|
the screen edge. Or in response to a keyboard shortcut.
|
2020-09-23 09:59:06 +10:00
|
|
|
|
|
|
|
|
Some of the capabilities are distinctively display server-specific, for
|
|
|
|
|
example the concept of a seat and a device is different between X and
|
|
|
|
|
Wayland.
|
2020-07-14 14:41:32 +10:00
|
|
|
|
|
|
|
|
### Keyboard layouts
|
|
|
|
|
|
2020-07-16 12:54:02 +10:00
|
|
|
The emulated input may require a specific keyboard layout, for example
|
|
|
|
|
for softtokens (usually: constant layout "us") or for the `synergy` case
|
|
|
|
|
where the remote keyboard should have the same keymap as the local one, even
|
|
|
|
|
where the remote host is configured otherwise.
|
|
|
|
|
|
|
|
|
|
libei provides keymap negotation: the client can pick a keymap, the server
|
|
|
|
|
can accept it, refuse it, or override it with its own. In the latter two
|
|
|
|
|
cases it is up to the client to handle the result.
|
|
|
|
|
|
|
|
|
|
Modifier state handling, group handling, etc. is still a private
|
|
|
|
|
implementation so even where the server supports individual keymaps. So it
|
|
|
|
|
remains to be seen if this approach is sufficient.
|
2020-07-31 14:02:33 +10:00
|
|
|
|
|
|
|
|
### Xwayland and XTEST
|
|
|
|
|
|
|
|
|
|
There are PoC implementations of using `libei` within Xwayland and
|
|
|
|
|
connecting it to a `libeis` context in the compositor (PoC with Weston).
|
|
|
|
|
This allows Xwayland to intercept XTEST events and route those through
|
|
|
|
|
the compositor instead.
|
|
|
|
|
|
2020-08-25 14:30:17 +10:00
|
|
|
```
|
|
|
|
|
+--------------------+ +------------------+
|
|
|
|
|
| Wayland compositor |---wayland---| Wayland client B |
|
|
|
|
|
+--------------------+\ +------------------+
|
|
|
|
|
| libinput | libeis | \_wayland______
|
|
|
|
|
+----------+---------+ \
|
|
|
|
|
| | +-------+------------------+
|
|
|
|
|
/dev/input/ +---brei----| libei | XWayland |
|
|
|
|
|
+-------+------------------+
|
|
|
|
|
|
|
|
|
|
|
| XTEST
|
|
|
|
|
|
|
|
|
|
|
+-----------+
|
|
|
|
|
| X client |
|
|
|
|
|
+-----------+
|
|
|
|
|
```
|
|
|
|
|
Of course, XWayland is just another Wayland compositor, so the connection
|
|
|
|
|
between libei and libeis could be handled through a portal.
|
|
|
|
|
|
2020-07-31 14:02:33 +10:00
|
|
|
### Short-lived applications
|
|
|
|
|
|
2020-08-12 14:15:35 +10:00
|
|
|
**libei** is not designed for short-lived fire-and-forget-type applications
|
|
|
|
|
like `xdotool`. It provides context and device negotiation between the
|
2020-07-31 14:02:33 +10:00
|
|
|
server and the client - the latter must be able to adjust to limitations the
|
|
|
|
|
server imposes.
|
|
|
|
|
|
2020-08-12 14:15:35 +10:00
|
|
|
The current implemtation of the protocol does not allow for a `libei` client
|
|
|
|
|
to send all requests in bulk and exit. The decision on whether to accept a
|
|
|
|
|
device is ultimately made by the caller implementation and
|
|
|
|
|
non-deterministic. For **libei** to support a batch request, *someone* would
|
|
|
|
|
have to wait. It cannot be the server as the exact requirements are unknown: do
|
|
|
|
|
we pause processing on the client altogether? We may miss a disconnect
|
|
|
|
|
event? Do we pause processing for one device only? But then we may be
|
|
|
|
|
re-ordering input events and cause havoc.
|
|
|
|
|
|
|
|
|
|
It could be `libei` itself to implement these event queues but this too can
|
|
|
|
|
mess with the input order. And implementing an event queue is not hard, so
|
|
|
|
|
this issue is punted to the caller instead. XWayland in its current
|
|
|
|
|
implementation already does this.
|
2020-08-27 11:45:15 +10:00
|
|
|
|
|
|
|
|
### uinput vs libei
|
|
|
|
|
|
|
|
|
|
uinput is a Linux kernel module that allows creating
|
|
|
|
|
`/dev/input/event`-compatible devices. Unlike XTest it is independent of a
|
|
|
|
|
windowing system but requires write access to `/dev/uinput`, usually limited
|
|
|
|
|
to root. uinput devices are effectively identical to physical devices and
|
|
|
|
|
will thus work on the tty and in any windowing system.
|
|
|
|
|
|
|
|
|
|
From the POV of a ``libei`` client, uinput is a server implementation
|
|
|
|
|
detail. The client does not need to know that the manager employs uinput to
|
|
|
|
|
create the devices, it merely connects to an available EIS instance.
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
+---------+
|
|
|
|
|
| server |___auth channel__________
|
|
|
|
|
+---------+ \
|
|
|
|
|
| libeis |- +---------------------+
|
|
|
|
|
+---------+ \____brei___| libei | application |
|
|
|
|
|
| +---------------------+
|
|
|
|
|
/dev/uinput/
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Note that the server would likely need some authentication channel
|
|
|
|
|
to verify which client is allowed to emulate input devices at the
|
|
|
|
|
kernel level (polkit? config files?). This however is out of scope for **libei**.
|
|
|
|
|
An example uinput server is implemented in the `eis-server-demo` in the
|
|
|
|
|
with libei repository.
|