The MainMonitorsConfig message is a replacement for the VDAgentMonitorsConfig message, that contains the unique ID pair (channel_id, monitor_id) to identify the monitors to which the configuration belongs. Once received, the (channel_id, monitor_id) pair is used to retrieve the guest_output_ids from the monitors_guest_output_id hash table. If we have no guest_output_ids (and the QXL monitors_config interface is present), we can use the QXL monitors_config interface to apply the monitors_config. If we have guest_output_ids, we need to forward those to the vd_agent which, in the guest context, can apply the monitors_configs. For this we use VDAgentMonitorsConfigV2, a version 2 of the VDAgentMonitorsConfig message which contains the guest_output_id for the monitors. Note that if we are sending monitors_config to the vd_agent and we don't have any guest_output_ids, we still set the output_ids for the vd_agent according to the old "channel_id ? channel_id : monitor_id" formula. This way, the vd_agent code always get's it's IDs in the message and needs no logic for it. All the logic stays in the server. Signed-off-by: Lukáš Hrázký <lhrazky@xxxxxxxxxx> --- server/main-channel.c | 3 + server/reds.c | 151 +++++++++++++++++++++++++++++++++++++++ server/reds.h | 3 + subprojects/spice-common | 2 +- 4 files changed, 158 insertions(+), 1 deletion(-) diff --git a/server/main-channel.c b/server/main-channel.c index f866fb4a..7b70e793 100644 --- a/server/main-channel.c +++ b/server/main-channel.c @@ -214,6 +214,9 @@ static bool main_channel_handle_message(RedChannelClient *rcc, uint16_t type, case SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST: reds_on_main_mouse_mode_request(reds, message, size); break; + case SPICE_MSGC_MAIN_MONITORS_CONFIG: + reds_on_main_monitors_config(reds, (SpiceMsgcMainMonitorsConfig *) message, size); + break; case SPICE_MSGC_PONG: main_channel_client_handle_pong(mcc, (SpiceMsgPing *)message, size); break; diff --git a/server/reds.c b/server/reds.c index d4356807..663a52f4 100644 --- a/server/reds.c +++ b/server/reds.c @@ -258,6 +258,7 @@ typedef struct __attribute__ ((__packed__)) VDInternalBuf { VDAgentMessage header; union { VDAgentMouseState mouse_state; + VDAgentMonitorsConfigV2 monitors_config; } u; } VDInternalBuf; @@ -1261,6 +1262,156 @@ void reds_on_main_mouse_mode_request(RedsState *reds, void *message, size_t size } } +static void reds_forward_monitors_config_to_agent(RedsState *reds, + SpiceMsgcMainMonitorsConfig *monitors_config, + size_t output_ids_count) +{ + // In case we have guest_output_ids, we send only those monitors_configs + // that have them set. The reason is there may be possible duplicities for + // MCs with and without guest_output_id (e.g. a QXL device that is also + // being streamed) and neither here nor in the vd_agent we have a way to + // identify the duplicity. + // + // We may also have no guest_output_ids but a qxl driver that doesn't + // support monitors_config, for that case we send all the monitors_configs. + size_t final_count = output_ids_count ? output_ids_count : monitors_config->count; + + size_t mc_msg_size = sizeof(VDAgentMonitorsConfigV2) + + final_count * sizeof(VDAgentMonConfigV2); + + size_t total_msg_size = sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + mc_msg_size; + + RedCharDeviceWriteBuffer *char_dev_buf = red_char_device_write_buffer_get_server_no_token( + RED_CHAR_DEVICE(reds->agent_dev), total_msg_size); + + char_dev_buf->buf_used = total_msg_size; + + VDInternalBuf *internal_buf = (VDInternalBuf *) char_dev_buf->buf; + internal_buf->chunk_header.port = VDP_SERVER_PORT; + internal_buf->chunk_header.size = sizeof(VDAgentMessage) + mc_msg_size; + internal_buf->header.protocol = VD_AGENT_PROTOCOL; + internal_buf->header.type = VD_AGENT_MONITORS_CONFIG; + internal_buf->header.opaque = 0; + internal_buf->header.size = mc_msg_size; + + VDAgentMonitorsConfigV2 *vda_mc = (VDAgentMonitorsConfigV2 *) &internal_buf->u; + + vda_mc->num_of_monitors = final_count; + vda_mc->flags = 0; + + spice_debug("Forwarding monitors config to vd_agent:"); + + size_t j = 0; + for (size_t i = 0; i < monitors_config->count; ++i) { + SpiceMainHead *head = &monitors_config->heads[i]; + + // If we don't have guest_output_ids, use the original guest_output_id + // calculation, assuming there is either: + // - only one display channel with multiple monitors + // - multiple display channels each with only one monitor + vda_mc->monitors[j].guest_output_id = head->channel_id ? head->channel_id : head->monitor_id; + + // If we're sending monitors_configs with the guest_output_ids set, + // look it up and if we don't find one, skip this MC (as we only send + // MCs that have it) + if (output_ids_count && !reds_get_monitor_guest_output_id(reds, + head->channel_id, + head->monitor_id, + &vda_mc->monitors[j].guest_output_id)) { + spice_debug( + " guest_output_id not found for channel_id=%u, monitor_id=%u, skipping", + head->channel_id, + head->monitor_id); + + continue; + } + + vda_mc->monitors[j].width = head->width; + vda_mc->monitors[j].height = head->height; + vda_mc->monitors[j].depth = head->depth; + vda_mc->monitors[j].x = head->x; + vda_mc->monitors[j].y = head->y; + + spice_debug(" monitor config (channel_id %u, monitor_id %u): guest_output_id %d, +%d+%d:%dx%d", + head->channel_id, + head->monitor_id, + vda_mc->monitors[j].guest_output_id, + vda_mc->monitors[j].x, + vda_mc->monitors[j].y, + vda_mc->monitors[j].width, + vda_mc->monitors[j].height); + + ++j; + } + + red_char_device_write_buffer_add(RED_CHAR_DEVICE(reds->agent_dev), char_dev_buf); +} + +static void reds_send_monitors_config_to_qxl(RedsState *reds, + SpiceMsgcMainMonitorsConfig *monitors_config) +{ + VDAgentMonitorsConfig *v1_mc = (VDAgentMonitorsConfig *) g_malloc(sizeof(VDAgentMonitorsConfig) + + monitors_config->count * sizeof(VDAgentMonConfig)); + + v1_mc->num_of_monitors = monitors_config->count; + v1_mc->flags = 0; + + spice_debug("Configuring monitors through QXL:"); + + for (size_t i = 0; i < monitors_config->count; ++i) { + SpiceMainHead *head = &monitors_config->heads[i]; + + v1_mc->monitors[i].height = head->height; + v1_mc->monitors[i].width = head->width; + v1_mc->monitors[i].depth = head->depth; + v1_mc->monitors[i].x = head->x; + v1_mc->monitors[i].y = head->y; + + spice_debug(" monitor config (channel_id %u, monitor_id %u): +%d+%d:%dx%d", + head->channel_id, + head->monitor_id, + v1_mc->monitors[i].x, + v1_mc->monitors[i].y, + v1_mc->monitors[i].width, + v1_mc->monitors[i].height); + } + + reds_client_monitors_config(reds, v1_mc); + + g_free(v1_mc); +} + +void reds_on_main_monitors_config(RedsState *reds, + SpiceMsgcMainMonitorsConfig *monitors_config, + uint32_t size) +{ + // Find out if we have any guest_output_ids and how many of them. If we + // have any, we can't use the QXL monitors_config interface. We want to + // forward only those monitors_configs that have the guest_output_ids to + // the vd_agent. + size_t output_ids_count = 0; + for (size_t i = 0; i < monitors_config->count; ++i) { + SpiceMainHead *head = &monitors_config->heads[i]; + + uint32_t dummy_output_id = 0; + + if (reds_get_monitor_guest_output_id(reds, + head->channel_id, + head->monitor_id, + &dummy_output_id)) { + ++output_ids_count; + } + } + + // Use QXL if we can. If not, forward the monitors_configs to the vd_agent. + if (!output_ids_count && reds->agent_dev->priv->write_filter.use_client_monitors_config) { + reds_send_monitors_config_to_qxl(reds, monitors_config); + } else { + reds_forward_monitors_config_to_agent(reds, monitors_config, output_ids_count); + // TODO for an agent that doesn't support V2, also pass V1 of the message (requires a capability) + } +} + /* * Push partial agent data, even if not all the chunk was consumend, * in order to avoid the roundtrip (src-server->client->dest-server) diff --git a/server/reds.h b/server/reds.h index 1f10c4b3..a3922375 100644 --- a/server/reds.h +++ b/server/reds.h @@ -88,6 +88,9 @@ void reds_on_main_migrate_connected(RedsState *reds, int seamless); //should be bool reds_handle_migrate_data(RedsState *reds, MainChannelClient *mcc, SpiceMigrateDataMain *mig_data, uint32_t size); void reds_on_main_mouse_mode_request(RedsState *reds, void *message, size_t size); +void reds_on_main_monitors_config(RedsState *reds, + SpiceMsgcMainMonitorsConfig *monitors_config, + uint32_t size); /* migration dest side: returns whether it can support seamless migration * with the given src migration protocol version */ int reds_on_migrate_dst_set_seamless(RedsState *reds, MainChannelClient *mcc, uint32_t src_version); diff --git a/subprojects/spice-common b/subprojects/spice-common index f82a6c53..0af835f1 160000 --- a/subprojects/spice-common +++ b/subprojects/spice-common @@ -1 +1 @@ -Subproject commit f82a6c5349a9a71485910bd3a57fe588c49d74f8 +Subproject commit 0af835f10e4dd51773dbeeb784fda4364b745874 -- 2.18.0 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel