[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