[Spice-devel] [RFC PATCH spice v2 11/20] Handle the MainMonitorsConfig message from the client
Lukáš Hrázký
lhrazky at redhat.com
Thu Aug 16 16:26:40 UTC 2018
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 at redhat.com>
---
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
More information about the Spice-devel
mailing list