mirror of
https://gitlab.freedesktop.org/libinput/libei.git
synced 2026-05-05 08:58:02 +02:00
proto: add a ping/pong request
This is effectively the same as connection.sync, but goes the other way. This adds the ei_callback.done request. In libeis this is (currently) enforced immediately after sending the connection object. Not required there and makes the code a bit messier but this way we can ensure that any client library handles that part of the code.
This commit is contained in:
parent
50013b4b3c
commit
f41cc91599
10 changed files with 201 additions and 3 deletions
|
|
@ -348,6 +348,28 @@
|
|||
</description>
|
||||
<arg name="invalid_id" type="uint" />
|
||||
</event>
|
||||
|
||||
<event name="ping" since="1">
|
||||
<description summary="ping event">
|
||||
The ping event asks the client to emit the 'done' event
|
||||
on the provided ei_callback object. Since requests are
|
||||
handled in-order and events are delivered in-order, this can
|
||||
be used as a synchronization point to ensure all previous requests
|
||||
and the resulting events have been handled.
|
||||
|
||||
The object returned by this request must be destroyed by the
|
||||
ei client implementation after the callback is fired and as
|
||||
such the client must not attempt to use it after that point.
|
||||
|
||||
Note that for a server to use this request the client must announce
|
||||
support for this interface in ei_connection_setup.interface. It is
|
||||
a protocol violation to send this event to a client without the
|
||||
"ei_callback" interface.
|
||||
</description>
|
||||
<arg name="callback" type="new_id" interface="ei_callback"
|
||||
summary="callback object for the ping request"/>
|
||||
<arg name="version" type="uint" summary="the version of the callback object"/>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
<interface name="ei_callback" version="1">
|
||||
|
|
@ -360,6 +382,20 @@
|
|||
support for this interface in ei_connection_setup.interface.
|
||||
</description>
|
||||
|
||||
<!-- ei_callback client requests version 1 -->
|
||||
|
||||
<request name="done" type="destructor" since="1">
|
||||
<description summary="done event">
|
||||
Notify the server when the related event is done. Immediately after this event
|
||||
the ei_callback object is destroyed by the client and as such must not be used
|
||||
any further.
|
||||
|
||||
This request may only be called on an ei_callback object that was created by the
|
||||
EIS implementation.
|
||||
</description>
|
||||
<arg name="callback_data" type="uint" summary="request-specific data for the callback"/>
|
||||
</request>
|
||||
|
||||
<!-- ei_callback events version 1 -->
|
||||
|
||||
<event name="done" type="destructor" since="1">
|
||||
|
|
@ -367,6 +403,9 @@
|
|||
Notify the client when the related request is done. Immediately after this event
|
||||
the ei_callback object is destroyed by the EIS implementation and as such the
|
||||
client must not attempt to use it after that point.
|
||||
|
||||
This event may only be sent on an ei_callback object that was created by the
|
||||
client.
|
||||
</description>
|
||||
<arg name="callback_data" type="uint" summary="request-specific data for the callback"/>
|
||||
</event>
|
||||
|
|
|
|||
|
|
@ -101,3 +101,18 @@ ei_callback_new(struct ei *ei, ei_callback_func func, void *callback_data)
|
|||
|
||||
return callback; /* ref owned by caller */
|
||||
}
|
||||
|
||||
struct ei_callback *
|
||||
ei_callback_new_for_id(struct ei *ei, uint32_t id, uint32_t version)
|
||||
{
|
||||
struct ei_callback *callback = ei_callback_create(&ei->object);
|
||||
|
||||
callback->proto_object.id = id;
|
||||
callback->proto_object.implementation = callback;
|
||||
callback->proto_object.interface = &ei_callback_proto_interface;
|
||||
callback->proto_object.version = version; /* FIXME */
|
||||
ei_register_object(ei, &callback->proto_object);
|
||||
list_init(&callback->link);
|
||||
|
||||
return callback; /* ref owned by caller */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,3 +57,6 @@ OBJECT_DECLARE_UNREF(ei_callback);
|
|||
|
||||
struct ei_callback *
|
||||
ei_callback_new(struct ei *ei, ei_callback_func func, void *callback_data);
|
||||
|
||||
struct ei_callback *
|
||||
ei_callback_new_for_id(struct ei *ei, uint32_t id, uint32_t version);
|
||||
|
|
|
|||
13
src/libei.c
13
src/libei.c
|
|
@ -83,6 +83,7 @@ OBJECT_IMPLEMENT_GETTER(ei, connection, struct ei_connection *);
|
|||
|
||||
DEFINE_UNREF_CLEANUP_FUNC(ei_device);
|
||||
DEFINE_UNREF_CLEANUP_FUNC(ei_region);
|
||||
DEFINE_UNREF_CLEANUP_FUNC(ei_callback);
|
||||
|
||||
struct ei *
|
||||
ei_get_context(struct ei *ei)
|
||||
|
|
@ -670,6 +671,17 @@ handle_msg_invalid_object(struct ei_connection *connection, uint32_t object)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_msg_ping(struct ei_connection *connection, uint32_t id, uint32_t version)
|
||||
{
|
||||
struct ei *ei = ei_connection_get_context(connection);
|
||||
_unref_(ei_callback) *callback = ei_callback_new_for_id(ei, id, version);
|
||||
|
||||
ei_callback_request_done(callback, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define DISCONNECT_IF_SENDER_CONTEXT(ei_) do {\
|
||||
if (ei_->is_sender) { \
|
||||
log_bug_client(ei_, "Invalid event from receiver EIS context. Disconnecting"); \
|
||||
|
|
@ -681,6 +693,7 @@ static const struct ei_connection_interface interface = {
|
|||
.disconnected = handle_msg_disconnected,
|
||||
.seat = handle_msg_seat,
|
||||
.invalid_object = handle_msg_invalid_object,
|
||||
.ping = handle_msg_ping,
|
||||
};
|
||||
|
||||
const struct ei_connection_interface *
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include "util-mem.h"
|
||||
#include "util-io.h"
|
||||
#include "util-strings.h"
|
||||
#include "util-version.h"
|
||||
|
||||
#include "libeis-private.h"
|
||||
#include "eis-proto.h"
|
||||
|
|
@ -46,6 +47,8 @@ eis_callback_destroy(struct eis_callback *callback)
|
|||
OBJECT_IMPLEMENT_REF(eis_callback);
|
||||
OBJECT_IMPLEMENT_UNREF_CLEANUP(eis_callback);
|
||||
OBJECT_IMPLEMENT_GETTER_AS_REF(eis_callback, proto_object, const struct brei_object *);
|
||||
OBJECT_IMPLEMENT_GETTER(eis_callback, user_data, void *);
|
||||
OBJECT_IMPLEMENT_SETTER(eis_callback, user_data, void *);
|
||||
|
||||
static
|
||||
OBJECT_IMPLEMENT_CREATE(eis_callback);
|
||||
|
|
@ -71,7 +74,21 @@ eis_callback_get_id(struct eis_callback *callback)
|
|||
return callback->proto_object.id;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
eis_callback_get_version(struct eis_callback *callback)
|
||||
{
|
||||
return callback->proto_object.version;
|
||||
}
|
||||
|
||||
static int
|
||||
client_msg_done(struct eis_callback *callback, uint32_t callback_data)
|
||||
{
|
||||
callback->func(callback, callback->callback_data, callback_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct eis_callback_interface interface = {
|
||||
.done = client_msg_done,
|
||||
};
|
||||
|
||||
const struct eis_callback_interface *
|
||||
|
|
@ -94,3 +111,21 @@ eis_callback_new(struct eis_client *client, uint32_t new_id, uint32_t version)
|
|||
|
||||
return callback; /* ref owned by caller */
|
||||
}
|
||||
|
||||
struct eis_callback *
|
||||
eis_callback_new_with_callback(struct eis_client *client, eis_callback_func func, void *callback_data)
|
||||
{
|
||||
struct eis_callback *callback = eis_callback_create(&client->object);
|
||||
|
||||
callback->proto_object.id = eis_client_get_new_id(client);
|
||||
callback->proto_object.implementation = callback;
|
||||
callback->proto_object.interface = &eis_callback_proto_interface;
|
||||
callback->proto_object.version = VERSION_V(1); /* FIXME */
|
||||
callback->callback_data = callback_data;
|
||||
eis_client_register_object(client, &callback->proto_object);
|
||||
|
||||
callback->func = func;
|
||||
list_init(&callback->link);
|
||||
|
||||
return callback; /* ref owned by caller */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,20 +30,35 @@
|
|||
|
||||
struct eis;
|
||||
struct eis_client;
|
||||
struct eis_callback;
|
||||
|
||||
typedef void (*eis_callback_func)(struct eis_callback *callback, void *callback_data, uint32_t proto_data);
|
||||
|
||||
/* This is a protocol-only object, not exposed in the API */
|
||||
struct eis_callback {
|
||||
struct object object;
|
||||
struct brei_object proto_object;
|
||||
void *user_data; /* Note: user-data is attached to the object */
|
||||
|
||||
struct list link; /* for use by the callers, if needed */
|
||||
|
||||
eis_callback_func func;
|
||||
void *callback_data; /* Note: callback-data is attached to the callback */
|
||||
};
|
||||
|
||||
OBJECT_DECLARE_GETTER(eis_callback, context, struct eis *);
|
||||
OBJECT_DECLARE_GETTER(eis_callback, client, struct eis_client *);
|
||||
OBJECT_DECLARE_GETTER(eis_callback, id, uint32_t);
|
||||
OBJECT_DECLARE_GETTER(eis_callback, version, uint32_t);
|
||||
OBJECT_DECLARE_GETTER(eis_callback, proto_object, const struct brei_object *);
|
||||
OBJECT_DECLARE_GETTER(eis_callback, interface, const struct eis_callback_interface *);
|
||||
OBJECT_DECLARE_GETTER(eis_callback, user_data, void*);
|
||||
OBJECT_DECLARE_SETTER(eis_callback, user_data, void*);
|
||||
OBJECT_DECLARE_REF(eis_callback);
|
||||
OBJECT_DECLARE_UNREF(eis_callback);
|
||||
|
||||
struct eis_callback *
|
||||
eis_callback_new(struct eis_client *client, uint32_t new_id, uint32_t version);
|
||||
|
||||
struct eis_callback *
|
||||
eis_callback_new_with_callback(struct eis_client *eis_client, eis_callback_func func, void *callback_data);
|
||||
|
|
|
|||
|
|
@ -242,9 +242,11 @@ eis_client_setup_done(struct eis_client *client, const char *name, bool is_sende
|
|||
client->name = xstrdup(name);
|
||||
client->is_sender = is_sender;
|
||||
client->interface_versions = *versions;
|
||||
|
||||
eis_queue_connect_event(client);
|
||||
client->state = EIS_CLIENT_STATE_CONNECTING;
|
||||
/* We don't queue the connect event yet because we send an extra ping/pong
|
||||
* out, just to make sure that part of the protocol works.
|
||||
* See pong() in libeis-client.c
|
||||
*/
|
||||
}
|
||||
|
||||
#define DISCONNECT_IF_RECEIVER_CONTEXT(client_) do { \
|
||||
|
|
|
|||
|
|
@ -75,6 +75,13 @@ eis_connection_setup_get_id(struct eis_connection_setup *setup)
|
|||
return setup->proto_object.id;
|
||||
}
|
||||
|
||||
static void
|
||||
pong(struct eis_connection *connection, void *user_data)
|
||||
{
|
||||
struct eis_client *client = eis_connection_get_client(connection);
|
||||
eis_queue_connect_event(client);
|
||||
}
|
||||
|
||||
static int
|
||||
client_msg_done(struct eis_connection_setup *setup)
|
||||
{
|
||||
|
|
@ -86,12 +93,15 @@ client_msg_done(struct eis_connection_setup *setup)
|
|||
rc = 0;
|
||||
}
|
||||
|
||||
|
||||
client->connection = eis_connection_new(client);
|
||||
eis_connection_setup_event_connection(setup, eis_connection_get_id(client->connection),
|
||||
eis_connection_get_version(client->connection));
|
||||
eis_connection_setup_unref(setup);
|
||||
|
||||
/* Force a ping/pong. This isn't necessary but it doesn't hurt much here
|
||||
* and it ensures that any client implementation doesn't have that part missing */
|
||||
eis_connection_ping(client->connection, pong, NULL);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,13 @@ eis_connection_destroy(struct eis_connection *connection)
|
|||
{
|
||||
struct eis_client *client = eis_connection_get_client(connection);
|
||||
eis_client_unregister_object(client, &connection->proto_object);
|
||||
|
||||
struct eis_callback *cb;
|
||||
list_for_each_safe(cb, &connection->pending_callbacks, link) {
|
||||
list_remove(&cb->link);
|
||||
free(eis_callback_get_user_data(cb));
|
||||
eis_callback_unref(cb);
|
||||
}
|
||||
}
|
||||
|
||||
OBJECT_IMPLEMENT_REF(eis_connection);
|
||||
|
|
@ -95,5 +102,49 @@ eis_connection_new(struct eis_client *client)
|
|||
connection->proto_object.version = client->interface_versions.ei_connection;
|
||||
eis_client_register_object(client, &connection->proto_object);
|
||||
|
||||
list_init(&connection->pending_callbacks);
|
||||
|
||||
|
||||
return connection; /* ref owned by caller */
|
||||
}
|
||||
|
||||
struct callback_user_data {
|
||||
eis_connection_ping_callback_t cb;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
static void
|
||||
ping_callback(struct eis_callback *callback, void *callback_data, uint32_t proto_data)
|
||||
{
|
||||
struct eis_connection *connection = callback_data;
|
||||
|
||||
_cleanup_free_ struct callback_user_data *data = eis_callback_get_user_data(callback);
|
||||
if (data->cb)
|
||||
data->cb(connection, data->user_data);
|
||||
|
||||
/* remove from pending callbacks */
|
||||
list_remove(&callback->link);
|
||||
eis_callback_unref(callback);
|
||||
}
|
||||
|
||||
void
|
||||
eis_connection_ping(struct eis_connection *connection, eis_connection_ping_callback_t cb,
|
||||
void *user_data)
|
||||
{
|
||||
struct eis_client *client = eis_connection_get_client(connection);
|
||||
|
||||
/* This is double-wrapped because we only use this for debugging purposes for
|
||||
* now. The actual callback calls sync_callback with our connection,
|
||||
* then we extract the user_data on the object and call into the
|
||||
* cb supplied to this function.
|
||||
*/
|
||||
struct eis_callback *callback = eis_callback_new_with_callback(client, ping_callback, connection);
|
||||
struct callback_user_data *data = xalloc(sizeof *data);
|
||||
data->cb = cb;
|
||||
data->user_data = user_data;
|
||||
eis_callback_set_user_data(callback, data);
|
||||
list_append(&connection->pending_callbacks, &callback->link);
|
||||
|
||||
eis_connection_event_ping(connection, eis_callback_get_id(callback),
|
||||
eis_callback_get_version(callback));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ struct eis_client;
|
|||
struct eis_connection {
|
||||
struct object object;
|
||||
struct brei_object proto_object;
|
||||
|
||||
struct list pending_callbacks;
|
||||
};
|
||||
|
||||
OBJECT_DECLARE_GETTER(eis_connection, context, struct eis *);
|
||||
|
|
@ -48,3 +50,16 @@ OBJECT_DECLARE_UNREF(eis_connection);
|
|||
|
||||
struct eis_connection *
|
||||
eis_connection_new(struct eis_client *client);
|
||||
|
||||
|
||||
/**
|
||||
* Called when the ei_callback.done request is received after
|
||||
* an ei_connection_ping() event.
|
||||
*/
|
||||
typedef void (*eis_connection_ping_callback_t)(struct eis_connection *connection,
|
||||
void *user_data);
|
||||
|
||||
void
|
||||
eis_connection_ping(struct eis_connection *connection,
|
||||
eis_connection_ping_callback_t callback,
|
||||
void *user_data);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue