diff --git a/proto/protocol.xml b/proto/protocol.xml
index 9787b21..57d3b45 100644
--- a/proto/protocol.xml
+++ b/proto/protocol.xml
@@ -68,6 +68,20 @@
the client with an error or (if appropriate) the client immediately disconnecting
from the EIS implementation.
+ Serial numbers:
+ Some events have serial numbers assiged by the EIS implementation. A serial is
+ a monotonically increasing unsigned 32-bit number (that wraps, clients must handle
+ this). Clients must use the most recently seen EIS serial number in requests that have
+ a last_serial argument.
+
+ The serial number aims to help a client track failure cases and the EIS
+ implementation to errors caused by the protocol's asynchronicity.
+ For example, if the EIS implementation removes a device at the serial number N,
+ client-initiated events for N-1 are caused by the client lagging behind and are not
+ a protocol violation.
+
+ Initial Connection:
+
The initial connection is a two-step process:
An ei_connection_setup object with the special ID 0 is guaranteed to
exists. The client must send the appropriate requests to set up
@@ -240,6 +254,7 @@
interface as announced by ei_connection_setup.interface, or any
lower version.
+
@@ -323,7 +338,9 @@
This event may be sent by the EIS implementation immediately before
- the client is disconnected.
+ the client is disconnected. The last_serial argument is set to the last
+ serial number used in a request by the client or zero if the client has not
+ yet issued a request.
Where a client is disconnected by EIS on purpose, for example after
a user interaction, the reason is disconnect_reason.disconnected (i.e. zero)
@@ -341,6 +358,7 @@
There is no guarantee this event is sent - the connection may be closed
without a disconnection event.
+
@@ -369,6 +387,7 @@
This event is sent by the EIS implementation when an object that
does not exist as seen by the EIS implementation.
+
@@ -609,6 +628,7 @@
It is a protocol violation to send this request for a client
of an ei_connection_setup.context_type other than sender.
+
@@ -620,6 +640,7 @@
It is a protocol violation to send this request for a client
of an ei_connection_setup.context_type other than sender.
+
@@ -634,6 +655,7 @@
It is a protocol violation to send this request for a client
of an ei_connection_setup.context_type other than sender.
+
@@ -815,9 +837,11 @@
+
+
@@ -826,7 +850,8 @@
It is a protocol violation to send this event for a client
of an ei_connection_setup.context_type other than receiver.
- >
+
+
@@ -837,6 +862,7 @@
It is a protocol violation to send this event for a client
of an ei_connection_setup.context_type other than receiver.
+
@@ -846,6 +872,7 @@
It is a protocol violation to send this event for a client
of an ei_connection_setup.context_type other than receiver.
+
diff --git a/src/libei-connection-setup.c b/src/libei-connection-setup.c
index d888475..8f6ba43 100644
--- a/src/libei-connection-setup.c
+++ b/src/libei-connection-setup.c
@@ -144,7 +144,7 @@ connected(struct ei_connection *connection, void *user_data)
}
static struct brei_result *
-handle_msg_connection(struct ei_connection_setup *setup, uint32_t id, uint32_t version)
+handle_msg_connection(struct ei_connection_setup *setup, uint32_t serial, uint32_t id, uint32_t version)
{
struct ei *ei = ei_connection_setup_get_context(setup);
assert(setup == ei->connection_setup);
@@ -153,6 +153,7 @@ handle_msg_connection(struct ei_connection_setup *setup, uint32_t id, uint32_t v
ei->connection = ei_connection_new(ei, id, version);
ei->state = EI_STATE_CONNECTING;
+ ei_update_serial(ei, serial);
/* Send a sync on the connection - EIS should immediately send a
* disconnect event where applicable, so if we get through to our
diff --git a/src/libei-device.c b/src/libei-device.c
index 527c20c..d15be86 100644
--- a/src/libei-device.c
+++ b/src/libei-device.c
@@ -249,8 +249,9 @@ handle_msg_done(struct ei_device *device)
}
static struct brei_result *
-handle_msg_resumed(struct ei_device *device)
+handle_msg_resumed(struct ei_device *device, uint32_t serial)
{
+ ei_update_serial(ei_device_get_context(device), serial);
ei_device_resumed(device);
ei_queue_device_resumed_event(device);
@@ -258,8 +259,9 @@ handle_msg_resumed(struct ei_device *device)
}
static struct brei_result *
-handle_msg_paused(struct ei_device *device)
+handle_msg_paused(struct ei_device *device, uint32_t serial)
{
+ ei_update_serial(ei_device_get_context(device), serial);
ei_device_paused(device);
ei_queue_device_paused_event(device);
@@ -275,12 +277,14 @@ handle_msg_paused(struct ei_device *device)
} while(0)
static struct brei_result *
-handle_msg_start_emulating(struct ei_device *device, uint32_t sequence)
+handle_msg_start_emulating(struct ei_device *device, uint32_t serial, uint32_t sequence)
{
struct brei_result *result = NULL;
DISCONNECT_IF_SENDER_CONTEXT(device);
+ ei_update_serial(ei_device_get_context(device), serial);
+
switch (device->state) {
case EI_DEVICE_STATE_DEAD:
case EI_DEVICE_STATE_NEW:
@@ -303,12 +307,14 @@ handle_msg_start_emulating(struct ei_device *device, uint32_t sequence)
}
static struct brei_result *
-handle_msg_stop_emulating(struct ei_device *device)
+handle_msg_stop_emulating(struct ei_device *device, uint32_t serial)
{
struct brei_result *result = NULL;
DISCONNECT_IF_SENDER_CONTEXT(device);
+ ei_update_serial(ei_device_get_context(device), serial);
+
switch (device->state) {
case EI_DEVICE_STATE_DEAD:
case EI_DEVICE_STATE_NEW:
@@ -353,10 +359,12 @@ maybe_error_on_device_state(struct ei_device *device, const char *event_type)
}
static struct brei_result *
-handle_msg_frame(struct ei_device *device, uint32_t time, uint32_t micros)
+handle_msg_frame(struct ei_device *device, uint32_t serial, uint32_t time, uint32_t micros)
{
DISCONNECT_IF_SENDER_CONTEXT(device);
+ ei_update_serial(ei_device_get_context(device), serial);
+
if (device->state == EI_DEVICE_STATE_EMULATING) {
ei_queue_frame_event(device, ms2us(time) + micros);
return NULL;
@@ -903,7 +911,8 @@ ei_device_close(struct ei_device *device)
break;
case EI_DEVICE_STATE_EMULATING:
if (ei_is_sender(ei_device_get_context(device)))
- ei_device_request_stop_emulating(device);
+ ei_device_request_stop_emulating(device,
+ ei_get_serial(ei_device_get_context(device)));
_fallthrough_;
case EI_DEVICE_STATE_PAUSED:
case EI_DEVICE_STATE_RESUMED:
@@ -1043,13 +1052,15 @@ _flush_frame(struct ei_device *device, const char *func)
_public_ void
ei_device_start_emulating(struct ei_device *device, uint32_t sequence)
{
+ struct ei *ei = ei_device_get_context(device);
+
if (device->state != EI_DEVICE_STATE_RESUMED)
return;
assert(!device->send_frame_event);
device->state = EI_DEVICE_STATE_EMULATING;
- int rc = ei_device_request_start_emulating(device, sequence);
+ int rc = ei_device_request_start_emulating(device, ei_get_serial(ei), sequence);
if (rc)
ei_disconnect(ei_device_get_context(device));
}
@@ -1057,12 +1068,14 @@ ei_device_start_emulating(struct ei_device *device, uint32_t sequence)
_public_ void
ei_device_stop_emulating(struct ei_device *device)
{
+ struct ei *ei = ei_device_get_context(device);
+
if (device->state != EI_DEVICE_STATE_EMULATING)
return;
ei_device_flush_frame(device);
device->state = EI_DEVICE_STATE_RESUMED;
- int rc = ei_device_request_stop_emulating(device);
+ int rc = ei_device_request_stop_emulating(device, ei_get_serial(ei));
if (rc)
ei_disconnect(ei_device_get_context(device));
}
@@ -1587,6 +1600,8 @@ ei_touch_up(struct ei_touch *touch)
_public_ void
ei_device_frame(struct ei_device *device, uint64_t time)
{
+ struct ei *ei = ei_device_get_context(device);
+
if (device->state != EI_DEVICE_STATE_EMULATING)
return;
@@ -1595,7 +1610,7 @@ ei_device_frame(struct ei_device *device, uint64_t time)
device->send_frame_event = false;
- int rc = ei_device_request_frame(device, us2ms(time), time % 1000);
+ int rc = ei_device_request_frame(device, ei_get_serial(ei), us2ms(time), time % 1000);
if (rc)
ei_disconnect(ei_device_get_context(device));
return;
diff --git a/src/libei-private.h b/src/libei-private.h
index b872d46..9cf66d1 100644
--- a/src/libei-private.h
+++ b/src/libei-private.h
@@ -81,6 +81,8 @@ struct ei {
struct list proto_objects; /* brei_object list */
uint32_t next_object_id;
+ uint32_t serial;
+
void *user_data;
struct brei_context *brei;
struct sink *sink;
@@ -118,6 +120,12 @@ ei_get_connection(struct ei *ei);
uint32_t
ei_get_new_id(struct ei *ei);
+void
+ei_update_serial(struct ei *ei, uint32_t serial);
+
+uint32_t
+ei_get_serial(struct ei *ei);
+
void
ei_register_object(struct ei *ei, struct brei_object *object);
diff --git a/src/libei.c b/src/libei.c
index e04d4c6..c936172 100644
--- a/src/libei.c
+++ b/src/libei.c
@@ -80,6 +80,7 @@ OBJECT_IMPLEMENT_SETTER(ei, user_data, void *);
_public_
OBJECT_IMPLEMENT_GETTER(ei, user_data, void *);
OBJECT_IMPLEMENT_GETTER(ei, connection, struct ei_connection *);
+OBJECT_IMPLEMENT_GETTER(ei, serial, uint32_t);
DEFINE_UNREF_CLEANUP_FUNC(brei_result);
DEFINE_UNREF_CLEANUP_FUNC(ei_device);
@@ -140,6 +141,12 @@ ei_get_new_id(struct ei *ei)
return ei->next_object_id++ & ~server_range;
}
+void
+ei_update_serial(struct ei *ei, uint32_t serial)
+{
+ ei->serial = serial;
+}
+
void
ei_register_object(struct ei *ei, struct brei_object *object)
{
@@ -656,7 +663,8 @@ ei_connected(struct ei *ei)
}
static struct brei_result *
-handle_msg_disconnected(struct ei_connection *connection, uint32_t reason, const char *explanation)
+handle_msg_disconnected(struct ei_connection *connection, uint32_t last_serial,
+ uint32_t reason, const char *explanation)
{
struct ei *ei = ei_connection_get_context(connection);
@@ -672,11 +680,11 @@ handle_msg_disconnected(struct ei_connection *connection, uint32_t reason, const
}
static struct brei_result *
-handle_msg_invalid_object(struct ei_connection *connection, uint32_t object)
+handle_msg_invalid_object(struct ei_connection *connection, uint32_t last_serial, uint32_t object)
{
struct ei *ei = ei_connection_get_context(connection);
- log_bug(ei, "Invalid object %#x, I don't yet know how to handle that", object);
+ log_bug(ei, "Invalid object %#x after %u, I don't yet know how to handle that", last_serial, object);
return NULL;
}
diff --git a/src/libeis-client.c b/src/libeis-client.c
index dee74bd..71f6c1c 100644
--- a/src/libeis-client.c
+++ b/src/libeis-client.c
@@ -78,6 +78,18 @@ OBJECT_IMPLEMENT_SETTER(eis_client, user_data, void*);
_public_
OBJECT_IMPLEMENT_GETTER(eis_client, user_data, void*);
+uint32_t
+eis_client_get_next_serial(struct eis_client *client)
+{
+ return ++client->serial;
+}
+
+void
+eis_client_update_client_serial(struct eis_client *client, uint32_t serial)
+{
+ client->last_client_serial = serial;
+}
+
_public_ struct eis*
eis_client_get_context(struct eis_client *client)
{
@@ -224,7 +236,9 @@ client_disconnect(struct eis_client *client,
}
}
eis_queue_disconnect_event(client);
- eis_connection_event_disconnected(client->connection, reason, explanation);
+ eis_connection_event_disconnected(client->connection,
+ client->last_client_serial,
+ reason, explanation);
client->connection = eis_connection_unref(client->connection);
_fallthrough_;
case EIS_CLIENT_STATE_NEW:
@@ -342,7 +356,9 @@ lookup_object(uint32_t object_id, struct brei_object **object, void *userdata)
log_debug(eis_client_get_context(client), "Failed to find object %#x", object_id);
if (client->connection)
- eis_connection_event_invalid_object(client->connection, object_id);
+ eis_connection_event_invalid_object(client->connection,
+ client->last_client_serial,
+ object_id);
return -ENOENT;
}
diff --git a/src/libeis-client.h b/src/libeis-client.h
index 96bed34..7395017 100644
--- a/src/libeis-client.h
+++ b/src/libeis-client.h
@@ -58,6 +58,9 @@ struct eis_client {
struct list proto_objects; /* struct brei_objects list */
uint32_t next_object_id;
+ uint32_t serial;
+ uint32_t last_client_serial;
+
struct eis_connection_setup *setup;
struct eis_client_interface_versions interface_versions;
@@ -87,6 +90,12 @@ OBJECT_DECLARE_GETTER(eis_client, proto_object, const struct brei_object *);
struct eis_client *
eis_client_new(struct eis *eis, int fd);
+uint32_t
+eis_client_get_next_serial(struct eis_client *client);
+
+void
+eis_client_update_client_serial(struct eis_client *client, uint32_t serial);
+
uint32_t
eis_client_get_new_id(struct eis_client *client);
diff --git a/src/libeis-connection-setup.c b/src/libeis-connection-setup.c
index 54296cf..c4400c8 100644
--- a/src/libeis-connection-setup.c
+++ b/src/libeis-connection-setup.c
@@ -102,7 +102,9 @@ client_msg_done(struct eis_connection_setup *setup)
eis_client_setup_done(client, setup->name, setup->is_sender, &setup->client_versions);
client->connection = eis_connection_new(client);
- eis_connection_setup_event_connection(setup, eis_connection_get_id(client->connection),
+ eis_connection_setup_event_connection(setup,
+ eis_client_get_next_serial(client),
+ eis_connection_get_id(client->connection),
eis_connection_get_version(client->connection));
/* These aren't required but libei is pointless without them, so let's enforce them
diff --git a/src/libeis-device.c b/src/libeis-device.c
index cf624d0..eedd855 100644
--- a/src/libeis-device.c
+++ b/src/libeis-device.c
@@ -234,10 +234,12 @@ client_msg_release(struct eis_device *device)
static struct brei_result *
-client_msg_start_emulating(struct eis_device *device, uint32_t sequence)
+client_msg_start_emulating(struct eis_device *device, uint32_t serial, uint32_t sequence)
{
struct brei_result *result = NULL;
+ eis_client_update_client_serial(eis_device_get_client(device), serial);
+
DISCONNECT_IF_RECEIVER_CONTEXT(device);
switch (device->state) {
@@ -261,10 +263,12 @@ client_msg_start_emulating(struct eis_device *device, uint32_t sequence)
}
static struct brei_result *
-client_msg_stop_emulating(struct eis_device *device)
+client_msg_stop_emulating(struct eis_device *device, uint32_t serial)
{
struct brei_result *result = NULL;
+ eis_client_update_client_serial(eis_device_get_client(device), serial);
+
DISCONNECT_IF_RECEIVER_CONTEXT(device);
switch (device->state) {
@@ -316,8 +320,10 @@ maybe_error_on_device_state(struct eis_device *device, const char *event_type)
}
static struct brei_result *
-client_msg_frame(struct eis_device *device, uint32_t time, uint32_t micros)
+client_msg_frame(struct eis_device *device, uint32_t serial, uint32_t time, uint32_t micros)
{
+ eis_client_update_client_serial(eis_device_get_client(device), serial);
+
DISCONNECT_IF_RECEIVER_CONTEXT(device);
if (device->state == EIS_DEVICE_STATE_EMULATING) {
@@ -854,6 +860,8 @@ _flush_frame(struct eis_device *device, const char *func)
_public_ void
eis_device_start_emulating(struct eis_device *device, uint32_t sequence)
{
+ struct eis_client *client = eis_device_get_client(device);
+
if (device->state != EIS_DEVICE_STATE_RESUMED)
return;
@@ -861,12 +869,14 @@ eis_device_start_emulating(struct eis_device *device, uint32_t sequence)
device->state = EIS_DEVICE_STATE_EMULATING;
- eis_device_event_start_emulating(device, sequence);
+ eis_device_event_start_emulating(device, eis_client_get_next_serial(client), sequence);
}
_public_ void
eis_device_stop_emulating(struct eis_device *device)
{
+ struct eis_client *client = eis_device_get_client(device);
+
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
@@ -874,7 +884,7 @@ eis_device_stop_emulating(struct eis_device *device)
device->state = EIS_DEVICE_STATE_RESUMED;
- eis_device_event_stop_emulating(device);
+ eis_device_event_stop_emulating(device, eis_client_get_next_serial(client));
}
_public_ void
@@ -1187,6 +1197,8 @@ eis_touch_up(struct eis_touch *touch)
_public_ void
eis_device_frame(struct eis_device *device, uint64_t time)
{
+ struct eis_client *client = eis_device_get_client(device);
+
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
@@ -1195,7 +1207,8 @@ eis_device_frame(struct eis_device *device, uint64_t time)
device->send_frame_event = false;
- eis_device_event_frame(device, us2ms(time), time % 1000);
+ eis_device_event_frame(device, eis_client_get_next_serial(client),
+ us2ms(time), time % 1000);
}
void
@@ -1222,21 +1235,25 @@ eis_device_closed_by_client(struct eis_device *device)
_public_ void
eis_device_pause(struct eis_device *device)
{
+ struct eis_client *client = eis_device_get_client(device);
+
if (device->state != EIS_DEVICE_STATE_RESUMED)
return;
device->state = EIS_DEVICE_STATE_PAUSED;
- eis_device_event_paused(device);
+ eis_device_event_paused(device, eis_client_get_next_serial(client));
}
_public_ void
eis_device_resume(struct eis_device *device)
{
+ struct eis_client *client = eis_device_get_client(device);
+
if (device->state != EIS_DEVICE_STATE_PAUSED)
return;
device->state = EIS_DEVICE_STATE_RESUMED;
- eis_device_event_resumed(device);
+ eis_device_event_resumed(device, eis_client_get_next_serial(client));
}
_public_ void