diff --git a/proto/protocol.xml b/proto/protocol.xml
index c4a02fa..64a481d 100644
--- a/proto/protocol.xml
+++ b/proto/protocol.xml
@@ -458,22 +458,17 @@
Bind to the bitmask of capabilities given. The bitmask is zero or more of the
- capabilities provided in the ei_seat.capabilities event. That event's capabilities
- are used as a mask of capabilities that can be bound.
+ masks representing an interface as provided in the ei_seat.capability event.
+ See the ei_seat.capability event documentation for examples.
- Note that available seat capabilities are defined in the ei_device
- interface and not dependent on the seat interface version. In other
- words, ei_seat.bind supports any capability available in the
- client-announced ei_device interface version.
-
- Binding capabilities that are not supported in the ei_device's interface version
+ Binding masks that are not supported in the ei_device's interface version
is a client bug and may result in disconnection.
A client may send this request multiple times to adjust the capabilities it
- is interested in. If capabilities are dropped, the EIS implementation may
- ei_device.remove devices that have these capabilities.
+ is interested in. If previously-bound capabilities are dropped by the client,
+ the EIS implementation may ei_device.remove devices that have these capabilities.
-
+
@@ -500,31 +495,38 @@
-
+
- A bitmask of the capabilities of this seat. A client may ei_seat.bind to these
- capabilies and an EIS implementation may then create device based on the bound
- capabilities.
+ A notification that this seat supports devices with the given interface.
+ The interface is mapped to a bitmask by the EIS implementation.
+ A client may then binary OR these bitmasks in ei_seat.bind.
+ In response, the EIS implementation may then create device based on those
+ bound capabilities.
- Note that available seat capabilities are defined in the ei_device
- interface and not dependent on the seat interface version. In other
- words, ei_seat.bind supports any capability available in the
- client-announced ei_device interface version.
+ For example, an EIS implementation may map "ei_pointer" to 0x1,
+ "ei_keyboard" to 0x4 and "ei_touchscreen" to 0x8. A client may then
+ ei_seat.bind(0xc) to bind to keyboard and touchscreen but not pointer.
+ Note that as shown in this example the set of masks may be sparse.
+ The value of the mask is contant for the lifetime of the seat but may differ
+ between seats.
Note that seat capabilities only represent a mask of possible capabilities on
devices in this seat. A capability that is not available on the seat cannot
ever be available on any device in this seat. For example, a seat that only has the
pointer and keyboard capabilities can never have a device with the touchscreen
- capability.
+ capability. It is up to the EIS implementation to decide how many (if any) devices
+ with any given capability exist in this seat.
- It is up to the EIS implementation to decide how many (if any) devices with any given
- capability exist in this seat.
+ Only interfaces that the client announced during ei_handshake.interface_version
+ can be a seat capability.
- Capabilities are only advertised once and are constant for the lifetime of the seat.
+ This event is sent multiple times - once per supported interface.
+ The set of capabilities is constant for the lifetime of the seat.
It is a protocol violation to send this event after the ei_seat.done event.
-
+
+
@@ -669,39 +671,6 @@
-
-
- A set of capabilities available on this device.
-
-
-
-
-
-
-
-
-
-
-
- A bitmask of the capabilities of this device. This mask is a subset of the
- ei_seat.capabilities announced for the seat for this device.
-
- Capabilities are only advertised once and are constant for the lifetime of the device.
-
- For clients of ei_handshake.context_type.sender, the capabilities limit
- the type of emulated input events a client can request. For example, a device
- without the keyboard capability cannot emulate keyboard events.
-
- For clients of ei_handshake.context_type.receiver, the capabilities limit
- the type of emulated input events a client may receive from the EIS implementation.
- For example, a device without the keyboard capability will ever generate keyboard events.
-
- This event must be is sent once immediately after object creation.
- It is a protocol violation to send this event after the ei_device.done event.
-
-
-
-
If the device type is ei_device.device_type.virtual, the device is a
diff --git a/src/ei-proto.h.tmpl b/src/ei-proto.h.tmpl
index d00378b..be58796 100644
--- a/src/ei-proto.h.tmpl
+++ b/src/ei-proto.h.tmpl
@@ -55,6 +55,19 @@ extern const struct brei_interface {{interface.name}}_proto_interface;
#define {{interface.name.upper()}}_INTERFACE_NAME "{{interface.protocol_name}}"
{% endfor %}
+__attribute__((unused))
+static const char *{{component.upper()}}_INTERFACE_NAMES[] = {
+ {% for interface in interfaces %}
+ {{interface.name.upper()}}_INTERFACE_NAME,
+ {% endfor %}
+};
+
+/* Interface indices as used in the protocol.xml file */
+{% for interface in interfaces %}
+#define {{interface.name.upper()}}_INTERFACE_INDEX {{loop.index0}}
+{% endfor %}
+#define {{component.upper()}}_INTERFACE_COUNT {{interfaces|length}}
+
{% for interface in interfaces %}
/** {{interface.name}} **/
diff --git a/src/libei-device.c b/src/libei-device.c
index 817db3e..ab5b933 100644
--- a/src/libei-device.c
+++ b/src/libei-device.c
@@ -163,17 +163,6 @@ handle_msg_name(struct ei_device *device, const char *name)
return NULL;
}
-static struct brei_result *
-handle_msg_capabilities(struct ei_device *device, uint32_t caps)
-{
- if (device->capabilities != 0)
- return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
- "EIS sent the device capabilities twice");
-
- ei_device_set_capabilities(device, caps);
- return NULL;
-}
-
static struct brei_result *
handle_msg_device_type(struct ei_device *device, enum ei_device_type type)
{
@@ -232,6 +221,23 @@ handle_msg_done(struct ei_device *device)
}
+ if (device->pointer)
+ mask_add(device->capabilities, EI_DEVICE_CAP_POINTER);
+ if (device->pointer_absolute)
+ mask_add(device->capabilities, EI_DEVICE_CAP_POINTER_ABSOLUTE);
+
+ /* button/scroll-only defaults to normal pointer cap */
+ if ((device->scroll || device->button) &&
+ (!device->pointer_absolute)) {
+ mask_add(device->capabilities, EI_DEVICE_CAP_POINTER);
+ }
+
+ if (device->keyboard)
+ mask_add(device->capabilities, EI_DEVICE_CAP_KEYBOARD);
+
+ if (device->touchscreen)
+ mask_add(device->capabilities, EI_DEVICE_CAP_TOUCH);
+
if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) &&
!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE) &&
!ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD) &&
@@ -438,7 +444,6 @@ handle_msg_interface(struct ei_device *device, object_id_t id, const char *name,
static const struct ei_device_interface interface = {
.destroyed = handle_msg_destroy,
.name = handle_msg_name,
- .capabilities = handle_msg_capabilities,
.device_type = handle_msg_device_type,
.dimensions = handle_msg_dimensions,
.region = handle_msg_region,
@@ -1112,13 +1117,6 @@ ei_device_set_name(struct ei_device *device, const char *name)
device->name = xstrdup(name);
}
-void
-ei_device_set_capabilities(struct ei_device *device,
- uint32_t capabilities)
-{
- device->capabilities = capabilities;
-}
-
_public_ bool
ei_device_has_capability(struct ei_device *device,
enum ei_device_capability cap)
diff --git a/src/libei-device.h b/src/libei-device.h
index 3604d75..1d5587f 100644
--- a/src/libei-device.h
+++ b/src/libei-device.h
@@ -124,7 +124,6 @@ OBJECT_DECLARE_GETTER(ei_device, touchscreen_interface, const struct ei_touchscr
OBJECT_DECLARE_SETTER(ei_device, type, enum ei_device_type);
OBJECT_DECLARE_SETTER(ei_device, name, const char*);
OBJECT_DECLARE_SETTER(ei_device, seat, const char*);
-OBJECT_DECLARE_SETTER(ei_device, capabilities, uint32_t);
struct ei_device *
ei_device_new(struct ei_seat *seat, object_id_t deviceid, uint32_t version);
diff --git a/src/libei-seat.c b/src/libei-seat.c
index 432651c..562c7e6 100644
--- a/src/libei-seat.c
+++ b/src/libei-seat.c
@@ -95,14 +95,26 @@ handle_msg_name(struct ei_seat *seat, const char * name)
}
static struct brei_result *
-handle_msg_capabilities(struct ei_seat *seat, uint32_t capabilities)
+handle_msg_capability(struct ei_seat *seat, uint64_t mask, const char *interface)
{
- if (seat->capabilities != 0)
- return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
- "EIS sent the seat capabilities twice");
+ struct ei *ei = ei_seat_get_context(seat);
- seat->capabilities = capabilities;
- return 0;
+ assert(ARRAY_LENGTH(EI_INTERFACE_NAMES) == ARRAY_LENGTH(seat->capabilities.map));
+
+ for (size_t i = 0; i < ARRAY_LENGTH(EI_INTERFACE_NAMES); i++) {
+ if (streq(EI_INTERFACE_NAMES[i], interface)) {
+ if (seat->capabilities.map[i] != 0)
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "EIS sent the seat capabilities for %s twice", interface);
+ log_debug(ei, "seat %#" PRIx64" has cap %s as %#" PRIx64, ei_seat_get_id(seat), interface, mask);
+ seat->capabilities.map[i] = mask;
+ return 0;
+ }
+ }
+
+ /* EIS must not send anything we didn't announce as supported */
+ return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL,
+ "EIS sent an unsupported interface %s", interface);
}
static struct brei_result *
@@ -111,7 +123,7 @@ handle_msg_done(struct ei_seat *seat)
struct ei *ei = ei_seat_get_context(seat);
seat->state = EI_SEAT_STATE_DONE;
- log_debug(ei, "Added seat '%s' with caps %#x", seat->name, seat->capabilities);
+ log_debug(ei, "Added seat '%s'", seat->name);
ei_queue_seat_added_event(seat);
@@ -145,7 +157,7 @@ handle_msg_device(struct ei_seat *seat, object_id_t id, uint32_t version)
static const struct ei_seat_interface interface = {
.destroyed = handle_msg_destroyed,
.name = handle_msg_name,
- .capabilities = handle_msg_capabilities,
+ .capability = handle_msg_capability,
.done = handle_msg_done,
.device = handle_msg_device,
};
@@ -167,7 +179,7 @@ ei_seat_new(struct ei *ei, object_id_t id, uint32_t version)
ei_register_object(ei, &seat->proto_object);
seat->state = EI_SEAT_STATE_NEW;
- seat->capabilities_bound = 0;
+ seat->capabilities.bound = 0;
list_init(&seat->devices);
list_init(&seat->devices_removed);
@@ -219,10 +231,16 @@ ei_seat_has_capability(struct ei_seat *seat,
{
switch (cap) {
case EI_DEVICE_CAP_POINTER:
+ /* FIXME: a seat without pointer or pointer_absolute but button
+ * and/or scroll should count as pointer here but that's niche
+ * enough that we can figure that out when needed */
+ return seat->capabilities.map[EI_POINTER_INTERFACE_INDEX] != 0;
case EI_DEVICE_CAP_POINTER_ABSOLUTE:
+ return seat->capabilities.map[EI_POINTER_ABSOLUTE_INTERFACE_INDEX] != 0;
case EI_DEVICE_CAP_KEYBOARD:
+ return seat->capabilities.map[EI_KEYBOARD_INTERFACE_INDEX] != 0;
case EI_DEVICE_CAP_TOUCH:
- return mask_all(seat->capabilities, cap);
+ return seat->capabilities.map[EI_TOUCHSCREEN_INTERFACE_INDEX] != 0;
}
return false;
}
@@ -233,22 +251,8 @@ ei_seat_bind_capability(struct ei_seat *seat, enum ei_device_capability cap)
ei_seat_bind_capabilities(seat, cap, NULL);
}
-static inline bool
-is_known_cap(enum ei_device_capability cap)
-{
- switch (cap) {
- case EI_DEVICE_CAP_POINTER_ABSOLUTE:
- case EI_DEVICE_CAP_POINTER:
- case EI_DEVICE_CAP_KEYBOARD:
- case EI_DEVICE_CAP_TOUCH:
- return true;
- }
-
- return false;
-}
-
static int
-ei_seat_send_bind(struct ei_seat *seat, uint32_t capabilities)
+ei_seat_send_bind(struct ei_seat *seat, uint64_t capabilities)
{
struct ei *ei = ei_seat_get_context(seat);
@@ -261,6 +265,39 @@ ei_seat_send_bind(struct ei_seat *seat, uint32_t capabilities)
return rc;
}
+static uint64_t
+ei_seat_cap_mask(struct ei_seat *seat, enum ei_device_capability cap)
+{
+ switch (cap) {
+ case EI_DEVICE_CAP_POINTER_ABSOLUTE:
+ return seat->capabilities.map[EI_POINTER_ABSOLUTE_INTERFACE_INDEX] |
+ seat->capabilities.map[EI_SCROLL_INTERFACE_INDEX] |
+ seat->capabilities.map[EI_BUTTON_INTERFACE_INDEX];
+ case EI_DEVICE_CAP_POINTER:
+ return seat->capabilities.map[EI_POINTER_INTERFACE_INDEX] |
+ seat->capabilities.map[EI_SCROLL_INTERFACE_INDEX] |
+ seat->capabilities.map[EI_BUTTON_INTERFACE_INDEX];
+ case EI_DEVICE_CAP_KEYBOARD:
+ return seat->capabilities.map[EI_KEYBOARD_INTERFACE_INDEX];
+ case EI_DEVICE_CAP_TOUCH:
+ return seat->capabilities.map[EI_TOUCHSCREEN_INTERFACE_INDEX];
+ }
+
+ return 0;
+}
+
+static uint64_t
+ei_seat_pointer_cap_mask(struct ei_seat *seat)
+{
+ return seat->capabilities.map[EI_POINTER_INTERFACE_INDEX];
+}
+
+static uint64_t
+ei_seat_pointer_absolute_cap_mask(struct ei_seat *seat)
+{
+ return seat->capabilities.map[EI_POINTER_ABSOLUTE_INTERFACE_INDEX];
+}
+
_public_ void
ei_seat_bind_capabilities(struct ei_seat *seat, ...)
{
@@ -272,25 +309,20 @@ ei_seat_bind_capabilities(struct ei_seat *seat, ...)
return;
}
- uint32_t mask = seat->capabilities_bound;
+ uint64_t mask = seat->capabilities.bound;
enum ei_device_capability cap;
va_list args;
va_start(args, seat);
while ((cap = va_arg(args, enum ei_device_capability)) > 0) {
- if (!is_known_cap(cap))
- continue;
-
- mask_add(mask, cap);
+ mask_add(mask,ei_seat_cap_mask(seat, cap));
}
- mask &= seat->capabilities;
-
- if (seat->capabilities_bound == mask)
+ if (seat->capabilities.bound == mask)
return;
- seat->capabilities_bound = mask;
- ei_seat_send_bind(seat, seat->capabilities_bound);
+ seat->capabilities.bound = mask;
+ ei_seat_send_bind(seat, seat->capabilities.bound);
}
_public_ void
@@ -311,22 +343,30 @@ ei_seat_unbind_capabilities(struct ei_seat *seat, ...)
return;
}
- uint32_t old_mask = seat->capabilities_bound;
+ uint64_t mask = seat->capabilities.bound;
enum ei_device_capability cap;
va_list args;
va_start(args, seat);
while ((cap = va_arg(args, enum ei_device_capability)) > 0) {
- if (!is_known_cap(cap))
- continue;
-
- mask_remove(seat->capabilities_bound, cap);
+ mask_remove(mask, ei_seat_cap_mask(seat, cap));
}
- if (seat->capabilities_bound == old_mask)
+ /* This is a bit frustrating because scroll/button aren't visible
+ * to the libei C API: if we remove POINTER but *not* POINTER_ABSOLUTE
+ * or vice versa we don't want to remove button/scroll either but the
+ * above code does just that. So here we re-add it if either is present.
+ */
+ if (mask & ei_seat_pointer_cap_mask(seat))
+ mask_add(mask, ei_seat_cap_mask(seat, EI_DEVICE_CAP_POINTER));
+ if (mask & ei_seat_pointer_absolute_cap_mask(seat))
+ mask_add(mask, ei_seat_cap_mask(seat, EI_DEVICE_CAP_POINTER_ABSOLUTE));
+
+ if (seat->capabilities.bound == mask)
return;
- if (seat->capabilities_bound == 0) {
+ seat->capabilities.bound = mask;
+ if (seat->capabilities.bound == 0) {
struct ei_device *device;
list_for_each(device, &seat->devices, link) {
if (ei_device_has_capability(device, cap))
@@ -334,5 +374,5 @@ ei_seat_unbind_capabilities(struct ei_seat *seat, ...)
}
}
- ei_seat_send_bind(seat, seat->capabilities_bound);
+ ei_seat_send_bind(seat, seat->capabilities.bound);
}
diff --git a/src/libei-seat.h b/src/libei-seat.h
index 1f7a21a..43835d4 100644
--- a/src/libei-seat.h
+++ b/src/libei-seat.h
@@ -28,6 +28,7 @@
#include "util-list.h"
#include "brei-shared.h"
+#include "ei-proto.h"
struct ei;
@@ -47,8 +48,14 @@ struct ei_seat {
enum ei_seat_state state;
struct list devices;
struct list devices_removed; /* removed from seat but client still has a ref */
- uint32_t capabilities;
- uint32_t capabilities_bound;
+
+ struct {
+ /* Maps the interface as index to the capability bitmask on the protocol */
+ uint64_t map[EI_INTERFACE_COUNT];
+ /* A bitmask of the bitmask the client bound last */
+ uint64_t bound;
+ } capabilities;
+
char *name;
};
diff --git a/src/libeis-device.c b/src/libeis-device.c
index c4bd43c..a5d6b48 100644
--- a/src/libeis-device.c
+++ b/src/libeis-device.c
@@ -34,10 +34,6 @@
#include "libeis-private.h"
#include "eis-proto.h"
-static_assert((int)EIS_DEVICE_CAP_POINTER == EIS_DEVICE_CAPABILITIES_POINTER, "ABI mismatch");
-static_assert((int)EIS_DEVICE_CAP_POINTER_ABSOLUTE == EIS_DEVICE_CAPABILITIES_POINTER_ABSOLUTE, "ABI mismatch");
-static_assert((int)EIS_DEVICE_CAP_KEYBOARD == EIS_DEVICE_CAPABILITIES_KEYBOARD, "ABI mismatch");
-static_assert((int)EIS_DEVICE_CAP_TOUCH == EIS_DEVICE_CAPABILITIES_TOUCHSCREEN, "ABI mismatch");
static_assert((int)EIS_DEVICE_TYPE_VIRTUAL == EIS_DEVICE_DEVICE_TYPE_VIRTUAL, "ABI mismatch");
static_assert((int)EIS_DEVICE_TYPE_PHYSICAL == EIS_DEVICE_DEVICE_TYPE_PHYSICAL, "ABI mismatch");
@@ -801,10 +797,6 @@ eis_device_add(struct eis_device *device)
if (rc < 0)
goto out;
- rc = eis_device_event_capabilities(device, device->capabilities);
- if (rc < 0)
- goto out;
-
rc = eis_device_event_device_type(device, device->type);
if (rc < 0)
goto out;
diff --git a/src/libeis-seat.c b/src/libeis-seat.c
index d14e94d..df10093 100644
--- a/src/libeis-seat.c
+++ b/src/libeis-seat.c
@@ -87,24 +87,28 @@ client_msg_release(struct eis_seat *seat)
}
static struct brei_result *
-client_msg_bind(struct eis_seat *seat, uint32_t caps)
+client_msg_bind(struct eis_seat *seat, uint64_t caps)
{
- uint32_t allowed_caps = 0;
+ uint32_t capabilities = 0;
- if (seat->proto_object.version >= EIS_DEVICE_CAPABILITIES_POINTER_SINCE_VERSION)
- allowed_caps |= EIS_DEVICE_CAPABILITIES_POINTER;
- if (seat->proto_object.version >= EIS_DEVICE_CAPABILITIES_POINTER_ABSOLUTE_SINCE_VERSION)
- allowed_caps |= EIS_DEVICE_CAPABILITIES_POINTER_ABSOLUTE;
- if (seat->proto_object.version >= EIS_DEVICE_CAPABILITIES_KEYBOARD_SINCE_VERSION)
- allowed_caps |= EIS_DEVICE_CAPABILITIES_KEYBOARD;
- if (seat->proto_object.version >= EIS_DEVICE_CAPABILITIES_TOUCHSCREEN_SINCE_VERSION)
- allowed_caps |= EIS_DEVICE_CAPABILITIES_TOUCHSCREEN;
-
- if (caps & ~allowed_caps)
+ if (caps & ~seat->capabilities.proto_mask)
return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_VALUE,
- "Invalid capabilities %#x", caps);
+ "Invalid capabilities %#" PRIx64, caps);
- eis_seat_bind(seat, caps);
+ /* Convert from protocol capabilities to our C API capabilities */
+ if (caps & bit(EIS_POINTER_INTERFACE_INDEX))
+ capabilities |= EIS_DEVICE_CAP_POINTER;
+ if (caps & bit(EIS_POINTER_ABSOLUTE_INTERFACE_INDEX))
+ capabilities |= EIS_DEVICE_CAP_POINTER_ABSOLUTE;
+ if (caps & bit(EIS_KEYBOARD_INTERFACE_INDEX))
+ capabilities |= EIS_DEVICE_CAP_KEYBOARD;
+ if (caps & bit(EIS_TOUCHSCREEN_INTERFACE_INDEX))
+ capabilities |= EIS_DEVICE_CAP_TOUCH;
+
+ /* Note: a client binding to button/scroll only
+ ends up with libeis capabilities 0. */
+
+ eis_seat_bind(seat, capabilities);
return NULL;
}
@@ -162,7 +166,52 @@ eis_seat_add(struct eis_seat *seat)
eis_client_register_object(client, &seat->proto_object);
eis_client_add_seat(client, seat);
eis_seat_event_name(seat, seat->name);
- eis_seat_event_capabilities(seat, seat->capabilities_mask);
+
+ if (seat->capabilities.c_mask & EIS_DEVICE_CAP_POINTER &&
+ client->interface_versions.ei_pointer > 0) {
+ uint64_t mask = bit(EIS_POINTER_INTERFACE_INDEX);
+ eis_seat_event_capability(seat, mask,
+ EIS_POINTER_INTERFACE_NAME);
+ mask_add(seat->capabilities.proto_mask, mask);
+ }
+
+ if (seat->capabilities.c_mask & EIS_DEVICE_CAP_POINTER_ABSOLUTE &&
+ client->interface_versions.ei_pointer_absolute > 0) {
+ uint64_t mask = bit(EIS_POINTER_ABSOLUTE_INTERFACE_INDEX);
+ eis_seat_event_capability(seat, mask,
+ EIS_POINTER_ABSOLUTE_INTERFACE_NAME);
+ mask_add(seat->capabilities.proto_mask, mask);
+ }
+
+ if (seat->capabilities.c_mask & (EIS_DEVICE_CAP_POINTER|EIS_DEVICE_CAP_POINTER_ABSOLUTE) &&
+ (client->interface_versions.ei_pointer > 0 || client->interface_versions.ei_pointer_absolute > 0)) {
+ uint64_t mask = bit(EIS_SCROLL_INTERFACE_INDEX);
+ eis_seat_event_capability(seat, mask,
+ EIS_SCROLL_INTERFACE_NAME);
+ mask_add(seat->capabilities.proto_mask, mask);
+
+ mask = bit(EIS_BUTTON_INTERFACE_INDEX);
+ eis_seat_event_capability(seat, mask,
+ EIS_BUTTON_INTERFACE_NAME);
+ mask_add(seat->capabilities.proto_mask, mask);
+ }
+
+ if (seat->capabilities.c_mask & EIS_DEVICE_CAP_KEYBOARD &&
+ client->interface_versions.ei_keyboard > 0) {
+ uint64_t mask = bit(EIS_KEYBOARD_INTERFACE_INDEX);
+ eis_seat_event_capability(seat, mask,
+ EIS_KEYBOARD_INTERFACE_NAME);
+ mask_add(seat->capabilities.proto_mask, mask);
+ }
+
+ if (seat->capabilities.c_mask & EIS_DEVICE_CAP_TOUCH &&
+ client->interface_versions.ei_touchscreen > 0) {
+ uint64_t mask = bit(EIS_TOUCHSCREEN_INTERFACE_INDEX);
+ eis_seat_event_capability(seat, mask,
+ EIS_TOUCHSCREEN_INTERFACE_NAME);
+ mask_add(seat->capabilities.proto_mask, mask);
+ }
+
eis_seat_event_done(seat);
}
@@ -184,7 +233,7 @@ eis_seat_bind(struct eis_seat *seat, uint32_t caps)
return;
}
- caps &= seat->capabilities_mask;
+ caps &= seat->capabilities.c_mask;
seat->state = EIS_SEAT_STATE_BOUND;
eis_queue_seat_bind_event(seat, caps);
@@ -250,7 +299,7 @@ eis_seat_configure_capability(struct eis_seat *seat,
case EIS_DEVICE_CAP_POINTER_ABSOLUTE:
case EIS_DEVICE_CAP_KEYBOARD:
case EIS_DEVICE_CAP_TOUCH:
- mask_add(seat->capabilities_mask, cap);
+ mask_add(seat->capabilities.c_mask, cap);
break;
}
}
@@ -264,7 +313,7 @@ eis_seat_has_capability(struct eis_seat *seat,
case EIS_DEVICE_CAP_POINTER_ABSOLUTE:
case EIS_DEVICE_CAP_KEYBOARD:
case EIS_DEVICE_CAP_TOUCH:
- return mask_all(seat->capabilities_mask, cap);
+ return mask_all(seat->capabilities.c_mask, cap);
}
return false;
}
diff --git a/src/libeis-seat.h b/src/libeis-seat.h
index a08d520..8b64d57 100644
--- a/src/libeis-seat.h
+++ b/src/libeis-seat.h
@@ -48,7 +48,10 @@ struct eis_seat {
enum eis_seat_state state;
char *name;
- uint32_t capabilities_mask;
+ struct {
+ uint32_t c_mask; /* this is the C API bitmask */
+ uint64_t proto_mask; /* the protocol mask */
+ } capabilities;
struct list devices;
};
diff --git a/test/eiproto.py.tmpl b/test/eiproto.py.tmpl
index bcc65ab..77e84ce 100644
--- a/test/eiproto.py.tmpl
+++ b/test/eiproto.py.tmpl
@@ -147,7 +147,7 @@ class InterfaceName(StrEnum):
{% endfor %}
-@attr.s
+@attr.s(eq=False)
class Interface:
object_id: int = attr.ib(repr=lambda id: f"{id:#x}")
version: int = attr.ib()
diff --git a/test/test_protocol.py b/test/test_protocol.py
index 346645f..3d38865 100644
--- a/test/test_protocol.py
+++ b/test/test_protocol.py
@@ -20,6 +20,7 @@
#
# Will run that test against whatever is providing that socket.
+from functools import reduce
from typing import Generator, Optional
from pathlib import Path
@@ -44,7 +45,6 @@ try:
EiCallback,
EiConnection,
EiHandshake,
- EiDevice,
EiSeat,
)
except ModuleNotFoundError as e:
@@ -191,6 +191,28 @@ class Ei:
return True
+ def seat_fill_capability_masks(self, seat: EiSeat):
+ """
+ Set up the seat to fill the interface masks for each Capability
+ and add the bind_mask() helper function to compile a mask
+ from interface names.
+ """
+
+ def seat_cap(seat, mask, intf_name):
+ seat._interface_masks[intf_name] = mask
+
+ seat._interface_masks = {}
+ seat.connect("Capability", seat_cap)
+
+ def bind_mask(interfaces: list[InterfaceName]) -> int:
+ return reduce(
+ lambda mask, v: mask | v,
+ [seat._interface_masks[i] for i in interfaces],
+ 0,
+ )
+
+ seat.bind_mask = bind_mask
+
def recv(self) -> bytes:
try:
data = self.sock.recv(1024)
@@ -257,7 +279,10 @@ class Ei:
elif isinstance(interface, EiSeat):
assert interface not in ei.seats
- ei.seats.append(interface)
+
+ seat = interface
+ ei.seat_fill_capability_masks(seat)
+ ei.seats.append(seat)
def unregister_cb(interface: Interface) -> None:
if interface == ei.connection:
@@ -486,6 +511,9 @@ class TestEiProtocol:
eis.terminate()
+ @pytest.mark.skipif(
+ not getattr(int, "bit_count", None), reason="int.bit_count() required"
+ )
def test_connect_receive_seat(self, eis):
"""
Ensure we get a seat object after setting our connection
@@ -502,6 +530,10 @@ class TestEiProtocol:
InterfaceName.EI_CONNECTION,
InterfaceName.EI_CALLBACK,
InterfaceName.EI_PINGPONG,
+ InterfaceName.EI_POINTER,
+ InterfaceName.EI_POINTER_ABSOLUTE,
+ InterfaceName.EI_KEYBOARD,
+ InterfaceName.EI_TOUCHSCREEN,
]:
ei.send(
setup.InterfaceVersion(interface, VERSION_V(1))
@@ -521,22 +553,29 @@ class TestEiProtocol:
for seat in ei.seats:
assert seat.version == 1 # we have 100, but the server only has 1
for call in seat.calllog:
- if call.name == "Capabilities":
- assert call.args["capabilities"] != 0
- if self.using_demo_server:
- caps = call.args["capabilities"]
- assert (
- caps
- == EiDevice.EiCapabilities.POINTER
- | EiDevice.EiCapabilities.POINTER_ABSOLUTE
- | EiDevice.EiCapabilities.KEYBOARD
- | EiDevice.EiCapabilities.TOUCHSCREEN
+ if call.name == "Capability":
+ assert call.args["mask"].bit_count() == 1
+ assert InterfaceName(call.args["interface"])
+
+ if self.using_demo_server:
+ all_caps = [
+ call.args["interface"]
+ for call in seat.calllog
+ if call.name == "Capability"
+ ]
+ assert sorted(all_caps) == sorted(
+ [
+ i.value
+ for i in (
+ InterfaceName.EI_POINTER,
+ InterfaceName.EI_POINTER_ABSOLUTE,
+ InterfaceName.EI_BUTTON,
+ InterfaceName.EI_SCROLL,
+ InterfaceName.EI_KEYBOARD,
+ InterfaceName.EI_TOUCHSCREEN,
)
- break
- else:
- assert (
- False
- ), f"Expected ei_seat.capabilities, but got none in {seat.calllog}"
+ ]
+ )
for call in seat.calllog:
if call.name == "Name":
@@ -606,7 +645,7 @@ class TestEiProtocol:
connection = ei.connection
assert connection is not None
seat = ei.seats[0]
- ei.send(seat.Bind(0x40)) # binding to invalid caps should get us disconnected
+ ei.send(seat.Bind(0x1)) # binding to invalid caps should get us disconnected
try:
ei.dispatch()
time.sleep(0.1)
@@ -647,7 +686,17 @@ class TestEiProtocol:
seat.connect("Destroyed", destroyed_cb)
if bind_first:
- ei.send(seat.Bind(EiDevice.EiCapabilities.POINTER))
+ ei.send(
+ seat.Bind(
+ seat.bind_mask(
+ [
+ InterfaceName.EI_POINTER,
+ InterfaceName.EI_BUTTON,
+ InterfaceName.EI_SCROLL,
+ ]
+ )
+ )
+ )
ei.send(seat.Release())
ei.dispatch()
@@ -767,7 +816,16 @@ class TestEiProtocol:
seat = new_objects["seat"]
assert seat is not None
seat.connect("Device", on_device)
- ei.send(seat.Bind(EiDevice.EiCapabilities.POINTER))
+
+ def on_done(seat):
+ if missing_interface != InterfaceName.EI_POINTER:
+ mask = seat.bind_mask([InterfaceName.EI_POINTER])
+ else:
+ # Need to bind to *something* to get at least one device
+ mask = seat.bind_mask([InterfaceName.EI_KEYBOARD])
+ ei.send(seat.Bind(mask))
+
+ seat.connect("Done", on_done)
status.seats = True
def on_disconnected(connection, last_serial, reason, explanation):
@@ -927,6 +985,7 @@ class TestEiProtocol:
def on_new_object(o: Interface):
logger.debug("new object", object=o)
if o.name == InterfaceName.EI_SEAT:
+ ei.seat_fill_capability_masks(o)
o.connect("Device", on_new_device)
ei.context.connect("register", on_new_object)
@@ -934,12 +993,17 @@ class TestEiProtocol:
ei.init_default_sender_connection()
ei.wait_for_seat()
+ seat = ei.seats[0]
ei.send(
- ei.seats[0].Bind(
- EiDevice.EiCapabilities.POINTER
- | EiDevice.EiCapabilities.POINTER_ABSOLUTE
- | EiDevice.EiCapabilities.KEYBOARD
- | EiDevice.EiCapabilities.TOUCHSCREEN
+ seat.Bind(
+ seat.bind_mask(
+ [
+ InterfaceName.EI_POINTER,
+ InterfaceName.EI_POINTER_ABSOLUTE,
+ InterfaceName.EI_KEYBOARD,
+ InterfaceName.EI_TOUCHSCREEN,
+ ]
+ )
)
)
@@ -951,7 +1015,7 @@ class TestEiProtocol:
@pytest.mark.parametrize(
"wanted_pointer",
- (EiDevice.EiCapabilities.POINTER, EiDevice.EiCapabilities.POINTER_ABSOLUTE),
+ (InterfaceName.EI_POINTER, InterfaceName.EI_POINTER_ABSOLUTE),
)
def test_connect_receive_pointer(self, eis, wanted_pointer):
"""
@@ -961,14 +1025,11 @@ class TestEiProtocol:
@attr.s
class Status:
- pointers: dict[EiDevice.EiCapabilities, Interface] = attr.ib(default=attr.Factory(dict)) # type: ignore
+ pointers: dict[InterfaceName, Interface] = attr.ib(default=attr.Factory(dict)) # type: ignore
all_caps: int = attr.ib(default=0)
status = Status()
- def on_capabilities(device, caps):
- status.all_caps |= caps
-
def on_interface(device, object, name, version, new_objects):
logger.debug(
"new capability",
@@ -977,21 +1038,17 @@ class TestEiProtocol:
name=name,
version=version,
)
- if name == InterfaceName.EI_POINTER:
- status.pointers[EiDevice.EiCapabilities.POINTER] = new_objects["object"]
- elif name == InterfaceName.EI_POINTER_ABSOLUTE:
- status.pointers[EiDevice.EiCapabilities.POINTER_ABSOLUTE] = new_objects[
- "object"
- ]
+ if name in [InterfaceName.EI_POINTER, InterfaceName.EI_POINTER_ABSOLUTE]:
+ status.pointers[InterfaceName(name)] = new_objects["object"]
def on_new_device(seat, device, version, new_objects):
logger.debug("new device", object=new_objects["device"])
new_objects["device"].connect("Interface", on_interface)
- new_objects["device"].connect("Capabilities", on_capabilities)
def on_new_object(o: Interface):
logger.debug("new object", object=o)
if o.name == InterfaceName.EI_SEAT:
+ ei.seat_fill_capability_masks(o)
o.connect("Device", on_new_device)
ei.context.connect("register", on_new_object)
@@ -999,10 +1056,10 @@ class TestEiProtocol:
ei.init_default_sender_connection()
ei.wait_for_seat()
- ei.send(ei.seats[0].Bind(wanted_pointer))
+ seat = ei.seats[0]
+ ei.send(seat.Bind(seat.bind_mask([wanted_pointer])))
ei.wait_for(lambda: status.pointers)
assert status.pointers[wanted_pointer] is not None
assert len(status.pointers) == 1
- assert status.all_caps & wanted_pointer