diff --git a/src/libeis-client.c b/src/libeis-client.c index dde377c..d088d7d 100644 --- a/src/libeis-client.c +++ b/src/libeis-client.c @@ -227,6 +227,15 @@ eis_client_connect(struct eis_client *client) client->state = EIS_CLIENT_STATE_CONNECTED; } +static void +client_drop_seats(struct eis_client *client) +{ + struct eis_seat *s; + list_for_each_safe(s, &client->seats, link) { + eis_seat_drop(s); + } +} + static void client_disconnect(struct eis_client *client, enum eis_connection_disconnect_reason reason, @@ -237,20 +246,29 @@ client_disconnect(struct eis_client *client, /* Client already disconnected? don't bother sending an * event */ return; + case EIS_CLIENT_STATE_REQUESTED_DISCONNECT: + /* Drop the seats again (see client_msg_disconnect) because + * the server may have added seats between the client requesting the + * disconnect and EIS actually processing that event + */ + client_drop_seats(client); + /* DISCONNECTED event is already in the EIS queue */ + /* Must not send disconnected to the client */ + client->connection = eis_connection_unref(client->connection); + client->state = EIS_CLIENT_STATE_DISCONNECTED; + source_remove(client->source); + break; case EIS_CLIENT_STATE_CONNECTING: case EIS_CLIENT_STATE_CONNECTED: - { - struct eis_seat *s; - list_for_each_safe(s, &client->seats, link) { - eis_seat_drop(s); - } - } + client_drop_seats(client); eis_queue_disconnect_event(client); eis_connection_event_disconnected(client->connection, client->last_client_serial, reason, explanation); client->connection = eis_connection_unref(client->connection); - _fallthrough_; + client->state = EIS_CLIENT_STATE_DISCONNECTED; + source_remove(client->source); + break; case EIS_CLIENT_STATE_NEW: client->state = EIS_CLIENT_STATE_DISCONNECTED; source_remove(client->source); @@ -301,7 +319,15 @@ static struct brei_result * client_msg_disconnect(struct eis_connection *connection) { struct eis_client * client = eis_connection_get_client(connection); - client_disconnect(client, EIS_CONNECTION_DISCONNECT_REASON_DISCONNECTED, NULL); + + /* We need to drop the seats because that unrolls the EIS device state + * so that EIS gets the correct DEVICE_REMOVED sequence, etc. + * Then queue the DISCONNECTED event and wait for EIS to call + * eis_client_disconnect() + */ + client_drop_seats(client); + eis_queue_disconnect_event(client); + client->state = EIS_CLIENT_STATE_REQUESTED_DISCONNECT; return NULL; } @@ -339,6 +365,7 @@ static const struct eis_connection_interface *interfaces[] = { [EIS_CLIENT_STATE_NEW] = &intf_state_new, [EIS_CLIENT_STATE_CONNECTING] = &intf_state_connecting, [EIS_CLIENT_STATE_CONNECTED] = &intf_state_connected, + [EIS_CLIENT_STATE_REQUESTED_DISCONNECT] = NULL, [EIS_CLIENT_STATE_DISCONNECTED] = NULL, }; @@ -390,6 +417,7 @@ client_dispatch(struct source *source, void *userdata) "NEW", "CONNECTING", "CONNECTED", + "REQUESTED_DISCONNECT", "DISCONNECTED", }; if (result) diff --git a/src/libeis-client.h b/src/libeis-client.h index bc93479..d335a50 100644 --- a/src/libeis-client.h +++ b/src/libeis-client.h @@ -35,6 +35,7 @@ enum eis_client_state { EIS_CLIENT_STATE_NEW, /* socket just handed over */ EIS_CLIENT_STATE_CONNECTING, /* client completed setup but hasn't been accepted yet */ EIS_CLIENT_STATE_CONNECTED, /* caller has done eis_client_connect */ + EIS_CLIENT_STATE_REQUESTED_DISCONNECT, /* caller has disconnected */ EIS_CLIENT_STATE_DISCONNECTED, }; diff --git a/test/test_protocol.py b/test/test_protocol.py index d3cd0b8..7327601 100644 --- a/test/test_protocol.py +++ b/test/test_protocol.py @@ -476,16 +476,7 @@ class TestEiProtocol: ) for call in connection.calllog: - if call.name == "Disconnected": - assert call.args["explanation"] is None - assert ( - call.args["reason"] == EiConnection.EiDisconnectReason.DISCONNECTED - ) - break - else: - assert ( - False - ), f"Expected Disconnected event, got none in {connection.calllog}" + assert call.name != "Disconnected", "No disconnect event allowed here" try: ei.send(connection.Disconnect())