diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a969d03..8de644b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -46,8 +46,8 @@ variables: # changing these will force rebuilding the associated image # Note: these tags have no meaning and are not tied to a particular # libinput version - FEDORA_TAG: '2023-03-17.0' - DEBIAN_TAG: '2023-03-17.0' + FEDORA_TAG: '2023-04-06.0' + DEBIAN_TAG: '2023-04-06.0' FDO_UPSTREAM_REPO: libinput/libei @@ -174,7 +174,7 @@ fedora:37@container-prep: FDO_DISTRIBUTION_VERSION: '37' FDO_DISTRIBUTION_PACKAGES: $FEDORA_PACKAGES FDO_DISTRIBUTION_TAG: $FEDORA_TAG - FDO_DISTRIBUTION_EXEC: 'pip install structlog' + FDO_DISTRIBUTION_EXEC: 'pip install structlog strenum' debian:bullseye@container-prep: extends: @@ -186,7 +186,7 @@ debian:bullseye@container-prep: FDO_DISTRIBUTION_VERSION: 'bullseye' FDO_DISTRIBUTION_PACKAGES: $DEBIAN_PACKAGES FDO_DISTRIBUTION_TAG: $DEBIAN_TAG - FDO_DISTRIBUTION_EXEC: 'pip install structlog' + FDO_DISTRIBUTION_EXEC: 'pip install structlog strenum' diff --git a/.gitlab-ci/ci.template b/.gitlab-ci/ci.template index ef2fb74..c7be526 100644 --- a/.gitlab-ci/ci.template +++ b/.gitlab-ci/ci.template @@ -183,7 +183,7 @@ python-ruff: FDO_DISTRIBUTION_VERSION: '{{version}}' FDO_DISTRIBUTION_PACKAGES: ${{distro.name.upper()}}_PACKAGES FDO_DISTRIBUTION_TAG: ${{distro.name.upper()}}_TAG - FDO_DISTRIBUTION_EXEC: 'pip install structlog' + FDO_DISTRIBUTION_EXEC: 'pip install structlog strenum' {% endfor %} {% endfor %} diff --git a/.gitlab-ci/config.yml b/.gitlab-ci/config.yml index 253b431..9bcd813 100644 --- a/.gitlab-ci/config.yml +++ b/.gitlab-ci/config.yml @@ -3,7 +3,7 @@ # # We're happy to rebuild all containers when one changes. -.default_tag: &default_tag '2023-03-17.0' +.default_tag: &default_tag '2023-04-06.0' distributions: - name: fedora diff --git a/proto/ei-scanner b/proto/ei-scanner index f8d8cce..c451560 100755 --- a/proto/ei-scanner +++ b/proto/ei-scanner @@ -299,7 +299,8 @@ class Enum: @attr.s class Interface: - name: str = attr.ib() + name: str = attr.ib() # mangled name, e.g. ei_pointer or eis_pointer + protocol_name: str = attr.ib() # name as in the XML, e.g. ei_pointer version: int = attr.ib() requests: List[Request] = attr.ib(init=False, factory=list) events: List[Event] = attr.ib(init=False, factory=list) @@ -388,9 +389,11 @@ class Interface: return snake2camel(self.name) @classmethod - def create(cls, name: str, version: int, mode: str = "ei") -> "Interface": + def create( + cls, name: str, protocol_name: str, version: int, mode: str = "ei" + ) -> "Interface": assert mode in ["ei", "eis", "brei"] - return cls(name=name, version=version, mode=mode) + return cls(name=name, protocol_name=protocol_name, version=version, mode=mode) @attr.s @@ -463,13 +466,19 @@ class ProtocolParser(xml.sax.handler.ContentHandler): self.location, ) - name = Interface.mangle_name(name, self.component) + protocol_name = name + interface_name = Interface.mangle_name(name, self.component) # We only create the interface on the first run, in subsequent runs we # re-use them so we can cross reference correctly if self._run_counter > 1: - intf = self.interface_by_name(name) + intf = self.interface_by_name(interface_name) else: - intf = Interface.create(name=name, version=version, mode=self.component) + intf = Interface.create( + name=interface_name, + protocol_name=protocol_name, + version=version, + mode=self.component, + ) self.interfaces.append(intf) self.current_interface = intf diff --git a/src/ei-proto.h.tmpl b/src/ei-proto.h.tmpl index 004065c..d00378b 100644 --- a/src/ei-proto.h.tmpl +++ b/src/ei-proto.h.tmpl @@ -51,6 +51,10 @@ struct {{interface.name}}; extern const struct brei_interface {{interface.name}}_proto_interface; {% endfor %} +{% for interface in interfaces %} +#define {{interface.name.upper()}}_INTERFACE_NAME "{{interface.protocol_name}}" +{% endfor %} + {% for interface in interfaces %} /** {{interface.name}} **/ diff --git a/src/libei-device.c b/src/libei-device.c index 0ef3b06..91caebe 100644 --- a/src/libei-device.c +++ b/src/libei-device.c @@ -393,18 +393,18 @@ handle_msg_interface(struct ei_device *device, object_id_t id, const char *name, { DISCONNECT_IF_INVALID_ID(device, id); - if (streq(name, "ei_pointer")) { + if (streq(name, EI_POINTER_INTERFACE_NAME)) { if (device->pointer) return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL, "Duplicate ei_pointer interface object on device"); device->pointer = ei_pointer_new(device, id, version); - } else if (streq(name, "ei_keyboard")) { + } else if (streq(name, EI_KEYBOARD_INTERFACE_NAME)) { if (device->keyboard) return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL, "Duplicate ei_keyboard interface object on device"); device->keyboard = ei_keyboard_new(device, id, version); - } else if (streq(name, "ei_touchscreen")) { + } else if (streq(name, EI_TOUCHSCREEN_INTERFACE_NAME)) { if (device->touchscreen) return brei_result_new(EI_CONNECTION_DISCONNECT_REASON_PROTOCOL, "Duplicate ei_touchscreen interface object on device"); diff --git a/src/libei-handshake.c b/src/libei-handshake.c index 507a86d..bd7856c 100644 --- a/src/libei-handshake.c +++ b/src/libei-handshake.c @@ -86,14 +86,14 @@ ei_handshake_initialize(struct ei_handshake *setup, uint32_t version) ei_handshake_request_name(setup, ei->name); if (version >= EI_HANDSHAKE_REQUEST_INTERFACE_VERSION_SINCE_VERSION) { - ei_handshake_request_interface_version(setup, "ei_connection", v->ei_connection); - ei_handshake_request_interface_version(setup, "ei_callback", v->ei_callback); - ei_handshake_request_interface_version(setup, "ei_pingpong", v->ei_pingpong); - ei_handshake_request_interface_version(setup, "ei_seat", v->ei_seat); - ei_handshake_request_interface_version(setup, "ei_device", v->ei_device); - ei_handshake_request_interface_version(setup, "ei_pointer", v->ei_pointer); - ei_handshake_request_interface_version(setup, "ei_keyboard", v->ei_keyboard); - ei_handshake_request_interface_version(setup, "ei_touchscreen", v->ei_touchscreen); + ei_handshake_request_interface_version(setup, EI_CONNECTION_INTERFACE_NAME, v->ei_connection); + ei_handshake_request_interface_version(setup, EI_CALLBACK_INTERFACE_NAME, v->ei_callback); + ei_handshake_request_interface_version(setup, EI_PINGPONG_INTERFACE_NAME, v->ei_pingpong); + ei_handshake_request_interface_version(setup, EI_SEAT_INTERFACE_NAME, v->ei_seat); + ei_handshake_request_interface_version(setup, EI_DEVICE_INTERFACE_NAME, v->ei_device); + ei_handshake_request_interface_version(setup, EI_POINTER_INTERFACE_NAME, v->ei_pointer); + ei_handshake_request_interface_version(setup, EI_KEYBOARD_INTERFACE_NAME, v->ei_keyboard); + ei_handshake_request_interface_version(setup, EI_TOUCHSCREEN_INTERFACE_NAME, v->ei_touchscreen); } ei_handshake_request_finish(setup); @@ -125,7 +125,7 @@ handle_msg_interface_version(struct ei_handshake *setup, const char *name, uint3 struct ei *ei = ei_handshake_get_context(setup); struct ei_interface_versions *v = &ei->interface_versions; - if (streq(name, "ei_handshake")) { + if (streq(name, EI_HANDSHAKE_INTERFACE_NAME)) { /* EIS shouldn't send this anyway, let's ignore this */ } #define VERSION_UPDATE(iface_) if (streq(name, #iface_)) v->iface_ = min(version, v->iface_); diff --git a/src/libeis-device.c b/src/libeis-device.c index 86be891..7080e6f 100644 --- a/src/libeis-device.c +++ b/src/libeis-device.c @@ -766,14 +766,16 @@ eis_device_add(struct eis_device *device) eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) { device->pointer = eis_pointer_new(device); rc = eis_device_event_interface(device, eis_pointer_get_id(device->pointer), - "ei_pointer", eis_pointer_get_version(device->pointer)); + EIS_POINTER_INTERFACE_NAME, + eis_pointer_get_version(device->pointer)); if (rc < 0) goto out; } if (eis_device_has_capability(device, EIS_DEVICE_CAP_KEYBOARD)) { device->keyboard = eis_keyboard_new(device); rc = eis_device_event_interface(device, eis_keyboard_get_id(device->keyboard), - "ei_keyboard", eis_keyboard_get_version(device->keyboard)); + EIS_KEYBOARD_INTERFACE_NAME, + eis_keyboard_get_version(device->keyboard)); if (rc < 0) goto out; @@ -786,7 +788,8 @@ eis_device_add(struct eis_device *device) if (eis_device_has_capability(device, EIS_DEVICE_CAP_TOUCH)) { device->touchscreen = eis_touchscreen_new(device); rc = eis_device_event_interface(device, eis_touchscreen_get_id(device->touchscreen), - "ei_touchscreen", eis_touchscreen_get_version(device->touchscreen)); + EIS_TOUCHSCREEN_INTERFACE_NAME, + eis_touchscreen_get_version(device->touchscreen)); if (rc < 0) goto out; } diff --git a/src/libeis-handshake.c b/src/libeis-handshake.c index b6b7a9e..c828d65 100644 --- a/src/libeis-handshake.c +++ b/src/libeis-handshake.c @@ -120,7 +120,7 @@ client_msg_finish(struct eis_handshake *setup) /* ei_callback needs a client-created object, so tell the client * about our version */ - eis_handshake_event_interface_version(setup, "ei_callback", + eis_handshake_event_interface_version(setup, EIS_CALLBACK_INTERFACE_NAME, setup->client_versions.ei_callback); eis_client_setup_done(client, setup->name, setup->is_sender, &setup->client_versions); @@ -188,9 +188,9 @@ client_msg_interface_version(struct eis_handshake *setup, const char *name, uint return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL, "Missing handshake versions"); - if (streq(name, "ei_handshake")) + if (streq(name, EIS_HANDSHAKE_INTERFACE_NAME)) return brei_result_new(EIS_CONNECTION_DISCONNECT_REASON_PROTOCOL, - "ei_handshake may not be used in interface_version"); + "%s may not be used in interface_version", name); struct eis_client *client = eis_handshake_get_client(setup); struct eis *eis = eis_client_get_context(client); diff --git a/test/eiproto.py.tmpl b/test/eiproto.py.tmpl index c19e554..bcc65ab 100644 --- a/test/eiproto.py.tmpl +++ b/test/eiproto.py.tmpl @@ -20,6 +20,10 @@ from typing import Any, Callable, Generator, Tuple from enum import IntEnum +try: + from enum import StrEnum +except ImportError: + from strenum import StrEnum import attr import binascii @@ -137,6 +141,12 @@ class Context: return o +class InterfaceName(StrEnum): +{% for interface in interfaces %} + {{ interface.name.upper() }} = "{{interface.protocol_name}}" +{% endfor %} + + @attr.s class Interface: object_id: int = attr.ib(repr=lambda id: f"{id:#x}") diff --git a/test/meson.build b/test/meson.build index 28651e7..5fc1fc1 100644 --- a/test/meson.build +++ b/test/meson.build @@ -46,7 +46,11 @@ required_python_modules = ['pytest', 'attr', 'structlog'] if build_oeffis required_python_modules += ['dbusmock'] endif -pymod.find_installation('python3', modules: required_python_modules) +python = pymod.find_installation('python3', modules: required_python_modules) +if python.language_version().version_compare('< 3.11') + pymod.find_installation('python3', modules: ['strenum']) +endif + pytest = find_program('pytest-3', 'pytest') pytest_args = ['--verbose', '--log-level=DEBUG'] diff --git a/test/test_protocol.py b/test/test_protocol.py index 76527cb..d6609b4 100644 --- a/test/test_protocol.py +++ b/test/test_protocol.py @@ -39,6 +39,7 @@ try: hexlify, Context, Interface, + InterfaceName, MessageHeader, EiCallback, EiConnection, @@ -150,7 +151,7 @@ class Ei: self.send(self.connection.Sync(cb.object_id)) return self.wait_for( - lambda: cb not in self.find_objects_by_interface("ei_callback") + lambda: cb not in self.find_objects_by_interface(InterfaceName.EI_CALLBACK) ) @property @@ -165,15 +166,15 @@ class Ei: self.send(setup.ContextType(EiHandshake.EiContextType.SENDER)) self.send(setup.Name("test client")) self.send( - setup.InterfaceVersion("ei_connection", VERSION_V(1)) + setup.InterfaceVersion(InterfaceName.EI_CONNECTION, VERSION_V(1)) ) # this one is required - self.send(setup.InterfaceVersion("ei_callback", VERSION_V(1))) - self.send(setup.InterfaceVersion("ei_pingpong", VERSION_V(1))) - self.send(setup.InterfaceVersion("ei_seat", VERSION_V(1))) - self.send(setup.InterfaceVersion("ei_device", VERSION_V(1))) - self.send(setup.InterfaceVersion("ei_pointer", VERSION_V(1))) - self.send(setup.InterfaceVersion("ei_keyboard", VERSION_V(1))) - self.send(setup.InterfaceVersion("ei_touchscreen", VERSION_V(1))) + self.send(setup.InterfaceVersion(InterfaceName.EI_CALLBACK, VERSION_V(1))) + self.send(setup.InterfaceVersion(InterfaceName.EI_PINGPONG, VERSION_V(1))) + self.send(setup.InterfaceVersion(InterfaceName.EI_SEAT, VERSION_V(1))) + self.send(setup.InterfaceVersion(InterfaceName.EI_DEVICE, VERSION_V(1))) + self.send(setup.InterfaceVersion(InterfaceName.EI_POINTER, VERSION_V(1))) + self.send(setup.InterfaceVersion(InterfaceName.EI_KEYBOARD, VERSION_V(1))) + self.send(setup.InterfaceVersion(InterfaceName.EI_TOUCHSCREEN, VERSION_V(1))) self.send(setup.Finish()) self.dispatch() @@ -388,7 +389,7 @@ class TestEiProtocol: if call.name == "InterfaceVersion": announced_interfaces.append((call.args["name"], call.args["version"])) - assert ("ei_callback", 1) in announced_interfaces + assert (InterfaceName.EI_CALLBACK, 1) in announced_interfaces eis.terminate() @@ -417,7 +418,11 @@ class TestEiProtocol: # or immediately disconnects us after the .done request ei.dispatch() - for interface in ["ei_connection", "ei_callback", "ei_pingpong"]: + for interface in [ + InterfaceName.EI_CONNECTION, + InterfaceName.EI_CALLBACK, + InterfaceName.EI_PINGPONG, + ]: ei.send( ei.handshake.InterfaceVersion(interface, VERSION_V(1)) ) # these are required @@ -501,13 +506,19 @@ class TestEiProtocol: ei.send(setup.HandshakeVersion(VERSION_V(1))) ei.send(setup.ContextType(EiHandshake.EiContextType.SENDER)) ei.send(setup.Name("test client")) - for interface in ["ei_connection", "ei_callback", "ei_pingpong"]: + for interface in [ + InterfaceName.EI_CONNECTION, + InterfaceName.EI_CALLBACK, + InterfaceName.EI_PINGPONG, + ]: ei.send( setup.InterfaceVersion(interface, VERSION_V(1)) ) # these are required - ei.send(setup.InterfaceVersion("ei_seat", VERSION_V(100))) # excessive version ei.send( - setup.InterfaceVersion("ei_device", VERSION_V(100)) + setup.InterfaceVersion(InterfaceName.EI_SEAT, VERSION_V(100)) + ) # excessive version + ei.send( + setup.InterfaceVersion(InterfaceName.EI_DEVICE, VERSION_V(100)) ) # excessive version ei.send(setup.Finish()) ei.dispatch() @@ -562,7 +573,11 @@ class TestEiProtocol: ei.send(setup.HandshakeVersion(VERSION_V(1))) ei.send(setup.ContextType(EiHandshake.EiContextType.SENDER)) ei.send(setup.Name("test client")) - for interface in ["ei_connection", "ei_callback", "ei_pingpong"]: + for interface in [ + InterfaceName.EI_CONNECTION, + InterfaceName.EI_CALLBACK, + InterfaceName.EI_PINGPONG, + ]: ei.send( setup.InterfaceVersion(interface, VERSION_V(1)) ) # these are required @@ -719,12 +734,12 @@ class TestEiProtocol: @pytest.mark.parametrize( "missing_interface", ( - "ei_callback", - "ei_connection", - "ei_pingpong", - "ei_seat", - "ei_device", - "ei_pointer", + InterfaceName.EI_CALLBACK, + InterfaceName.EI_CONNECTION, + InterfaceName.EI_PINGPONG, + InterfaceName.EI_SEAT, + InterfaceName.EI_DEVICE, + InterfaceName.EI_POINTER, ), ) def test_connect_without_ei_interfaces(self, eis, missing_interface): @@ -737,14 +752,14 @@ class TestEiProtocol: ei.send(setup.Name("test client")) for interface in [ - "ei_connection", - "ei_callback", - "ei_pingpong", - "ei_seat", - "ei_device", - "ei_pointer", - "ei_keyboard", - "ei_touchscreen", + InterfaceName.EI_CONNECTION, + InterfaceName.EI_CALLBACK, + InterfaceName.EI_PINGPONG, + InterfaceName.EI_SEAT, + InterfaceName.EI_DEVICE, + InterfaceName.EI_POINTER, + InterfaceName.EI_KEYBOARD, + InterfaceName.EI_TOUCHSCREEN, ]: if interface != missing_interface: ei.send(setup.InterfaceVersion(interface, 1)) @@ -761,11 +776,11 @@ class TestEiProtocol: try: def on_device(seat, id, version, new_objects={}): - assert missing_interface not in ["ei_device"] + assert missing_interface not in [InterfaceName.EI_DEVICE] status.devices = True def on_seat(connection, id, version, new_objects={}): - assert missing_interface not in ["ei_seat"] + assert missing_interface not in [InterfaceName.EI_SEAT] seat = new_objects["seat"] assert seat is not None seat.connect("Device", on_device) @@ -773,15 +788,18 @@ class TestEiProtocol: status.seats = True def on_disconnected(connection, last_serial, reason, explanation): - assert missing_interface in ["ei_seat", "ei_device"] + assert missing_interface in [ + InterfaceName.EI_SEAT, + InterfaceName.EI_DEVICE, + ] status.disconnected = True def on_connection(setup, serial, id, version, new_objects={}): # these three must be present, otherwise we get disconnected assert missing_interface not in [ - "ei_connection", - "ei_callback", - "ei_pingpong", + InterfaceName.EI_CONNECTION, + InterfaceName.EI_CALLBACK, + InterfaceName.EI_PINGPONG, ] status.connected = True connection = new_objects["connection"] @@ -794,7 +812,11 @@ class TestEiProtocol: ei.send(setup.Finish()) ei.dispatch() - if missing_interface in ["ei_connection", "ei_callback", "ei_pingpong"]: + if missing_interface in [ + InterfaceName.EI_CONNECTION, + InterfaceName.EI_CALLBACK, + InterfaceName.EI_PINGPONG, + ]: # valgrind is slow, so let's wait for it to catch up time.sleep(0.3) ei.dispatch() @@ -804,14 +826,14 @@ class TestEiProtocol: assert False, "We should've been disconnected by now" ei.wait_for(lambda: status.connected) - if missing_interface in ["ei_device", "ei_seat"]: + if missing_interface in [InterfaceName.EI_DEVICE, InterfaceName.EI_SEAT]: assert ei.wait_for(lambda: status.disconnected) assert ( status.disconnected ), f"Expected to be disconnected for missing {missing_interface}" else: assert ( - missing_interface == "ei_pointer" + missing_interface == InterfaceName.EI_POINTER ) # otherwise we shouldn't get here assert ei.callback_roundtrip(), "Callback roundtrip failed" assert status.connected @@ -821,7 +843,11 @@ class TestEiProtocol: assert status.devices except BrokenPipeError: - assert missing_interface in ["ei_connection", "ei_callback", "ei_pingpong"] + assert missing_interface in [ + InterfaceName.EI_CONNECTION, + InterfaceName.EI_CALLBACK, + InterfaceName.EI_PINGPONG, + ] @pytest.mark.parametrize("test_for", ("repeat-id", "invalid-id", "decreasing-id")) def test_invalid_object_id(self, eis, test_for): @@ -881,7 +907,12 @@ class TestEiProtocol: assert status.explanation is not None @pytest.mark.parametrize( - "wanted_interface", ("ei_pointer", "ei_keyboard", "ei_touchscreen") + "wanted_interface", + ( + InterfaceName.EI_POINTER, + InterfaceName.EI_KEYBOARD, + InterfaceName.EI_TOUCHSCREEN, + ), ) def test_connect_receive_device(self, eis, wanted_interface): """ @@ -912,7 +943,7 @@ class TestEiProtocol: def on_new_object(o: Interface): logger.debug("new object", object=o) - if o.name == "ei_seat": + if o.name == InterfaceName.EI_SEAT: o.connect("Device", on_new_device) ei.context.connect("register", on_new_object) @@ -963,7 +994,7 @@ class TestEiProtocol: name=name, version=version, ) - if name == "ei_pointer": + if name == InterfaceName.EI_POINTER: status.pointer = new_objects["object"] def on_new_device(seat, device, version, new_objects): @@ -973,7 +1004,7 @@ class TestEiProtocol: def on_new_object(o: Interface): logger.debug("new object", object=o) - if o.name == "ei_seat": + if o.name == InterfaceName.EI_SEAT: o.connect("Device", on_new_device) ei.context.connect("register", on_new_object)