If a receiver client stops calling ei_dispatch for a while eventually
we fill up the send buffer, causing messages to be quietly dropped.
When the client resumes the message stream resumes with whatever we send
next but that can leave the client in an inconsistent state.
deskflow hit this in the server-side where our event sequence of pointer
motion+frames eventually filled up the buffer, causing
eis_device_stop_emulating() to be silently dropped. On the next
InputCapture sequence eis_device_start_emulating() was sent to an
already emulating client (as seen by the client).
This patch adds a secondary queue, if we fail to send a message with
EAGAIN queue it up and flush that queue whenever the next message is
sent. Meanwhile any newly added messages go straight into that queue.
The caveat here: a nonresponding client will eventually trigger OOM,
there is no upper limit on the messages yet
This is the libeis version of
commit 69e973e6b3 ("ei: queue unsent messages for later delivery if our buffer is full")
Closes: #79
Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/331>
This event is required to fix an issue with the current
ei_callback.done handling in libeis: previously we would imediately send
the ei_callback.done upon receiving ei_connection.sync from the client.
This results in incorrect behavior as we may have events in the queue
(and/or pending a frame) that the EIS implementation hasn't seen yet.
For example a client sending:
- ei_pointer.motion
- ei_connection.sync
- ei_device.frame
- ei_connection.sync
Will queue a motion + frame event on the EIS side during eis_dispatch()
but immediately receive done events for both sync requests.
This could be handled purely internally by keeping the sync event in the
queue but hidden to the caller - and automatically calling done when
it's that events turn, i.e. something like:
```
struct eis_event *eis_get_event(struct eis) {
struct eis_event *e = first_event(eis);
if (e == EIS_EVENT_SYNC) {
eis_callback_send_done(e);
eis_event_unref(e);
e = next_event(eis);
}
return e;
}
```
but that opens us up to a set of potential bugs detailed in
https://gitlab.freedesktop.org/libinput/libei/-/issues/71#note_2694603
So let's go the easy route by having a new event type that does nothing
other than eis_event_unref() in the EIS implementation. This way we can
queue the event and have everything behave in-order and as expected.
Closes#71
Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/316>
In the protocol it's a new request/event that is sent instead of the
touch up event.
In the library this is implemented as ei_touch_cancel() which
transparently sends cancel() or up depending on the EIS implementation
support. This is mirrored for the EIS implementation.
Where touch cancel is received as an event it is presented
as EI_EVENT_TOUCH_UP with an ei_event_touch_is_cancel() flag to
check if it was a cancel. This is required for backwards compatbility,
we cannot replace the TOUCH_UP event with a TOUCH_CANCEL event without
breaking existing callers. To add a new event type we would need clients
announcing support for those event types but that's an effort that's
better postponed until we have a stronger need for it (see #68).
Closes#60
Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/308>
This allows a caller to match up a region with other data, e.g. in the
remote desktop case the same mapping_id can be assigned to the pipewire
stream that represents that output.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This is the only request that creates a new object but doesn't specify
the version for that object, courtesy of copy/paste from the wayland
protocol. In libei/libeis this a bit was hidden away so it didn't get
noticed - but it was already buggy: libei would always hardcode to
version 1 but libeis would take whichever ei_callback version was agreed
upon during handshake. This version could be higher than 1.
This is a protocol break but we're still pre-1.0, there are very few
people that will be affected by this and it's better than having to
carry this bug around for years.
Fixes#35
A client that does ei_disconnect() sends out the 'disconnect' protocol
message before closing the fd. On the libeis side, this will cause a
Connection error when reading past that message. Logging that as error
looks bad when this is normal behavior.
The 'disconnect' protocol message changes the client into the
REQUEST_DISCONNECT state. If we get a transport error when the client is
in that state, don't log an error - the client wanted to be disconnected
anyway and even if it's a legitimate socket failure there isn't anything
we can do about it anyway.
This makes the debug logs easier to read - previously we'd get an error
and disconnect. That causes unwinding of the state internally
(including lots of debug messages) and then eventually the actual error
that caused the disconnect.
Reshuffle this, so we see the "Connection error" first before all the
unwiding of the state, making it easier to spot what caused the actual
error.
Leftover from 479bda259a (and possibly
others). This dates back to when a client could have restrictions
configured on the same fd. This is now all out-of-band (portals!) so the
compositor knows what the client is allowed to set up anyway.
No need for this (read-only) API here.
Same as the corresponding ei change a few commits ago, this one does all
the EIS renaming in the same manner.
As with the libei changes, an EIS implementation must now handle the
EIS_DEVICE_CAP_BUTTON and EI_DEVICE_CAP_SCROLL capabilities. In
virtually all cases, clients will likely expect that a device with the
pointer or absolute pointer capabilities will also have button and
scroll capabilities.
Split the ei_pointer protocol interface into ei_pointer,
ei_pointer_absolute, ei_scroll and ei_button.
This gets rid of the slightly awkward pointer vs pointer absolute
handling. Those were two different capabilities but tied to the same
interface.
Plus it paves the way for devices that are keyboards with scroll
buttons, etc.
This *should* have happened when the client got disconnected but in some
race conditions a seat may be added after the client gets disconnected.
Reproducible (sometimes) by test_invalid_object_id with the
eis-demo-server:
- client connects, sends invalid object ID, gets disconnected
- server sees CONNECTED, adds a seat, then sees DISCONNECTED and drops
the client.
From the demo-server's POV the seat is handled by the client, so it
expects the client to destroy it.
As the protocol spec says, EIS should treat this as already disconnected
and not touch the connection.
This fixes a memleak if a client connects and immediately disconnects -
when EIS processes the EIS_EVENT_CLIENT_CONNECT it may set up a bunch of
things like seats (the eis-demo-server does this). Then, later, when
the EIS_EVENTE_CLIENT_DISCONNECT is processed, it calls
eis_client_disconnect() but we were already in the disconnected state
and the seats would not get released.
These were previously (1 << cap) for convenience but that results in the
capability mask on the wire starting at 2 - which is a bit awkward.
Lets shift them down by one so we start the mask at 1.
Add a new simple object "brei_result" that maps to the protocol-type
reason + explanation. That object is now returned instead of the errno,
giving us better debugging options.
This changes the dispatcher functions from returning an int to returning
a brei_result instead (default NULL for success). A helper function for
converting a neg errno to a result is provided for convenience for now,
eventually all these paths should deal with things correctly.
Leave the ei_callback as-is and instead add ei_pingpong for the same
thing initiated by the server. The interface is otherwise identical but
for the direction it is supposed to flow.
This reduces the possibility of a client accidentally sending a
request when it is supposed to handle an event or vice versa.
This is effectively the same as connection.sync, but goes the other way.
This adds the ei_callback.done request.
In libeis this is (currently) enforced immediately after sending the
connection object. Not required there and makes the code a bit messier
but this way we can ensure that any client library handles that part of
the code.
This event is to notify the client that an object used in a request was
unknown. This allows the client to work around race conditions like
binding to a seat that was removed.
This is currently the server-side only which is probably enough for now.
The only client-side created objects we have are the callbacks.
This changes the initial connection negotiation to have the
ei_connection_setup as the pre-existing object id 0. Once the client has
sent all the data to set up the connection, the EIS implementation
replies with a new object ID that is the ei_connection protocol object,
i.e. the main object.
This allows for version negotiation of our main protocol object.
This allows us to have a device support multiple interfaces and evolve
those interfaces individually, e.g. add things to the keyboard interface
without having to bump the touchscreen interface.
Note that due to a name clash with the existing struct ei_touch public
API the protocol interface is named touchscreen.
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.