On the protocol level these are implemented as three separate interfaces
for swipe, pinch and hold, each interface has the begin/update/end
sequence and effectively matches the wayland pointer-gestures protocol.
Notably, only one of each gesture may be active at any time though the
protocol allows for separate gestures to be active (i.e. swipe while
pinching).
On the library side the gestures match the touch interface so the
sequence for a sender is:
swipe = ei_device_new_swipe(device, finger_count);
ei_swipe_begin(swipe);
ei_swipe_update(swipe, dx, dy);
ei_swipe_end(swipe);
with the corresponding APIs for pinch and hold.
On the receiver side the event types are separated for BEGIN/UPDATE/END
for all three gestures and thus match the libinput interface.
The notable difference however: there is only one CAP_GESTURES (similar
to libinput) and it is set if any gesture is available on the caller.
Creating a swipe gesture if the remote end does not support it will
return NULL though.
Co-Authored-by: Claude Code <noreply@anthropic.com>
Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/309>
The text capability allows for two types of events on that interface:
- XKB keysym events, e.g. XK_ssharp (0x00df) with a press/release state
- UTF8 strings
Keysym events are useful for scenarious where the hardware keycode is
unsuitable due to potentially different key mappings on the client and
server side and/or the client just not wanting to worry about those
mappings. For example a client may want to send Ctrl+C instead of
what effectively is now keycodes for what may or may not be a C key.
UTF8 strings take this a step further and provide a full string (with
implementation-defined size limits to avoid OOM). Unlike e.g. the
wayland text input protocols the assumption is here that the
interaction required to generate that string has already been
performed before the final string is sent over the wire.
Closes#73
Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/355>
clang-format taken from libinput, except for
ColumnLimit: 100
and some macro definitions (which don't all have an effect anyway...)
It's not perfect but good enough and at least consistent.
Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/383>
Add support for a client to request the creation of a new device
from the EIS implementation. This is necessary in situations where the
devices created by the EIS implementation are not (or no longer)
suitable for the client to function correctly.
The primary use-case of this is the upcoming tablet tool support where a
client may need to create multiple tablet tools in response to a new
physical tool brought into proximity locally.
Other use-cases include a client closing a device but requiring that
device (or one with similar capabilities) later.
The implementation in libei is straightforward
- on the client side we have a new function to request the new device:
ei_seat_request_device_with_capabilities()
- on the server side we have a new event EIS_EVENT_SEAT_DEVICE_REQUESTED
that can make use of the existing eis_event_seat_has_capability() API
Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/345>
This function is almost always wrong, very few clients will want to bind
to a single capability. Having this function means clients will use it to
bind caps one-by-one, causing the EIS implementation to create (an later
destroy) devices with capabilities that are about to be bound again anyway.
Better to have an API that encourages clients to bind all at once.
EIS implementation could avoid this by using a pingpong roundtrip in
response to a bind call, but removing this API is likely going to have
the same utility.
Now that the protocol interfaces are more fine-grained, let's match this
with the C API too.
This is just a rename of things so that in general
ei_pointer_*foo now becomes ei_foo*.
A few notable renames for better readability here:
- ei_device_scroll_delta (because scroll_scroll is awkward)
- ei_event_scroll_get_dx/dy and
ei_event_scroll_get_discrete_dx/dy to indicate the delta-ness
Beyond that, clients must ensure to check/bind to the new
EI_DEVICE_CAP_BUTTON and EI_DEVICE_CAP_SCROLL capabilities to be able
to send button or scroll events.
Note that this API now allows for an EIS implementation to send a device
that only has a button or a scroll cap. Or a pointer cap without
buttons, etc. It's up to the clients how to handle such devices
(probably: ignore them).
Previously we had ei_seat.capabilities and ei_device.capabilities,
both referring to the same enum. The seat caps were used to bind,
the device caps were used to announce capabilities.
The device caps were already mostly superfluous as the information
they carried was implicitly available by the set of interfaces
the device announced - if the device has a keyboard interface
it must also have the keyboard capability.
So let's drop the separate enum and make the capabilities
the set of supported interfaces. In the device we can drop the
event directly and just send the interface list. In the seat
we have a capability event that sends each *possible* interface
with a custom-assigned mask. The client can then use that mask
to bind to the capability as before.
For example:
<- ei_seat.capability(0x1, "ei_pointer")
<- ei_seat.capability(0x4, "ei_keyboard")
<- ei_seat.capability(0x8, "ei_touchscreen")
<- ei_seat.done()
-> ei_seat.bind(0x4 | 0x8) # bind to keyboard and touchscreen
<- ei_seat.device()
-> ei_device.interface("ei_keyboard")
-> ei_device.interface("ei_touchscreen")
<- ei_device.done()
In the generated bindings we simply use the interface index
to generate the masks, but the protocol at least states that
the mask may not be constant.
Because the button/scroll interfaces are not exposed by the C API, some
of the handling is a bit awkward since we need to use both depending
whether we have pointer/pointer_absolute selected.
Fixes#28
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
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.
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.
Our API requires a client to know which capability to pass into the
drop_capabilities function. This doesn't work for capabilities newer
than the client's version so they do not get disabled. The client will
thus receive devices it didn't ask for and doesn't know how to handle.
Let's invert the requirement and require the caller to confirm the
capabilities it wants - all others are dropped.
This is an API break but also requires updates of all clients, the
previous simple case of just calling ei_seat_bind() will now result in
zero capabilities.
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.
Once a device is removed, it is moved to the seat's devices_removed
list and the seat's ref is dropped - meaning the seat no longer keeps
the device alive. Once the caller's refcounts drop, the device can be
destroyed.
The device still keeps the seat alive though through a ref.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This changes the protocol so that it is the EIS implementation that
creates devices within a seat.
A client now "binds" to a seat and the EIS implementation creates
devices matching the requested capabilities. A client can close a device
if it no longer wants those but otherwise everything (including pointer
ranges) is handled by the server.
This is one giant patch because changes at the protocol level cannot
easily be broken out into smaller patches. Some FIXMEs are left which
will be handled in follow-up patches, e.g. the keymap handling is
basically broken right now.
Fixes#7
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This simplifies the handling of devices that were never added a bit, including
handling the refs between seat and device. And for legitimate use-cases
there's no reason why a caller would create a device but never add it.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Previously we didn't always clean up properly, especially where unexpected
removals happened. So the new approach is:
- the device always has a ref to the seat, we must not remove the seat until
even not-yet-added devices are released
- the seat has a ref to the device *after* it was added. this is a circular
ref so we need to make sure the device is manually removed from the seat
so we can actually reach a refcount 1
This is made slightly more complicated by us calling ei_disconnect() whenever
we fail to write on the fd. The result is re-entrant functions that we need to
protect against inadvertent changes. The best option here is probably to mark
the context as degraded and clean up once we finished whatever we were really
doing - that's a larger rework though.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Removing a seat could cause two device remove events to happen. Fix this by
splitting the removal up into two bits: removed by server and removed by
client. Only once both bits are set, remove the device.
This needs to happen in libei and libeis.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
To cut down on the boilerplate, an unref-able struct variable can now be
declared as
_unref_(type) *name = NULL;
which is the equivalent of
_cleanup_(type_unrefp) struct type *name = NULL;
Let's see how that style ends up reading.
This means we can get rid of the custom _cleanup_foo_ functions everywhere, no
need for all the extra #defines etc. A somewhat special case is systemd which
defines the various unrefp functions for us in the headers, so we can use them
directly.
OBJECT_IMPLEMENT_UNREF now also creates the unrefp function for this object -
this of course conflicts where DECLARE_UNREF_CLEANUP_FUNC is in scope. Not a
problem so far, let's see how we go.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
After CONNECT, the EIS implementation needs to add one or more seats. The
libei client can only create devices within those seats. This mirrors the
wayland hierarchy as well as the X.Org one.
The seat has a set of allowed capabilities, so the client knows ahead of time
when it may not be possible to create a specific device.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>