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>
Currently libei's valgrind test setup has 100x timeout (3000s), but the
standalone protocol-test-valgrind still has 30s timeout, which is not enough
for RISC-V hardware. On Milk-V Pioneer (SG2042) it takes ~70s to complete. In
addition, `kill_gently` sends SIGKILL before the process terminates and fails
the test.
Bump protocol-test-valgrind timeout to 300s and increase timeout between kill
signals to 3s to solve the issue.
Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/329>
Updates protocol and API documentation to specify that the modifiers
event should be sent by the EIS implementation every time the modifier
or group state changes, including when the change is triggered by key
events on the emulated keyboard.
The previous approach of expecting the client to track modifier state
using xkb_state_update_key() for injected keys resulted in multiple
opportunities for the client and server to get out of sync (both due to
unavoidable race conditions and due the client not having access to the
complete state used by the server to calculate state changes), with no
way for the client to ensure it had a correct modifier map short of
unbinding and rebinding the seat.
The new approach allows the client to track state solely by applying
modifiers events with xkb_state_update_mask(), simplifying client
implementation. Because the event is sent for all changes, the client
can use ei_connection.sync / ei_ping() to ensure that it has received
the latest state incorporating all key requests sent prior to the sync
request (along with any externally-caused modifier state changes that
may have occured up to the time the sync message was received).
Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/318>
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>
Identical to "ei: revamp the internal sync callback" but for libeis.
In prep work for exposing some of this to the caller, this adds a new
object that carries the our callbacks including the user data (if any).
This is an internal system only and is only used in the handshake
implementation where we don't have userdata anyway.
The new approach is: the callback has an object with a done() and
destroy() callback and the user data, done() is called when we receive
the message from the protocol, destroy() on destroy regardless whether
we got done() first.
This allows a caller to clean up user data even where the callback was
not triggered because we got disconnected first.
Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/316>
In prep work for exposing some of this to the caller, this adds a new
object that carries the our callbacks including the user data (if any).
This is an internal system only and is only used in the handshake
implementation where we don't have userdata anyway.
The new approach is: the callback has an object with a done() and
destroy() callback and the user data, done() is called when we receive
the message from the protocol, destroy() on destroy regardless whether
we got done() first.
This allows a caller to clean up user data even where the callback was
not triggered because we got disconnected first.
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>
When running repeated tests in parallel, this one eventually fails
because the ei.send() doesn't trigger the expected BrokenPipeError.
Use ei.dispatch() instaed and check whether the connection still exists.
Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/317>
Our automatic pong handler unconditionally sends a Pong back to the EIS
implementation. For some tests and depending on timing we may have
been disconnected already, resulting in a BrokenPipeError that fails
us the test. Ignore that error.
Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/317>
If our server disconnects us with data still in the pipe we never picked
up on that data causing heisenbugs depending on whether we read things
(esp. the "disconnected" event) fast enough or not.
Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/317>
This fixes a memleak caused by anything having a device ref when
ei_device_remove() is called. This can happen e.g. if there is a pending
event that hasn't been flushed with a ei_device.frame yet.
Example Sequence:
- client sends an event, event is queued to pending with a ref to the
device
- next event causes a client disconnect, queuing an emulated seat bind
of 0
- server calls eis_device_remove()
- client calls eis_device_unref()
One ref is still held in the pending event that was never visible to the
server, preventing the device from being freed.
Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/317>