diff --git a/proto/ei.proto b/proto/ei.proto index 74392d4..d0ed8ac 100644 --- a/proto/ei.proto +++ b/proto/ei.proto @@ -84,8 +84,9 @@ message ConfigureCapabilities { } message Connect { - string name = 1; - bool is_sender = 2; + uint32 version = 1; /* Must be equal or less to the server's GetVersion response */ + string name = 2; + bool is_sender = 3; } message ConnectDone { diff --git a/src/libei-private.h b/src/libei-private.h index 610a974..10e04fa 100644 --- a/src/libei-private.h +++ b/src/libei-private.h @@ -71,6 +71,7 @@ struct ei { bool is_sender; uint32_t server_version; + uint32_t client_version; }; enum ei_seat_state { diff --git a/src/libei-proto.c b/src/libei-proto.c index 7c274c2..4f3a569 100644 --- a/src/libei-proto.c +++ b/src/libei-proto.c @@ -325,12 +325,13 @@ ei_proto_send_get_version(struct ei *ei) } static int -ei_proto_send_connect(struct ei *ei) +ei_proto_send_connect(struct ei *ei, uint32_t version) { prepare_msg(CONNECT, Connect, connect); connect.name = ei->name; connect.is_sender = ei->is_sender; + connect.version = version; return ei_proto_send_msg(ei, &msg); } diff --git a/src/libei-proto.h b/src/libei-proto.h index 5738819..d224194 100644 --- a/src/libei-proto.h +++ b/src/libei-proto.h @@ -80,7 +80,7 @@ struct ei_proto_interface { }; struct ei_proto_requests { - int (*connect)(struct ei *ei); + int (*connect)(struct ei *ei, uint32_t version); int (*connect_done)(struct ei *ei); int (*disconnect)(struct ei *ei); int (*bind_seat)(struct ei_seat *seat, enum ei_device_capability cap); diff --git a/src/libei.c b/src/libei.c index f60c350..6e00286 100644 --- a/src/libei.c +++ b/src/libei.c @@ -52,7 +52,7 @@ _Static_assert(sizeof(enum ei_event_type) == sizeof(int), "Invalid enum size"); _Static_assert(sizeof(enum ei_log_priority) == sizeof(int), "Invalid enum size"); static int -ei_finish_set_connection(struct ei *ei); +ei_finish_set_connection(struct ei *ei, uint32_t version); static struct ei_seat * ei_find_seat(struct ei *ei, uint32_t seatid) @@ -196,6 +196,27 @@ ei_get_fd(struct ei *ei) return sink_get_fd(ei->sink); } +_public_ uint32_t +ei_get_version(struct ei *ei) +{ + switch (ei->state) { + case EI_STATE_NEW: + case EI_STATE_BACKEND: + case EI_STATE_VERSION_QUERY: + case EI_STATE_CONNECTING: + break; + case EI_STATE_CONNECTED: + case EI_STATE_DISCONNECTING: + return ei->client_version; + case EI_STATE_DISCONNECTED: + break; + } + + log_bug(ei, "Version is only available on successful connection"); + + return 0; +} + _public_ void ei_dispatch(struct ei *ei) { @@ -1354,7 +1375,11 @@ handle_msg_version_during_connection(struct ei *ei, uint32_t version) if (rc != 0) return rc; - return ei_finish_set_connection(ei); + const uint32_t our_version = VERSION_V(1); + + ei->client_version = min(our_version, version); + + return ei_finish_set_connection(ei, ei->client_version); } static const struct ei_proto_interface intf_state_backend = { @@ -1487,9 +1512,9 @@ ei_set_connection(struct ei *ei, int fd) } static int -ei_finish_set_connection(struct ei *ei) +ei_finish_set_connection(struct ei *ei, uint32_t version) { - int rc = ei->requests->connect(ei); + int rc = ei->requests->connect(ei, version); struct ei_property *prop; list_for_each_safe(prop, &ei->properties, link) { diff --git a/src/libei.h b/src/libei.h index 6024d53..4b584c5 100644 --- a/src/libei.h +++ b/src/libei.h @@ -656,6 +656,21 @@ ei_get_fd(struct ei *ei); void ei_dispatch(struct ei *ei); +/** + * Return the protocol version for this client. Version negotiation is handled by libei + * during the initial connection, the client's version is the minimum version between + * the server's version and the client supported version. + * + * @note libei currently has no API to request specific versions + * + * It is an application bug to call this function before receiving the @ref + * EI_EVENT_CONNECT event. + * + * @return The protocol version. + */ +uint32_t +ei_get_version(struct ei *ei); + /** * Return the next event from the event queue, removing it from the queue. * diff --git a/src/libeis-client.c b/src/libeis-client.c index 821a14a..c7cd833 100644 --- a/src/libeis-client.c +++ b/src/libeis-client.c @@ -72,6 +72,8 @@ OBJECT_IMPLEMENT_UNREF_CLEANUP(eis_client); _public_ OBJECT_IMPLEMENT_GETTER(eis_client, name, const char*); _public_ +OBJECT_IMPLEMENT_GETTER(eis_client, version, uint32_t); +_public_ OBJECT_IMPLEMENT_SETTER(eis_client, user_data, void*); _public_ OBJECT_IMPLEMENT_GETTER(eis_client, user_data, void*); @@ -524,8 +526,14 @@ client_msg_configure_capabilities(struct eis_client *client, uint32_t allowed_ca } static int -client_msg_connect(struct eis_client *client, const char *name, bool is_sender) +client_msg_connect(struct eis_client *client, uint32_t version, + const char *name, bool is_sender) { + if (client->version > EIS_PROTOCOL_VERSION) + return -EPROTO; + + client->version = version; + if (client->name == NULL) client->name = xstrdup(name); diff --git a/src/libeis-private.h b/src/libeis-private.h index a3896d7..aadf2b2 100644 --- a/src/libeis-private.h +++ b/src/libeis-private.h @@ -68,6 +68,7 @@ struct eis_client { void *user_data; struct list link; struct source *source; + uint32_t version; uint32_t id; enum eis_client_state state; char *name; diff --git a/src/libeis-proto.c b/src/libeis-proto.c index 207260e..3f24568 100644 --- a/src/libeis-proto.c +++ b/src/libeis-proto.c @@ -524,7 +524,8 @@ eis_proto_handle_message(struct eis_client *client, int rc; switch (proto->msg_case) { case CLIENT_MESSAGE__MSG_CONNECT: - rc = call(connect, client, proto->connect->name, proto->connect->is_sender); + rc = call(connect, client, proto->connect->version, + proto->connect->name, proto->connect->is_sender); break; case CLIENT_MESSAGE__MSG_CONNECT_DONE: rc = call(connect_done, client); diff --git a/src/libeis-proto.h b/src/libeis-proto.h index a1ff587..c52df2a 100644 --- a/src/libeis-proto.h +++ b/src/libeis-proto.h @@ -36,7 +36,7 @@ /* callbacks invoked during eis_proto_parse_message() */ struct eis_proto_interface { - int (*connect)(struct eis_client *client, const char *name, bool is_sender); + int (*connect)(struct eis_client *client, uint32_t version, const char *name, bool is_sender); int (*connect_done)(struct eis_client *client); int (*disconnect)(struct eis_client *client); int (*bind_seat)(struct eis_client *client, uint32_t seatid, enum eis_device_capability cap); diff --git a/src/libeis.h b/src/libeis.h index 546833c..12c76fe 100644 --- a/src/libeis.h +++ b/src/libeis.h @@ -517,6 +517,12 @@ eis_client_set_user_data(struct eis_client *eis_client, void *user_data); const char * eis_client_get_name(struct eis_client *client); +/** + * Return the protocol version supported by this client. + */ +uint32_t +eis_client_get_version(struct eis_client *client); + /** * Allow connection from the client. This can only be done once, further * calls to this functions are ignored. diff --git a/test/eierpecken.c b/test/eierpecken.c index 3a75452..4892326 100644 --- a/test/eierpecken.c +++ b/test/eierpecken.c @@ -731,6 +731,9 @@ _peck_dispatch_eis(struct peck *peck, int lineno) switch (eis_event_get_type(e)) { case EIS_EVENT_CLIENT_CONNECT: case EIS_EVENT_CLIENT_DISCONNECT: + /* version is currently hardcoded, remove when necessary */ + munit_assert_int(eis_client_get_version(eis_event_get_client(e)), ==, 1); + if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT) || flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_REJECT_CLIENT)) process_event = tristate_yes; diff --git a/test/test-eis.c b/test/test-eis.c index cc7b3aa..f6d90d9 100644 --- a/test/test-eis.c +++ b/test/test-eis.c @@ -25,6 +25,7 @@ #include "config.h" #include "util-munit.h" +#include "util-version.h" #include "eierpecken.h" @@ -45,6 +46,26 @@ MUNIT_TEST(eistest_ref_unref) return MUNIT_OK; } +MUNIT_TEST(eistest_version) +{ + _unref_(peck) *peck = peck_new(); + + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_NONE); + peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_CONNECT); + + peck_dispatch_until_stable(peck); + + with_server(peck) { + _unref_(eis_event) *connect = peck_eis_next_event(eis, EIS_EVENT_CLIENT_CONNECT); + struct eis_client *client = eis_event_get_client(connect); + + /* We can't set the client version yet, but we know it's hardcoded to 1 */ + munit_assert_int(eis_client_get_version(client), ==, VERSION_V(1)); + } + + return MUNIT_OK; +} + MUNIT_TEST(eistest_name) { _unref_(peck) *peck = peck_new();