The code relied on spice_main_channel_update_display() called from the client application to create a list of monitor_configs to be sent to the server (to enable/disable monitors and set resolution). One of the disadvantages this approach has is that all data that you need in the list have to go through the client application. Thus adding any attribute to a monitor_config requires an API change. This commit changes this by creating and maintaining our own list of monitor_configs under the session object. The methods that are exposed to the client app no longer create new items in this list (strictly speaking, they never did, as the list was static and they only filled them up with values and enabled/disabled the items, which also has it's problems). Now the spice_main_channel_update_display{,_enabled}() functions only update existing items in the spice-gtk-maintained list. The identifier for these functions was unfortunately the index in the array on the spice-gtk side and channel_id + monitor_id in e.g. virt-viewer. Presumably other clients can use whatever they like as the id, but since the monitors sent in monitors_config on the main channel need to match those received in monitors_config on the display channels, they probably need to do basically the same. The code is making a lot of assumptions here. So, we make one more assumption that all clients use the channel_id + monitor_id to pass to spice_main_channel_update_display{,_enabled}() as the id argument and use that to look up the correct monitor_config. Signed-off-by: Lukáš Hrázký <lhrazky@xxxxxxxxxx> --- spice-common | 2 +- src/channel-display.c | 23 ++++++- src/channel-main.c | 139 ++++++++++++++++++++++++--------------- src/channel-main.h | 2 +- src/map-file | 5 ++ src/spice-glib-sym-file | 5 ++ src/spice-session-priv.h | 1 + src/spice-session.c | 129 ++++++++++++++++++++++++++++++++++++ src/spice-session.h | 30 +++++++++ 9 files changed, 278 insertions(+), 58 deletions(-) diff --git a/spice-common b/spice-common index 8096b12..11aeda6 160000 --- a/spice-common +++ b/spice-common @@ -1 +1 @@ -Subproject commit 8096b1206bb266b8d0b80b3e4c0d36fc621d772d +Subproject commit 11aeda6b04628258f0cb604727f6f800710a2d9e diff --git a/src/channel-display.c b/src/channel-display.c index e0520a3..9e83e43 100644 --- a/src/channel-display.c +++ b/src/channel-display.c @@ -1051,8 +1051,10 @@ static void spice_display_channel_up(SpiceChannel *channel) out->marshallers->msgc_display_init(out->marshaller, &init); spice_msg_out_send_internal(out); - /* notify of existence of this monitor */ - g_coroutine_object_notify(G_OBJECT(channel), "monitors"); + // if we don't have MONITORS_CONFIG_CAPABILITY, notify of existence of this monitor + if (!spice_channel_test_capability(channel, SPICE_DISPLAY_CAP_MONITORS_CONFIG)) { + g_coroutine_object_notify(G_OBJECT(channel), "monitors"); + } if (preferred_compression != SPICE_IMAGE_COMPRESSION_INVALID) { spice_display_channel_change_preferred_compression(channel, preferred_compression); @@ -1868,6 +1870,7 @@ static void display_handle_surface_destroy(SpiceChannel *channel, SpiceMsgIn *in /* coroutine context */ static void display_handle_monitors_config(SpiceChannel *channel, SpiceMsgIn *in) { + SpiceSession *session = spice_channel_get_session(channel); SpiceMsgDisplayMonitorsConfig *config = spice_msg_in_parsed(in); SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv; guint i; @@ -1906,6 +1909,22 @@ static void display_handle_monitors_config(SpiceChannel *channel, SpiceMsgIn *in mc->y = head->y; mc->width = head->width; mc->height = head->height; + + if (spice_session_find_monitor_config(session, channel->priv->channel_id, i)) { + spice_session_update_monitor_config(session, channel->priv->channel_id, i, + head->x, head->y, head->width, head->height); + } else { + spice_session_add_monitor_config(session, channel->priv->channel_id, i, + head->x, head->y, head->width, head->height); + } + } + + // On top of the above, create disabled configs for all monitors up to max_allowed + // if they don't exist. + for (i = config->count; i < config->max_allowed; i++) { + if (!spice_session_find_monitor_config(session, channel->priv->channel_id, i)) { + spice_session_add_monitor_config(session, channel->priv->channel_id, i, 0, 0, 0, 0); + } } g_coroutine_object_notify(G_OBJECT(channel), "monitors"); diff --git a/src/channel-main.c b/src/channel-main.c index 3d682d6..a35e9b9 100644 --- a/src/channel-main.c +++ b/src/channel-main.c @@ -55,20 +55,6 @@ typedef struct spice_migrate spice_migrate; -typedef enum { - DISPLAY_UNDEFINED, - DISPLAY_DISABLED, - DISPLAY_ENABLED, -} SpiceDisplayState; - -typedef struct { - int x; - int y; - int width; - int height; - SpiceDisplayState display_state; -} SpiceDisplayConfig; - typedef struct { GHashTable *xfer_task; SpiceMainChannel *channel; @@ -105,7 +91,6 @@ struct _SpiceMainChannelPrivate { guint agent_msg_pos; uint8_t agent_msg_size; uint32_t agent_caps[VD_AGENT_CAPS_SIZE]; - SpiceDisplayConfig display[MAX_DISPLAY]; gint timer_id; GQueue *agent_msg_queue; GHashTable *file_xfer_tasks; @@ -1115,13 +1100,17 @@ gboolean spice_main_channel_send_monitor_config(SpiceMainChannel *channel) c = channel->priv; g_return_val_if_fail(c->agent_connected, FALSE); + GArray *monitor_configs = spice_session_get_monitor_configs( + spice_channel_get_session(SPICE_CHANNEL(channel))); + if (spice_main_channel_agent_test_capability(channel, VD_AGENT_CAP_SPARSE_MONITORS_CONFIG)) { - monitors = SPICE_N_ELEMENTS(c->display); + monitors = monitor_configs->len; } else { monitors = 0; - for (i = 0; i < SPICE_N_ELEMENTS(c->display); i++) { - if (c->display[i].display_state == DISPLAY_ENABLED) + for (i = 0; i < monitor_configs->len; i++) { + if (g_array_index(monitor_configs, SpiceMonitorConfig, i).display_state == DISPLAY_ENABLED) { monitors += 1; + } } } @@ -1135,18 +1124,19 @@ gboolean spice_main_channel_send_monitor_config(SpiceMainChannel *channel) CHANNEL_DEBUG(channel, "sending new monitors config to guest"); j = 0; - for (i = 0; i < SPICE_N_ELEMENTS(c->display); i++) { - if (c->display[i].display_state != DISPLAY_ENABLED) { + for (i = 0; i < monitor_configs->len; i++) { + SpiceMonitorConfig *mc = &g_array_index(monitor_configs, SpiceMonitorConfig, i); + if (mc->display_state != DISPLAY_ENABLED) { if (spice_main_channel_agent_test_capability(channel, VD_AGENT_CAP_SPARSE_MONITORS_CONFIG)) j++; continue; } mon->monitors[j].depth = c->display_color_depth ? c->display_color_depth : 32; - mon->monitors[j].width = c->display[i].width; - mon->monitors[j].height = c->display[i].height; - mon->monitors[j].x = c->display[i].x; - mon->monitors[j].y = c->display[i].y; + mon->monitors[j].width = mc->width; + mon->monitors[j].height = mc->height; + mon->monitors[j].x = mc->x; + mon->monitors[j].y = mc->y; CHANNEL_DEBUG(channel, "monitor #%d: %ux%u+%d+%d @ %u bpp", j, mon->monitors[j].width, mon->monitors[j].height, mon->monitors[j].x, mon->monitors[j].y, @@ -1481,15 +1471,18 @@ static void agent_clipboard_release(SpiceMainChannel *channel, guint selection) static gboolean any_display_has_dimensions(SpiceMainChannel *channel) { - SpiceMainChannelPrivate *c; guint i; g_return_val_if_fail(SPICE_IS_MAIN_CHANNEL(channel), FALSE); - c = channel->priv; - for (i = 0; i < MAX_DISPLAY; i++) { - if (c->display[i].width > 0 && c->display[i].height > 0) + GArray *monitor_configs = spice_session_get_monitor_configs( + spice_channel_get_session(SPICE_CHANNEL(channel))); + + for (i = 0; i < monitor_configs->len; i++) { + SpiceMonitorConfig *mc = &g_array_index(monitor_configs, SpiceMonitorConfig, i); + if (mc->width > 0 && mc->height > 0) { return TRUE; + } } return FALSE; @@ -1513,12 +1506,19 @@ static gboolean timer_set_display(gpointer data) } session = spice_channel_get_session(SPICE_CHANNEL(channel)); + GArray *monitor_configs = spice_session_get_monitor_configs(session); if (!spice_main_channel_agent_test_capability(channel, VD_AGENT_CAP_SPARSE_MONITORS_CONFIG)) { /* ensure we have an explicit monitor configuration at least for number of display channels */ - for (i = 0; i < spice_session_get_n_display_channels(session); i++) - if (c->display[i].display_state == DISPLAY_UNDEFINED) { + + guint n_display_channels = spice_session_get_n_display_channels(session); + if (n_display_channels < monitor_configs->len) { + SPICE_DEBUG("Not sending monitors config, missing monitors"); + return FALSE; + } + for (i = 0; i < n_display_channels; i++) + if (g_array_index(monitor_configs, SpiceMonitorConfig, i).display_state == DISPLAY_UNDEFINED) { SPICE_DEBUG("Not sending monitors config, missing monitors"); return FALSE; } @@ -2630,7 +2630,7 @@ gboolean spice_main_channel_agent_test_capability(SpiceMainChannel *channel, gui /** * spice_main_update_display: * @channel: a #SpiceMainChannel - * @id: display ID + * @id: the display ID - assumed to be channel_id + monitor_id or equivalent * @x: x position * @y: y position * @width: display width @@ -2656,7 +2656,7 @@ void spice_main_update_display(SpiceMainChannel *channel, int id, /** * spice_main_channel_update_display: * @channel: a #SpiceMainChannel - * @id: display ID + * @id: the display ID - assumed to be channel_id + monitor_id or equivalent * @x: x position * @y: y position * @width: display width @@ -2675,8 +2675,6 @@ void spice_main_update_display(SpiceMainChannel *channel, int id, void spice_main_channel_update_display(SpiceMainChannel *channel, int id, int x, int y, int width, int height, gboolean update) { - SpiceMainChannelPrivate *c; - g_return_if_fail(channel != NULL); g_return_if_fail(SPICE_IS_MAIN_CHANNEL(channel)); g_return_if_fail(x >= 0); @@ -2684,19 +2682,32 @@ void spice_main_channel_update_display(SpiceMainChannel *channel, int id, int x, g_return_if_fail(width >= 0); g_return_if_fail(height >= 0); - c = SPICE_MAIN_CHANNEL(channel)->priv; - - g_return_if_fail(id < SPICE_N_ELEMENTS(c->display)); + GArray *monitor_configs = spice_session_get_monitor_configs( + spice_channel_get_session(SPICE_CHANNEL(channel))); - SpiceDisplayConfig display = { - .x = x, .y = y, .width = width, .height = height, - .display_state = c->display[id].display_state - }; + int found = 0; + SpiceMonitorConfig *mc = NULL; + for (size_t i = 0; i < monitor_configs->len; ++i) { + mc = &g_array_index(monitor_configs, SpiceMonitorConfig, i); + if ((mc->channel_id + mc->monitor_id) == id) { + found = 1; + break; + } + } - if (memcmp(&display, &c->display[id], sizeof(SpiceDisplayConfig)) == 0) + if (!found) { + g_warning("Display ID %d not found while trying to update the display (monitor config).", id); return; + } - c->display[id] = display; + CHANNEL_DEBUG(channel, + "Update display (monitor config) id: %d (channel_id: %u, monitor_id: %u), +%d+%d-%dx%d", + id, mc->channel_id, mc->monitor_id, x, y, width, height); + + mc->x = x; + mc->y = y; + mc->width = width; + mc->height = height; if (update) update_display_timer(channel, 1); @@ -2705,7 +2716,7 @@ void spice_main_channel_update_display(SpiceMainChannel *channel, int id, int x, /** * spice_main_set_display: * @channel: a #SpiceMainChannel - * @id: display ID + * @id: the display ID - assumed to be channel_id + monitor_id or equivalent * @x: x position * @y: y position * @width: display width @@ -2947,7 +2958,7 @@ void spice_main_channel_clipboard_selection_request(SpiceMainChannel *channel, g /** * spice_main_update_display_enabled: * @channel: a #SpiceMainChannel - * @id: display ID (if -1: set all displays) + * @id: the display ID - assumed to be channel_id + monitor_id or equivalent, -1 to update all * @enabled: wether display @id is enabled * @update: if %TRUE, update guest display state after 1sec. * @@ -2972,7 +2983,7 @@ void spice_main_update_display_enabled(SpiceMainChannel *channel, int id, gboole /** * spice_main_channel_update_display_enabled: * @channel: a #SpiceMainChannel - * @id: display ID (if -1: set all displays) + * @id: the display ID - assumed to be channel_id + monitor_id or equivalent, -1 to update all * @enabled: wether display @id is enabled * @update: if %TRUE, update guest display state after 1sec. * @@ -2990,23 +3001,43 @@ void spice_main_update_display_enabled(SpiceMainChannel *channel, int id, gboole void spice_main_channel_update_display_enabled(SpiceMainChannel *channel, int id, gboolean enabled, gboolean update) { - SpiceDisplayState display_state = enabled ? DISPLAY_ENABLED : DISPLAY_DISABLED; g_return_if_fail(channel != NULL); g_return_if_fail(SPICE_IS_MAIN_CHANNEL(channel)); g_return_if_fail(id >= -1); - SpiceMainChannelPrivate *c = channel->priv; + GArray *monitor_configs = spice_session_get_monitor_configs( + spice_channel_get_session(SPICE_CHANNEL(channel))); + + SpiceDisplayState display_state = enabled ? DISPLAY_ENABLED : DISPLAY_DISABLED; if (id == -1) { + SPICE_DEBUG("Updating all monitor configs' state to %s", enabled ? "enabled" : "disabled"); + gint i; - for (i = 0; i < G_N_ELEMENTS(c->display); i++) { - c->display[i].display_state = display_state; + for (i = 0; i < monitor_configs->len; i++) { + g_array_index(monitor_configs, SpiceMonitorConfig, i).display_state = display_state; } } else { - g_return_if_fail(id < G_N_ELEMENTS(c->display)); - if (c->display[id].display_state == display_state) + int found = 0; + SpiceMonitorConfig *mc = NULL; + for (size_t i = 0; i < monitor_configs->len; ++i) { + mc = &g_array_index(monitor_configs, SpiceMonitorConfig, i); + if ((mc->channel_id + mc->monitor_id) == id) { + found = 1; + break; + } + } + + if (!found) { + g_warning("Display ID %d not found while trying to %s the display (monitor config).", + id, enabled ? "enable" : "disable"); return; - c->display[id].display_state = display_state; + } + + SPICE_DEBUG("Updating monitor config id: %d (channel_id: %u, monitor_id: %u) state to %s", + id, mc->channel_id, mc->monitor_id, enabled ? "enabled" : "disabled"); + + mc->display_state = display_state; } if (update) @@ -3016,7 +3047,7 @@ void spice_main_channel_update_display_enabled(SpiceMainChannel *channel, int id /** * spice_main_set_display_enabled: * @channel: a #SpiceMainChannel - * @id: display ID (if -1: set all displays) + * @id: the display ID - assumed to be channel_id + monitor_id or equivalent, -1 to update all * @enabled: wether display @id is enabled * * When sending monitor configuration to agent guest, don't set diff --git a/src/channel-main.h b/src/channel-main.h index 530378d..bbc2fea 100644 --- a/src/channel-main.h +++ b/src/channel-main.h @@ -112,7 +112,7 @@ G_DEPRECATED_FOR(spice_main_channel_clipboard_selection_request) void spice_main_clipboard_request(SpiceMainChannel *channel, guint32 type); G_DEPRECATED_FOR(spice_main_channel_update_display) -void spice_main_set_display(SpiceMainChannel *channel, int id,int x, int y, int width, int height); +void spice_main_set_display(SpiceMainChannel *channel, int id, int x, int y, int width, int height); G_DEPRECATED_FOR(spice_main_update_display) void spice_main_update_display(SpiceMainChannel *channel, int id, int x, int y, int width, int height, gboolean update); diff --git a/src/map-file b/src/map-file index cdb81c3..72ee2f3 100644 --- a/src/map-file +++ b/src/map-file @@ -120,9 +120,13 @@ spice_port_write_finish; spice_record_channel_get_type; spice_record_channel_send_data; spice_record_send_data; +spice_session_add_monitor_config; spice_session_connect; spice_session_disconnect; +spice_display_state_get_type; +spice_session_find_monitor_config; spice_session_get_channels; +spice_session_get_monitor_configs; spice_session_get_proxy_uri; spice_session_get_read_only; spice_session_get_type; @@ -131,6 +135,7 @@ spice_session_is_for_migration; spice_session_migration_get_type; spice_session_new; spice_session_open_fd; +spice_session_update_monitor_config; spice_session_verify_get_type; spice_set_session_option; spice_smartcard_channel_get_type; diff --git a/src/spice-glib-sym-file b/src/spice-glib-sym-file index b19844c..c186624 100644 --- a/src/spice-glib-sym-file +++ b/src/spice-glib-sym-file @@ -99,9 +99,13 @@ spice_port_write_finish spice_record_channel_get_type spice_record_channel_send_data spice_record_send_data +spice_session_add_monitor_config spice_session_connect spice_session_disconnect +spice_display_state_get_type +spice_session_find_monitor_config spice_session_get_channels +spice_session_get_monitor_configs spice_session_get_proxy_uri spice_session_get_read_only spice_session_get_type @@ -110,6 +114,7 @@ spice_session_is_for_migration spice_session_migration_get_type spice_session_new spice_session_open_fd +spice_session_update_monitor_config spice_session_verify_get_type spice_set_session_option spice_smartcard_channel_get_type diff --git a/src/spice-session-priv.h b/src/spice-session-priv.h index 03005aa..12b0764 100644 --- a/src/spice-session-priv.h +++ b/src/spice-session-priv.h @@ -100,6 +100,7 @@ void spice_session_set_main_channel(SpiceSession *session, SpiceChannel *channel gboolean spice_session_set_migration_session(SpiceSession *session, SpiceSession *mig_session); SpiceAudio *spice_audio_get(SpiceSession *session, GMainContext *context); const gchar* spice_audio_data_mode_to_string(gint mode); + G_END_DECLS #endif /* __SPICE_CLIENT_SESSION_PRIV_H__ */ diff --git a/src/spice-session.c b/src/spice-session.c index 57acc63..c170110 100644 --- a/src/spice-session.c +++ b/src/spice-session.c @@ -90,6 +90,14 @@ struct _SpiceSessionPrivate { GStrv secure_channels; gint color_depth; + /* A list of monitor configs for all channels and monitors. It is filled up + * as display channels are created from their monitors_config messages. The + * client application then updates these through + * spice_main_channel_update_display{,_enabled} to enable/disable them and + * set the resolution. + */ + GArray *monitor_configs; + int connection_id; int protocol; SpiceChannel *cmain; /* weak reference */ @@ -294,6 +302,8 @@ static void spice_session_init(SpiceSession *session) s->glz_window = glz_decoder_window_new(); update_proxy(session, NULL); + s->monitor_configs = g_array_new(FALSE, TRUE, sizeof(SpiceMonitorConfig)); + s->usb_manager = spice_usb_device_manager_get(session, &err); if (err != NULL) { SPICE_DEBUG("Could not initialize SpiceUsbDeviceManager - %s", err->message); @@ -349,6 +359,8 @@ spice_session_dispose(GObject *gobject) g_clear_object(&s->proxy); g_clear_object(&s->webdav); + g_array_free(s->monitor_configs, TRUE); + /* Chain up to the parent class */ if (G_OBJECT_CLASS(spice_session_parent_class)->dispose) G_OBJECT_CLASS(spice_session_parent_class)->dispose(gobject); @@ -2800,6 +2812,123 @@ gboolean spice_session_is_for_migration(SpiceSession *session) return session->priv->for_migration; } +/** + * spice_session_get_monitor_configs: + * @session: a #SpiceSession + * + * Returns: a pointer to the array of SpiceMonitorConfigs + * Since: 0.36 + **/ +GArray *spice_session_get_monitor_configs(SpiceSession *session) +{ + g_assert(session != NULL); + + return session->priv->monitor_configs; +} + +/** + * spice_session_find_monitor_config: + * @session: a #SpiceSession + * @channel_id: a channel ID + * @monitor_id: a monitor ID + * + * Looks up a monitor config which has the channel_id and monitor_id equal to + * those specified in aruments. + * + * Returns: a pointer to SpiceMonitorConfig if found, %NULL otherwise + * Since: 0.36 + **/ +SpiceMonitorConfig *spice_session_find_monitor_config(SpiceSession *session, + uint32_t channel_id, + uint32_t monitor_id) +{ + GArray *monitor_configs = spice_session_get_monitor_configs(session); + g_assert(monitor_configs != NULL); + + for (size_t i = 0; i < monitor_configs->len; ++i) { + SpiceMonitorConfig *d = &g_array_index(monitor_configs, SpiceMonitorConfig, i); + if (d->channel_id == channel_id && d->monitor_id == monitor_id) { + return d; + } + } + + return NULL; +} + +/** + * spice_session_add_monitor_config: + * @session: a #SpiceSession + * @channel_id: a channel ID + * @monitor_id: a monitor ID + * @x: X coordinate of the monitor + * @y: Y coordinate of the monitor + * @width: width of the monitor + * @height: height of the monitor + * + * Appends a new #SpiceMonitorConfig to the end of monitor_configs array under + * the session and sets its attributes to those specified in arguments. + * + * Since: 0.36 + **/ +void spice_session_add_monitor_config(SpiceSession *session, uint32_t channel_id, uint32_t monitor_id, + int x, int y, int width, int height) +{ + GArray *monitor_configs = spice_session_get_monitor_configs(session); + g_assert(monitor_configs != NULL); + + g_return_if_fail(x >= 0); + g_return_if_fail(y >= 0); + g_return_if_fail(width >= 0); + g_return_if_fail(height >= 0); + + SpiceMonitorConfig mc = { + .channel_id = channel_id, + .monitor_id = monitor_id, + .x = x, + .y = y, + .width = width, + .height = height, + .display_state = DISPLAY_DISABLED + }; + + SPICE_DEBUG("Adding new monitor_config idx=%u +%d+%d:%dx%d", + monitor_configs->len, x, y, width, height); + + g_array_append_val(monitor_configs, mc); +} + +/** + * spice_session_update_monitor_config: + * @session: a #SpiceSession + * @channel_id: a channel ID + * @monitor_id: a monitor ID + * @x: X coordinate of the monitor + * @y: Y coordinate of the monitor + * @width: width of the monitor + * @height: height of the monitor + * + * Looks up #SpiceMonitorConfig with channel_id and monitor_id equal to those + * in arguments in the session's monitor_configs array and sets its x, y, width + * and height to those specified in arguments. + * + * Since: 0.36 + **/ +void spice_session_update_monitor_config(SpiceSession *session, uint32_t channel_id, uint32_t monitor_id, + int x, int y, int width, int height) +{ + SpiceMonitorConfig *mc = spice_session_find_monitor_config(session, channel_id, monitor_id); + + g_return_if_fail(mc != NULL); + + SPICE_DEBUG("Updating monitor config channel_id: %u, monitor_id: %u, +%d+%d-%dx%d", + channel_id, monitor_id, x, y, width, height); + + mc->x = x; + mc->y = y; + mc->width = width; + mc->height = height; +} + G_GNUC_INTERNAL void spice_session_set_main_channel(SpiceSession *session, SpiceChannel *channel) { diff --git a/src/spice-session.h b/src/spice-session.h index cfe02b1..6d97ab2 100644 --- a/src/spice-session.h +++ b/src/spice-session.h @@ -23,6 +23,8 @@ #endif #include <glib-object.h> +#include <stdint.h> + #include "spice-types.h" #include "spice-uri.h" #include "spice-glib-enums.h" @@ -103,6 +105,22 @@ struct _SpiceSessionClass gchar _spice_reserved[SPICE_RESERVED_PADDING]; }; +typedef enum { + DISPLAY_UNDEFINED, + DISPLAY_DISABLED, + DISPLAY_ENABLED, +} SpiceDisplayState; + +typedef struct { + uint32_t channel_id; + uint32_t monitor_id; + int x; + int y; + int width; + int height; + SpiceDisplayState display_state; +} SpiceMonitorConfig; + GType spice_session_get_type(void); SpiceSession *spice_session_new(void); @@ -115,6 +133,18 @@ gboolean spice_session_get_read_only(SpiceSession *session); SpiceURI *spice_session_get_proxy_uri(SpiceSession *session); gboolean spice_session_is_for_migration(SpiceSession *session); +GArray *spice_session_get_monitor_configs(SpiceSession *session); + +SpiceMonitorConfig *spice_session_find_monitor_config(SpiceSession *session, + uint32_t channel_id, + uint32_t monitor_id); + +void spice_session_add_monitor_config(SpiceSession *session, uint32_t channel_id, uint32_t display_id, + int x, int y, int width, int height); + +void spice_session_update_monitor_config(SpiceSession *session, uint32_t channel_id, uint32_t monitor_id, + int x, int y, int width, int height); + G_END_DECLS #endif /* __SPICE_CLIENT_SESSION_H__ */ -- 2.17.1 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel