From 835cbbafba69d991003fb33a6842e5962bc61916 Mon Sep 17 00:00:00 2001 From: mikael-lovqvists-claude-agent Date: Sun, 29 Mar 2026 19:48:22 +0000 Subject: [PATCH] Fix connect accumulation; add display sinks to enum-devices - controller_cli: drain semaphore and reset pending_cmd in do_connect so stale posts from old connection don't unblock the next command - protocol: add Proto_Display_Device_Info; extend proto_write_enum_devices_response and proto_read_enum_devices_response with display section; backward-compatible (absent in older messages) - node: handle_enum_devices snapshots active Display_Slots under mutex and includes them in the response - controller_cli: on_display callback prints display window info in enum-devices output - query_cli: updated to pass NULL on_display (no display interest) Co-Authored-By: Claude Sonnet 4.6 --- dev/cli/controller_cli.c | 30 +++++++++++++++++++- dev/cli/query_cli.c | 2 +- include/protocol.h | 21 +++++++++++++- src/modules/protocol/protocol.c | 49 ++++++++++++++++++++++++++++++++- src/node/main.c | 23 +++++++++++++++- 5 files changed, 120 insertions(+), 5 deletions(-) diff --git a/dev/cli/controller_cli.c b/dev/cli/controller_cli.c index 8b526da..0a31ae0 100644 --- a/dev/cli/controller_cli.c +++ b/dev/cli/controller_cli.c @@ -158,6 +158,31 @@ static void on_standalone( (int)name_len, name); } +static const char *scale_name(uint8_t s) +{ + switch (s) { + case 0: return "stretch"; + case 1: return "fit"; + case 2: return "fill"; + case 3: return "1:1"; + default: return "?"; + } +} + +static void on_display( + uint16_t stream_id, + int16_t win_x, int16_t win_y, + uint16_t win_w, uint16_t win_h, + uint8_t scale, uint8_t anchor, + void *ud) +{ + (void)ud; + printf(" display stream=%u pos=%d,%d size=%ux%u scale=%s anchor=%s\n", + stream_id, win_x, win_y, win_w, win_h, + scale_name(scale), + anchor == 0 ? "center" : "topleft"); +} + static void on_control( uint32_t id, uint8_t type, uint32_t flags, const char *name, uint8_t name_len, @@ -208,7 +233,7 @@ static void on_frame(struct Transport_Conn *conn, struct Proto_Response_Header hdr; struct App_Error e = proto_read_enum_devices_response( frame->payload, frame->payload_length, &hdr, - on_media_device, on_video_node, on_standalone, NULL); + on_media_device, on_video_node, on_standalone, on_display, NULL); if (!APP_IS_OK(e)) { app_error_print(&e); } else if (hdr.status != PROTO_STATUS_OK) { fprintf(stderr, "ENUM_DEVICES: status=%u\n", hdr.status); @@ -452,6 +477,9 @@ static struct Transport_Conn *do_connect(struct Ctrl_State *cs, struct Transport_Conn *old_conn) { if (old_conn) { transport_conn_close(old_conn); } + /* Reset state — drain stale semaphore posts from the old connection */ + cs->pending_cmd = 0; + while (sem_trywait(&cs->sem) == 0) { /* drain */ } struct Transport_Conn *conn; struct App_Error e = transport_connect(&conn, host, port, TRANSPORT_DEFAULT_MAX_PAYLOAD, on_frame, on_disconnect, cs); diff --git a/dev/cli/query_cli.c b/dev/cli/query_cli.c index 9bb81be..274fff3 100644 --- a/dev/cli/query_cli.c +++ b/dev/cli/query_cli.c @@ -195,7 +195,7 @@ static void on_frame(struct Transport_Conn *conn, struct Proto_Response_Header hdr; struct App_Error e = proto_read_enum_devices_response( frame->payload, frame->payload_length, &hdr, - on_media_device, on_video_node, on_standalone, NULL); + on_media_device, on_video_node, on_standalone, NULL, NULL); if (!APP_IS_OK(e)) { app_error_print(&e); } else if (hdr.status != PROTO_STATUS_OK) { fprintf(stderr, "ENUM_DEVICES failed: status=%u\n", hdr.status); diff --git a/include/protocol.h b/include/protocol.h index 0d0f4d2..abe562f 100644 --- a/include/protocol.h +++ b/include/protocol.h @@ -147,6 +147,18 @@ struct Proto_Standalone_Device_Info { const char *name; }; +/* + * An active display window (video sink role). + * stream_id is the stream being displayed; win_* are current geometry. + */ +struct Proto_Display_Device_Info { + uint16_t stream_id; + int16_t win_x, win_y; + uint16_t win_w, win_h; + uint8_t scale; + uint8_t anchor; +}; + struct Proto_Monitor_Info { int32_t x, y; uint32_t width, height; @@ -362,7 +374,8 @@ struct App_Error proto_write_control_response(struct Transport_Conn *conn, struct App_Error proto_write_enum_devices_response(struct Transport_Conn *conn, uint16_t request_id, uint16_t status, const struct Proto_Media_Device_Info *media_devices, uint16_t media_count, - const struct Proto_Standalone_Device_Info *standalone, uint16_t standalone_count); + const struct Proto_Standalone_Device_Info *standalone, uint16_t standalone_count, + const struct Proto_Display_Device_Info *displays, uint16_t display_count); /* CONTROL_RESPONSE: ENUM_CONTROLS */ struct App_Error proto_write_enum_controls_response(struct Transport_Conn *conn, @@ -481,6 +494,12 @@ struct App_Error proto_read_enum_devices_response( const char *path, uint8_t path_len, const char *name, uint8_t name_len, void *userdata), + void (*on_display)( + uint16_t stream_id, + int16_t win_x, int16_t win_y, + uint16_t win_w, uint16_t win_h, + uint8_t scale, uint8_t anchor, + void *userdata), void *userdata); /* diff --git a/src/modules/protocol/protocol.c b/src/modules/protocol/protocol.c index b53fefd..d69f46b 100644 --- a/src/modules/protocol/protocol.c +++ b/src/modules/protocol/protocol.c @@ -51,6 +51,14 @@ static struct App_Error wbuf_u16(struct Wbuf *b, uint16_t v) { return APP_OK; } +static struct App_Error wbuf_i16(struct Wbuf *b, int16_t v) { + struct App_Error e = wbuf_grow(b, 2); + if (!APP_IS_OK(e)) { return e; } + put_i16(b->data, b->len, v); + b->len += 2; + return APP_OK; +} + static struct App_Error wbuf_u32(struct Wbuf *b, uint32_t v) { struct App_Error e = wbuf_grow(b, 4); if (!APP_IS_OK(e)) { return e; } @@ -366,7 +374,8 @@ struct App_Error proto_write_get_control_response(struct Transport_Conn *conn, struct App_Error proto_write_enum_devices_response(struct Transport_Conn *conn, uint16_t request_id, uint16_t status, const struct Proto_Media_Device_Info *media_devices, uint16_t media_count, - const struct Proto_Standalone_Device_Info *standalone, uint16_t standalone_count) + const struct Proto_Standalone_Device_Info *standalone, uint16_t standalone_count, + const struct Proto_Display_Device_Info *displays, uint16_t display_count) { struct Wbuf b; struct App_Error e = wbuf_init(&b, 128); @@ -402,6 +411,18 @@ struct App_Error proto_write_enum_devices_response(struct Transport_Conn *conn, e = wbuf_str8(&b, standalone[i].name); if (!APP_IS_OK(e)) { goto fail; } } + e = wbuf_u16(&b, display_count); if (!APP_IS_OK(e)) { goto fail; } + for (uint16_t i = 0; i < display_count; i++) { + const struct Proto_Display_Device_Info *d = &displays[i]; + e = wbuf_u16(&b, d->stream_id); if (!APP_IS_OK(e)) { goto fail; } + e = wbuf_i16(&b, d->win_x); if (!APP_IS_OK(e)) { goto fail; } + e = wbuf_i16(&b, d->win_y); if (!APP_IS_OK(e)) { goto fail; } + e = wbuf_u16(&b, d->win_w); if (!APP_IS_OK(e)) { goto fail; } + e = wbuf_u16(&b, d->win_h); if (!APP_IS_OK(e)) { goto fail; } + e = wbuf_u8 (&b, d->scale); if (!APP_IS_OK(e)) { goto fail; } + e = wbuf_u8 (&b, d->anchor); if (!APP_IS_OK(e)) { goto fail; } + } + e = transport_send_frame(conn, PROTO_MSG_CONTROL_RESPONSE, b.data, b.len); fail: wbuf_free(&b); @@ -705,6 +726,12 @@ struct App_Error proto_read_enum_devices_response( const char *path, uint8_t path_len, const char *name, uint8_t name_len, void *userdata), + void (*on_display)( + uint16_t stream_id, + int16_t win_x, int16_t win_y, + uint16_t win_w, uint16_t win_h, + uint8_t scale, uint8_t anchor, + void *userdata), void *userdata) { struct Cursor c; @@ -759,6 +786,26 @@ struct App_Error proto_read_enum_devices_response( if (on_standalone) { on_standalone(path, path_len, name, name_len, userdata); } } + /* Display section — optional; absent in messages from older nodes */ + if (c.ok && c.pos + 2 <= c.len) { + uint16_t display_count = cur_u16(&c); + CUR_CHECK(c); + for (uint16_t i = 0; i < display_count; i++) { + uint16_t stream_id = cur_u16(&c); + int16_t win_x = (int16_t)cur_u16(&c); + int16_t win_y = (int16_t)cur_u16(&c); + uint16_t win_w = cur_u16(&c); + uint16_t win_h = cur_u16(&c); + uint8_t scale = cur_u8(&c); + uint8_t anchor = cur_u8(&c); + CUR_CHECK(c); + if (on_display) { + on_display(stream_id, win_x, win_y, + win_w, win_h, scale, anchor, userdata); + } + } + } + return APP_OK; } diff --git a/src/node/main.c b/src/node/main.c index e300f12..5af3cfe 100644 --- a/src/node/main.c +++ b/src/node/main.c @@ -829,10 +829,31 @@ static void handle_enum_devices(struct Node *node, standalone_count++; } + struct Proto_Display_Device_Info disp_infos[MAX_DISPLAYS]; + int disp_count = 0; + for (int i = 0; i < MAX_DISPLAYS; i++) { + struct Display_Slot *d = &node->displays[i]; + pthread_mutex_lock(&d->mutex); + int snap = d->allocated && d->wanted_state == DISP_OPEN; + struct Proto_Display_Device_Info info = { + .stream_id = d->stream_id, + .win_x = (int16_t)d->win_x, + .win_y = (int16_t)d->win_y, + .win_w = (uint16_t)d->win_w, + .win_h = (uint16_t)d->win_h, + .scale = (uint8_t)d->scale, + .anchor = (uint8_t)d->anchor, + }; + pthread_mutex_unlock(&d->mutex); + if (!snap) { continue; } + disp_infos[disp_count++] = info; + } + struct App_Error e = proto_write_enum_devices_response(conn, request_id, PROTO_STATUS_OK, mdevs, (uint16_t)node->devices.media_count, - standalone, (uint16_t)standalone_count); + standalone, (uint16_t)standalone_count, + disp_infos, (uint16_t)disp_count); if (!APP_IS_OK(e)) { app_error_print(&e); } }