The idea here is that the client announces the interfaces it can support
and their version (including the core ei_connection). The server can
then send the various bits based on those versions, where applicable.
This replaces the connect/connect_done and version/get_version requests.
Immediately after connecting, the server sends an ei_protocol_setup
event to the client with the ID of the object and the server's highest
supported version number (of this object).
This is a one-shot object that the client can use to configure its name
and whether it is a sender or receiver context. Once .done is sent, the
object is discarded.
The server version is sent along to the client to allow for requests to
be added to this object in the future.
As a fixme left: the client now assumes to be connected as soon as the
.done request is sent and the following sync event is received. The
EIS implementation will not have actually eis_client_connect()ed the
client yet, but it's good enough for now.
Arguably, the CONNECTED event is superfluous anyway since *any* event
other than DISCONNECTED indicates connected status. CONNECTED is a
leftover from when the client created devices and needed to know if it's
worth doing so.
Directly copied from wayland. Note that while the wayland protocol
specifies the data is the last event serial in our case here it's just
0 since we don't have any event serials (yet).
The sync request is currently triggered after connection, merely to
ensure it works, it's not actually needed.
In the protocol this is a simple rename but in the implementation we can
now separate the protocol object out from the ei/ei-client context
itself by having the ei_connection objects.
This protocol is wayland-like though it uses a slightly different
message format. The XML file uses the same structure, except for the
"fixed" type which is "float" here.
The scanner uses a jinja template to generate source and header files
for ei and eis which are now used instead of the protobuf-generated
objects. Note that the scanner is a minimal working version, some
features like enum value checks are not yet implemented.
Unlike wayland we do not need to generate the libwayland-like library,
we only need the wire protocol parser - some shortcuts can thus be taken.
To keep the changes simple, the protocol currently is a flat protocol
with only one interface and all messages copied over from the previous
ei.proto file. In future commits, this will be moved to the respective
interfaces instead.
With the planned switch to a protocol supporting multiple interfaces
(a la wayland), a single version number is no longer useful. Remove this
API, we can add something more specific later if we need to.
This makes it easier to correlate a particular input transaction
(whether there are events or not) with out-of-band information like the
planned portal InputCapture::Activated signal's "activation-id".
The primary use-case for these properties in libei itself was to send
some fixed information (pid, cmdline and conection type). In the portal
case, these can be obtained out-of-band via the portal. In the
non-portal case these can be obtained from the socket itself (fetch pid,
look up /proc/pid/cmdline) which is just as reliable as trusting
whatever libei sends.
The only other use-case for the properties was the activation id in the
InputCapture::Activated portal signal. This can be achieved with a
serial in the START_EMULATING event.
libreis was intended for an intermediary to set some information that
the libei client cannot be entrusted with. In particular this was the
application name, the allowed capabilities, and some properties that -
once set - the client could no longer change (appid as probably the only
really useful one). The price for this was a rather complicated version
negotiation dance before the initial CONNECT request.
Now that we have a clear view of what's going to happen -
RemoteDesktop.ConnectToEIS and the InputCapture portal - there is no
longer any need for libreis. The extra information that libreis would've
sent is communicated out-of-band in both portals and are known to the
compositor at the time the connection is being established.
So we can simply drop this, it's no longer required and dropping it
makes the protocol significantly simpler anyway.
This is primarily for namespacing: where a portal sets some properties
it needs to do so *before* the Connect event. By moving this out to a
different namespace we can separate this easier, avoiding a portal
accidentally sending a property event after it has already passed the fd
to the client.
Fixes#23
The same socket is used for pre-connection configuration by a portal and
for the actual client that then uses the data. The portal and the client
may need different protocol versions *and* there may be different REIS
intermediaries.
So let's allow version negotiation for the configuration through
transactions: a REIS intermediary must start/finish a transaction with a
given version number.
This is only partially implemented in libreis right now: each API call
is wrapped in a transaction. Since we support version 1 only anyway,
there's no need to do anything but send our version down the wire. In
the future where we actually need to negotiate, libreis will need a
reis_dispatch() so we can wait for the server version to arrive, parse
it, etc. before sending ConfigureVersion down the wire. It's likely this
will never be needed.
Let the client set the version number it wants on Connect. There is new
public API to query the client/server's version as set once the connect
finished (eis_client_get_version() and ei_get_version()) but there is
currently no public API for the client to select the version it actually
wants, other than whatever both support. IOW, it's not possible for the
client to say "I want version 8 but if that's not supported, use version
5".
Add a new protocol message "GetVersion" and the matching reply from the
server with "Version" that can be sent at any time. The server always
replies with the highest protocol version it supports, allowing the
client to choose the protocol version it wants.
These two messages also have a fixed string to make the protocol easy to
identify in hexdumps.
To avoid roundtrips on connection, libeis immediately sends the Version
message. Ideally and by the time the client actually starts, that
version is already available and we can continue without requiring a
full roundtrip.
This patch only adds the version exchange with the server, it does not
yet add the bits for the client to actually set the version.
Punt this job to the caller, any structured logging handler doesn't need
them anyway and it makes handling of messages more awkward.
For our default log handlers (fprintf) we can just append them
ourselves.
Fixes#19
If a REIS implementation sends multiple ConfigureCapabilities messages,
the allowed capabilities would be whichever ones were sent last rather
than the intersection of all capablities.
Fallout from 7fc9498f1d.
There's a very limited use for configure messages after client
connection. The name is already static after connect anyway, and
the ability to drop capabilities after connect would just complicate the
EIS implementations unnecessary.
Currently only implemented for frame events, the vague plan for the
future is to merely queue the device events internally and "release"
them once a frame event was received, retrofitting the timestamp to the
C event struct (i.e. making ei_event_get_time() available on all device
events).
Meanwhile, the frame event it is.
With passive libei contexts receiving events sent by the EIS
implementation, the type of device changes significantly. While a
relative input device could still send data in logical pixels,
absolute devices may not have that luxury.
Best example here is an external tablet (think: Wacom Intuos): that
tablet has no built-in mapping to a screen and thus cannot capture input
events in logical pixels.
Address this by adding a device type, either virtual or physical.
In terms of functionality, the device's type decides:
- only virtual devices have regions
- only physical devices have a size
The event API remains as-is but the event data not represents either
logical pixels (virtual devices) or mm (physical device).
An EIS implementation connected to a passive libei context would likely
create:
- a virtual relative device (sending deltas in logical pixels)
- one or more physical absolute devices (sending deltas in mm)
Previously, a client could only bind to a capability immediately after
SEAT_ADDED and with a rather awkward API to confirm the capabilities.
Change this to allow for dynamic binding of capabilities, i.e. a client
calls ei_bind_capability() or ei_unbind_capability() whenever it feels
like, causing the respective devices of this capabilty to be added or
removed.
This allows for clients that are temporarily disinterested in a
capability but may require said capability later.
The default function takes one capability, a helper for enable/disable
multiple capabilities in one go is provided as well. On the protocol,
only the "bind" request exists which always represents the currently
wanted set of capabilities.
Note that the helper functions for multiple capabilities require NULL
(not zero) as sentinel, thanks to gcc.
This is a leftover from an earlier implementation that didn't get
removed in time. This extends to a macro that was using the context flag
(rather than the client flag) and in turn caused a bunch of false
positives on the tests.
A libei context can be initialized as active or passive context -
an "active" context sends events, a "passive" context receives events.
The EIS context supports both simultaneously, it is up to the
implementation to disconnect libei clients that it does not want to
suppport.
For example, the xdotool use-case creates an active libei context. The
EIS implementation controls and sets up the devices, but libei
sends the events.
In an input-capturing use-case, the EIS implementation controls
and sets up the devices **and** sends the events. libei is merely the
receiver for any event, it cannot send events. Thus this use-case
requires a passive libei context.
Most of this code is copy/paste with minor modifications - libei already
had the code to send events, libeis had the code to receive events, so
the vast majority of this patch is copying the code into the respective
other library, swap "ei" and "eis" and then apply the various minor
modifications needed to hook into the existing library.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
One macro that also defines the cleanup function, one macro that only
defines the unref. This is required for any place where we want to
use cleanup from multiple source files - like the test suite.
If we get EPROTO or EINVAL on the connection, all we do is disconnect
(ourselves or the client). So let's drain the fd so there's no other
data to be parsed by accident and then continue with actually
disconnecting.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
We will need eis_device_foo() for the actual event API soon, so let's
move the internal handlers out of the way by namespacing them.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Previous sequence reproducible with the eis-demo-server and
ei-demo-client:
- ctrl+c the client
- eis client dispatch fails with an rc < 0 due to the socket being closed
- eis calls eis_client_disconnect() which calls eis_seat_removed()
- that triggers an event for `SEAT_UNBIND`
- seat is set to REMOVE
- eis-demo-server gets the `SEAT_UNBIND` event and (correctly) calls
eis_seat_remove() since it doesn't know the client is disconnected
yet. That is due to the library unwinding the state transparently.
Fix this by splitting out the calls: eis_drop_seat() is the internally
used one that sets it to a new state of REMOVED_INTERNALLY, and then
eis_seat_remove merely updates the state.
Fixes#10
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
If we have libreis setting properties on the connection, this looks to
the server like they're coming from the ei connection. But libreis is a
different context than the libei context later, so when libei
initializes, it may set the same properties again. Since the libei
context doesn't know about the properties, it can't filter internally
and will send the properties to the server.
So we need to do all the permissions checks in the server to make sure
we don't overwrite values that we're not allowed to overwrite.
There are no real restrictions on changing properties from within the
eis implementation (other than not being able to set the reserved
namespaces).
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Pass the fd into the original context creation, then write any changes
to the wire immediately. For the capabilities that means we can't build
them up as before anymore, so change the API to have a vararg function
and require the allowed capabilities to be passed in.
There's likely little use for the previous allow-vs-deny policy etc, so
let's not make things more complicated an they have to be.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
There is data that libei and the EIS implementation will want to
exchange that is not covered by the immediate API.
To avoid having to add APIs for all of these, let's provide a generic
property API that both server and client can use to exchange this info.
The property API provides read/write/delete permissions but those only
apply to the client, not the server. The idea is that a server can
create (or restrict) properties that the client can read but not modify
and/or delete. A special-case are properties filled in automatically by
libei: ei.application.pid and ei.application.cmdline. These could be
used by e.g. the portal implementation to match permissions.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This allows us to transmit extra information about the client before the
server has to decide on whether it wants to connect us.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This effectively provides the EIS implementation with a notification
that the client will actually send events in the near future. To be used
by e.g. synergy-like clients when the pointer enters the logical screen
so that the EIS implementation can flash a warning or something.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This allows a client to trigger kinetic scrolling (or prevent it).
For compositors implementing EIS, the only realistic scroll source is
continuous which allows for scroll stop events. So let's give the client
the opportunity to trigger those on demand.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Already present in e.g. libinput and wayland, this event allows us to
group several events together to denote them as a logical group.
Required for multi-touch but as we've learned with Wayland it's also
required to group other events (scroll events in the case of Wayland).
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>