mirror of
https://gitlab.freedesktop.org/libinput/libei.git
synced 2026-05-05 03:08:14 +02:00
proto: add a version exchange prior to connect
Add a new protocol message "GetVersion" and the matching reply from the server with "Version" that can be sent at any time. The server always replies with the highest protocol version it supports, allowing the client to choose the protocol version it wants. These two messages also have a fixed string to make the protocol easy to identify in hexdumps. To avoid roundtrips on connection, libeis immediately sends the Version message. Ideally and by the time the client actually starts, that version is already available and we can continue without requiring a full roundtrip. This patch only adds the version exchange with the server, it does not yet add the bits for the client to actually set the version.
This commit is contained in:
parent
1592fdd57f
commit
08a4ce4aac
9 changed files with 185 additions and 16 deletions
15
meson.build
15
meson.build
|
|
@ -43,10 +43,14 @@ endif
|
|||
|
||||
add_project_arguments(cc.get_supported_arguments(cflags), language: 'c')
|
||||
|
||||
protocol_version = 1
|
||||
|
||||
config_h = configuration_data()
|
||||
config_h.set('_GNU_SOURCE', '1')
|
||||
config_h.set_quoted('EI_VERSION', meson.project_version())
|
||||
config_h.set_quoted('EIS_VERSION', meson.project_version())
|
||||
config_h.set('EI_PROTOCOL_VERSION', protocol_version)
|
||||
config_h.set('EIS_PROTOCOL_VERSION', protocol_version)
|
||||
|
||||
subdir('proto')
|
||||
|
||||
|
|
@ -127,7 +131,10 @@ pkgconfig.generate(lib_libei,
|
|||
description: 'Emulated Input client library',
|
||||
version: meson.project_version(),
|
||||
libraries: lib_libei,
|
||||
variables: [ 'portal=' + get_option('portal').to_string() ],
|
||||
variables: [
|
||||
'portal=' + get_option('portal').to_string(),
|
||||
'protocol_version=' + protocol_version.to_string(),
|
||||
],
|
||||
)
|
||||
|
||||
src_libeis = [
|
||||
|
|
@ -166,6 +173,9 @@ pkgconfig.generate(lib_libeis,
|
|||
description: 'Emulated Input server library',
|
||||
version: meson.project_version(),
|
||||
libraries: lib_libeis,
|
||||
variables: [
|
||||
'protocol_version=' + protocol_version.to_string(),
|
||||
],
|
||||
)
|
||||
|
||||
lib_libreis = shared_library('reis',
|
||||
|
|
@ -188,6 +198,9 @@ pkgconfig.generate(lib_libreis,
|
|||
description: 'Restriction handler for Emulated Input servers',
|
||||
version: meson.project_version(),
|
||||
libraries: lib_libreis,
|
||||
variables: [
|
||||
'protocol_version=' + protocol_version.to_string(),
|
||||
],
|
||||
)
|
||||
|
||||
dep_libxkbcommon = dependency('xkbcommon', required: false)
|
||||
|
|
|
|||
|
|
@ -48,6 +48,21 @@ syntax = "proto3";
|
|||
* Configure messages after a client has connected will be silently ignored.
|
||||
*/
|
||||
|
||||
/* Request the server version. This request MAY be sent at any time and/or
|
||||
* multiple times and the server always replies with the server's highest
|
||||
* supported version, see Version.
|
||||
*
|
||||
* To avoid roundtrips during the initial connection, an EIS implementation
|
||||
* *SHOULD* send a Version message immediately. A client thus may not need to
|
||||
* request the version.
|
||||
*
|
||||
* The "ei" field is the constant string "EI" and helps identifying
|
||||
* this connection in debugging tools.
|
||||
*/
|
||||
message GetVersion {
|
||||
string ei = 1; /* always "EI" */
|
||||
}
|
||||
|
||||
/* ConfigureName *must* be sent before the Connect event. Once a name is set,
|
||||
* subsequent attempts to change the name are ignored, including the name
|
||||
* provided in the Connect message.
|
||||
|
|
@ -178,6 +193,7 @@ message ClientMessage {
|
|||
BindSeat bind_seat = 4;
|
||||
CloseDevice close_device = 6;
|
||||
SetProperty set_property = 9;
|
||||
GetVersion get_version = 10;
|
||||
|
||||
/* Events */
|
||||
StartEmulating start_emulating = 20;
|
||||
|
|
@ -200,6 +216,25 @@ message ClientMessage {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The highest version number supported by the server. This message SHOULD be
|
||||
* sent by the EIS implementation immediately after a socket is
|
||||
* created/obtained. This avoids roundtrips between the server and the client
|
||||
* on connection.
|
||||
*
|
||||
* This message MAY be sent at any other time.
|
||||
* This message MUST be sent in response to a GetVersion request.
|
||||
*
|
||||
* The "eis" field is the constant string "EIS" and helps identifying
|
||||
* this connection in debugging tools.
|
||||
*
|
||||
* The content of this message never changes.
|
||||
*/
|
||||
message Version {
|
||||
uint32 version = 1;
|
||||
string eis = 2; /* always "EIS" */
|
||||
}
|
||||
|
||||
message Connected {
|
||||
}
|
||||
|
||||
|
|
@ -288,6 +323,7 @@ message ServerMessage {
|
|||
DevicePaused device_paused = 12;
|
||||
KeyboardModifiers keyboard_modifiers = 13;
|
||||
Property property = 14;
|
||||
Version version = 15;
|
||||
|
||||
/* Events */
|
||||
StartEmulating start_emulating = 20;
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ struct ei_backend_interface {
|
|||
enum ei_state {
|
||||
EI_STATE_NEW, /* No backend yet */
|
||||
EI_STATE_BACKEND, /* We have a backend */
|
||||
EI_STATE_VERSION_QUERY, /* Waiting for server version */
|
||||
EI_STATE_CONNECTING, /* client requested connect */
|
||||
EI_STATE_CONNECTED, /* server has sent connect */
|
||||
EI_STATE_DISCONNECTING, /* in the process of cleaning up */
|
||||
|
|
@ -69,6 +70,7 @@ struct ei {
|
|||
const struct ei_proto_requests *requests;
|
||||
|
||||
bool is_sender;
|
||||
uint32_t server_version;
|
||||
};
|
||||
|
||||
enum ei_seat_state {
|
||||
|
|
|
|||
|
|
@ -135,6 +135,9 @@ ei_proto_handle_message(struct ei *ei,
|
|||
proto->property->value[0] ? proto->property->value : NULL,
|
||||
proto->property->permissions);
|
||||
break;
|
||||
case SERVER_MESSAGE__MSG_VERSION:
|
||||
rc = call(version, ei, proto->version->version);
|
||||
break;
|
||||
/* Events */
|
||||
case SERVER_MESSAGE__MSG_START_EMULATING:
|
||||
rc = call(start_emulating, ei,
|
||||
|
|
@ -241,6 +244,7 @@ log_wire_message(struct ei *ei, const ClientMessage *msg, int error)
|
|||
MSG_STRING_CASE(BIND_SEAT);
|
||||
MSG_STRING_CASE(CLOSE_DEVICE);
|
||||
MSG_STRING_CASE(SET_PROPERTY);
|
||||
MSG_STRING_CASE(GET_VERSION);
|
||||
/* events */
|
||||
MSG_STRING_CASE(START_EMULATING);
|
||||
MSG_STRING_CASE(STOP_EMULATING);
|
||||
|
|
@ -310,6 +314,16 @@ ei_proto_send_msg_with_fds(struct ei *ei, const ClientMessage *msg, int *fds)
|
|||
msg.msg_case = CLIENT_MESSAGE__MSG_##_type; \
|
||||
msg._field = &_field
|
||||
|
||||
static int
|
||||
ei_proto_send_get_version(struct ei *ei)
|
||||
{
|
||||
prepare_msg(GET_VERSION, GetVersion, get_version);
|
||||
|
||||
get_version.ei = "EI";
|
||||
|
||||
return ei_proto_send_msg(ei, &msg);
|
||||
}
|
||||
|
||||
static int
|
||||
ei_proto_send_connect(struct ei *ei)
|
||||
{
|
||||
|
|
@ -545,6 +559,7 @@ static const struct ei_proto_requests requests = {
|
|||
.bind_seat = ei_proto_send_bind_seat,
|
||||
.set_property = ei_proto_send_set_property,
|
||||
.close_device = ei_proto_send_close_device,
|
||||
.get_version = ei_proto_send_get_version,
|
||||
|
||||
/* events */
|
||||
.start_emulating = ei_proto_send_start_emulating,
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ struct ei_proto_interface {
|
|||
int (*property)(struct ei *ei,
|
||||
const char *name, const char *value,
|
||||
uint32_t permissions);
|
||||
int (*version)(struct ei *ei, uint32_t version);
|
||||
|
||||
/* events */
|
||||
int (*start_emulating)(struct ei *ei, uint32_t deviceid);
|
||||
|
|
@ -85,6 +86,7 @@ struct ei_proto_requests {
|
|||
int (*bind_seat)(struct ei_seat *seat, enum ei_device_capability cap);
|
||||
int (*unbind_seat)(struct ei_seat *seat, enum ei_device_capability cap);
|
||||
int (*close_device)(struct ei_device *device);
|
||||
int (*get_version)(struct ei *ei);
|
||||
int (*start_emulating)(struct ei_device *device);
|
||||
int (*stop_emulating)(struct ei_device *device);
|
||||
|
||||
|
|
|
|||
92
src/libei.c
92
src/libei.c
|
|
@ -38,6 +38,7 @@
|
|||
#include "util-sources.h"
|
||||
#include "util-strings.h"
|
||||
#include "util-time.h"
|
||||
#include "util-version.h"
|
||||
|
||||
#include "libei.h"
|
||||
#include "libei-private.h"
|
||||
|
|
@ -50,6 +51,9 @@ _Static_assert(sizeof(enum ei_keymap_type) == sizeof(int), "Invalid enum size");
|
|||
_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);
|
||||
|
||||
static struct ei_seat *
|
||||
ei_find_seat(struct ei *ei, uint32_t seatid)
|
||||
{
|
||||
|
|
@ -1325,14 +1329,50 @@ handle_msg_touch_up(struct ei *ei, uint32_t deviceid, uint32_t touchid)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_msg_version(struct ei *ei, uint32_t version)
|
||||
{
|
||||
if (version == 0) {
|
||||
log_bug(ei, "server version is zero");
|
||||
return -EINVAL;
|
||||
} else if (ei->server_version && ei->server_version != version) {
|
||||
log_bug(ei, "server version is not constant (was %u, now %u)",
|
||||
ei->server_version, version);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
log_debug(ei, "server protocol version: %u\n", version);
|
||||
ei->server_version = version;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_msg_version_during_connection(struct ei *ei, uint32_t version)
|
||||
{
|
||||
int rc = handle_msg_version(ei, version);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
return ei_finish_set_connection(ei);
|
||||
}
|
||||
|
||||
static const struct ei_proto_interface intf_state_backend = {
|
||||
.version = handle_msg_version_during_connection,
|
||||
/* Everything triggers -EPROTO */
|
||||
.connected = NULL,
|
||||
};
|
||||
|
||||
static const struct ei_proto_interface intf_state_version_query = {
|
||||
.version = handle_msg_version_during_connection,
|
||||
/* Everything else triggers -EPROTO */
|
||||
.connected = NULL,
|
||||
};
|
||||
|
||||
static const struct ei_proto_interface intf_state_connecting = {
|
||||
.connected = handle_msg_connected,
|
||||
.disconnected = handle_msg_disconnected,
|
||||
.version = handle_msg_version,
|
||||
};
|
||||
|
||||
static const struct ei_proto_interface intf_state_connected = {
|
||||
|
|
@ -1348,6 +1388,7 @@ static const struct ei_proto_interface intf_state_connected = {
|
|||
.device_done = handle_msg_device_added_done,
|
||||
.keyboard_modifiers = handle_msg_keyboard_modifiers,
|
||||
.property = handle_msg_property,
|
||||
.version = handle_msg_version,
|
||||
|
||||
/* events */
|
||||
.start_emulating = handle_msg_start_emulating,
|
||||
|
|
@ -1368,6 +1409,7 @@ static const struct ei_proto_interface intf_state_connected = {
|
|||
static const struct ei_proto_interface *interfaces[] = {
|
||||
[EI_STATE_NEW] = NULL,
|
||||
[EI_STATE_BACKEND] = &intf_state_backend,
|
||||
[EI_STATE_VERSION_QUERY] = &intf_state_version_query,
|
||||
[EI_STATE_CONNECTING] = &intf_state_connecting,
|
||||
[EI_STATE_CONNECTED] = &intf_state_connected,
|
||||
[EI_STATE_DISCONNECTING] = NULL,
|
||||
|
|
@ -1424,23 +1466,18 @@ ei_set_connection(struct ei *ei, int fd)
|
|||
if (rc == 0) {
|
||||
ei->source = source_ref(source);
|
||||
ei->state = EI_STATE_BACKEND;
|
||||
rc = ei->requests->connect(ei);
|
||||
|
||||
struct ei_property *prop;
|
||||
list_for_each_safe(prop, &ei->properties, link) {
|
||||
if (rc == 0)
|
||||
rc = ei->requests->set_property(ei, prop->name, prop->value, prop->permissions);
|
||||
}
|
||||
/* The server SHOULD have sent the version number, let's
|
||||
* process that. If it's ready, we don't need a full roundtrip.
|
||||
*
|
||||
* FIXME: this will block if O_NONBLOCK is missing
|
||||
*/
|
||||
ei_dispatch(ei);
|
||||
|
||||
if (rc == 0) {
|
||||
rc = ei->requests->connect_done(ei);
|
||||
}
|
||||
if (rc == 0) {
|
||||
ei->state = EI_STATE_CONNECTING;
|
||||
}
|
||||
if (rc != 0) {
|
||||
log_error(ei, "message failed to send: %s", strerror(-rc));
|
||||
ei_disconnect(ei);
|
||||
if (ei->state == EI_STATE_BACKEND) {
|
||||
/* The server didn't send the version number, so request it. */
|
||||
rc = ei->requests->get_version(ei);
|
||||
ei->state = EI_STATE_VERSION_QUERY;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1449,6 +1486,31 @@ ei_set_connection(struct ei *ei, int fd)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
ei_finish_set_connection(struct ei *ei)
|
||||
{
|
||||
int rc = ei->requests->connect(ei);
|
||||
|
||||
struct ei_property *prop;
|
||||
list_for_each_safe(prop, &ei->properties, link) {
|
||||
if (rc == 0)
|
||||
rc = ei->requests->set_property(ei, prop->name, prop->value, prop->permissions);
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
rc = ei->requests->connect_done(ei);
|
||||
}
|
||||
if (rc == 0) {
|
||||
ei->state = EI_STATE_CONNECTING;
|
||||
}
|
||||
if (rc != 0) {
|
||||
log_error(ei, "message failed to send: %s\n", strerror(-rc));
|
||||
ei_disconnect(ei);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
_public_ void
|
||||
ei_configure_name(struct ei *ei, const char *name)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -36,11 +36,14 @@
|
|||
#include "util-sources.h"
|
||||
#include "util-strings.h"
|
||||
#include "util-structs.h"
|
||||
#include "util-tristate.h"
|
||||
|
||||
#include "libeis-private.h"
|
||||
#include "libeis-proto.h"
|
||||
#include "brei-shared.h"
|
||||
|
||||
DEFINE_TRISTATE(started, finished, connected);
|
||||
|
||||
static void
|
||||
eis_client_destroy(struct eis_client *client)
|
||||
{
|
||||
|
|
@ -132,6 +135,13 @@ eis_client_find_seat(struct eis_client *client, uint32_t seatid)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
client_send_version(struct eis_client *client, uint32_t version)
|
||||
{
|
||||
struct eis *eis = eis_client_get_context(client);
|
||||
return eis->requests->version(client, version);
|
||||
}
|
||||
|
||||
static int
|
||||
client_send_disconnect(struct eis_client *client)
|
||||
{
|
||||
|
|
@ -560,11 +570,18 @@ client_msg_set_property_with_event(struct eis_client *client,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
client_msg_get_version(struct eis_client *client)
|
||||
{
|
||||
return client_send_version(client, EI_PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
static const struct eis_proto_interface intf_state_new = {
|
||||
.connect = client_msg_connect,
|
||||
.set_property = client_msg_set_property,
|
||||
.connect_done = client_msg_connect_done,
|
||||
.disconnect = client_msg_disconnect,
|
||||
.get_version = client_msg_get_version,
|
||||
|
||||
.configure_name = client_msg_configure_name,
|
||||
.configure_capabilities = client_msg_configure_capabilities,
|
||||
|
|
@ -573,6 +590,7 @@ static const struct eis_proto_interface intf_state_new = {
|
|||
/* Client is waiting for us, shouldn't send anything except disconnect */
|
||||
static const struct eis_proto_interface intf_state_connecting = {
|
||||
.disconnect = client_msg_disconnect,
|
||||
.get_version = client_msg_get_version,
|
||||
|
||||
.configure_name = client_msg_configure_name,
|
||||
.configure_capabilities = client_msg_configure_capabilities,
|
||||
|
|
@ -583,6 +601,7 @@ static const struct eis_proto_interface intf_state_connected = {
|
|||
.set_property = client_msg_set_property_with_event,
|
||||
.bind_seat = client_msg_bind_seat,
|
||||
.close_device = client_msg_close_device,
|
||||
.get_version = client_msg_get_version,
|
||||
|
||||
/* events */
|
||||
.start_emulating = client_msg_start_emulating,
|
||||
|
|
@ -685,6 +704,9 @@ eis_client_new(struct eis *eis, int fd)
|
|||
|
||||
source_unref(s);
|
||||
|
||||
/* Immediately send our version so the client doesn't need a roundtrip */
|
||||
client_send_version(client, EI_PROTOCOL_VERSION);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ log_wire_message(struct eis *eis, const ServerMessage *msg)
|
|||
MSG_STRING_CASE(DEVICE_PAUSED);
|
||||
MSG_STRING_CASE(KEYBOARD_MODIFIERS);
|
||||
MSG_STRING_CASE(PROPERTY);
|
||||
MSG_STRING_CASE(VERSION);
|
||||
/* events */
|
||||
MSG_STRING_CASE(START_EMULATING);
|
||||
MSG_STRING_CASE(STOP_EMULATING);
|
||||
|
|
@ -129,6 +130,16 @@ eis_proto_send_msg_with_fds(struct eis_client *client, const ServerMessage *msg,
|
|||
msg.msg_case = SERVER_MESSAGE__MSG_##_type; \
|
||||
msg._field = &_field
|
||||
|
||||
static int
|
||||
eis_proto_send_version(struct eis_client *client, uint32_t v)
|
||||
{
|
||||
prepare_msg(VERSION, Version, version);
|
||||
version.version = v;
|
||||
version.eis = "EIS";
|
||||
|
||||
return eis_proto_send_msg(client, &msg);
|
||||
}
|
||||
|
||||
static int
|
||||
eis_proto_send_disconnected(struct eis_client *client)
|
||||
{
|
||||
|
|
@ -464,6 +475,7 @@ static const struct eis_proto_requests requests = {
|
|||
.device_region = eis_proto_send_device_region,
|
||||
.keyboard_modifiers = eis_proto_send_keyboard_modifiers,
|
||||
.property = eis_proto_send_property,
|
||||
.version = eis_proto_send_version,
|
||||
|
||||
/* events */
|
||||
.start_emulating = eis_proto_send_start_emulating,
|
||||
|
|
@ -534,6 +546,9 @@ eis_proto_handle_message(struct eis_client *client,
|
|||
proto->set_property->value[0] ? proto->set_property->value : NULL,
|
||||
proto->set_property->permissions);
|
||||
break;
|
||||
case CLIENT_MESSAGE__MSG_GET_VERSION:
|
||||
rc = call(get_version, client);
|
||||
break;
|
||||
/* Events */
|
||||
case CLIENT_MESSAGE__MSG_START_EMULATING:
|
||||
rc = call(start_emulating, client,
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ struct eis_proto_interface {
|
|||
int (*close_device)(struct eis_client *client, uint32_t deviceid);
|
||||
int (*set_property)(struct eis_client *client, const char *name,
|
||||
const char *value, uint32_t permissions);
|
||||
int (*get_version)(struct eis_client *client);
|
||||
/* events */
|
||||
int (*start_emulating)(struct eis_client *client, uint32_t deviceid);
|
||||
int (*stop_emulating)(struct eis_client *client, uint32_t deviceid);
|
||||
|
|
@ -83,6 +84,7 @@ struct eis_proto_requests {
|
|||
const struct eis_xkb_modifiers *mods);
|
||||
int (*property)(struct eis_client *client, const char *name,
|
||||
const char *value, uint32_t permissions);
|
||||
int (*version)(struct eis_client *client, uint32_t version);
|
||||
|
||||
/* events */
|
||||
int (*start_emulating)(struct eis_device *device, uint32_t deviceid);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue