On Fri, 2018-06-22 at 12:05 +0200, Christophe de Dinechin wrote: > > On 20 Jun 2018, at 11:45, Lukáš Hrázký <lhra...@redhat.com> wrote: > > > > On Tue, 2018-06-19 at 17:06 +0200, Christophe de Dinechin wrote: > > > Hi Lukas, > > > > > > > > > Please don’t be disturbed by the many comments. Overall, looks like > > > really good progress, and insightful analysis of the problem. Probably > > > will require several more iterations, but that’s entirely expected. > > > Thanks a lot. > > > > No worries, thanks for the review! > > > > > > On 5 Jun 2018, at 17:30, Lukáš Hrázký <lhra...@redhat.com> wrote: > > > > > > > > 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. > > > > > > I have trouble following the reasoning. It’s only adding a client-visible > > > attribute that requires an API change, right? Is the output_id > > > necessarily visible to the client, or could that be kept internal inside > > > the SPICE API? > > > > I'm also having a hard time understanding what exactly you don't > > understand or find confusing from the text :) > > The part I find confusing is the “thus adding… requires”. It seems that you > believe if follows from the previous sentence. And it does follow iff the > output_id needs to be visible. But since the output_id is not visible today > (because it just does not exist), therefore it seems it’s not needed…
Hmm... The output_id does not need to be visible. But before this patch, all monitors_config fields had to be visible by design, which is what the first sentence of the paragraph in question is trying to say. > But if I’m the only one confused, I’ll shut up. > > > You're right, the practical problem is I don't want to make the > > output_id visible to the client app (I use "client app" = e.g. virt- > > viewer, "client" could mean either virt-viewer or spice-gtk) and have > > to change the API. > > > > The underlying reason for this problem is that items to the > > monitors_config list are added when the client app calls the > > spice_main_channel_update_display() API method, with all the data for > > the new item in arguments. A better approach (the more "standard" one, > > IMO) is to add items to the list inside spice-gtk and only use the API > > method to update the items. > > > > So that's what I did and I tried to describe that in the text. I have > > no problem fixing the description, I'm just not sure how... > > > > > So I think the feeling is right (you want to hide some internal details) > > > but the way it’s spelled out does not sound right to me. > > > > > > > > > > > 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ý <lhra...@redhat.com> > > > > --- > > > > 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. > > > > > > What is the point of doing this? Why not add code when enabling a monitor > > > that adds it if not present? > > > > > > Also, as implemented, it’s O(N^2) since there is a loop in > > > find_monitor_config. > > > > > > Or if you want a dense array, then you don’t need a GArray, you may as > > > well pre-allocate and use a two-dimentional array indexed by channel_id > > > and monitor_id, that will be faster. > > > > > > But at present, it’s the worse of both world: dense array gobbling > > > memory, with loops to search that can’t take advantage of the fact that > > > it’s dense so you have to search every time instead of direct indexing. > > > > > > Note that going dense would avoid the risk of having two entries with the > > > same monitor_id+channel_id. > > > > (as a side note, I think some appearances of "dense" should have been > > "sparse" in your text, which makes it quite confusing) > > I don’t think so. I think that what was confusing is that my last sentence > was false. If you index a[i][j], you can have multiple entries with the same > i+j. Don’t know what I was thinking. > > > > > :) You know I usually have a more high-level approach to things... I > > guess it depends on the point of view. > > > > So first off, I want the convenience of a dynamic container here and I > > don't care about the memory. I know about the O(N^2) and I don't care > > much about that either. We're about to have 5 or less items in the > > array in majority of cases and no prospects of it ever going much > > higher. > > I’d refer you to > https://septheory.wordpress.com/2018/06/04/premature-optimization/, but I > know you don’t like when I quote my own stuff… :-) > > The fact that it’s not entirely insufferable because we happen to have a very > small number of items is not an excuse for having code that can’t seem to > decide if all items in the array are populated or not. Pick one or the other, > I don’t care, but choose :-) But the population of the array by disabled monitor_configs here is valid. The configs are there, just the monitors are disabled. When the client app wants to enable them, it calls spice_main_channel_update_display_enabled(), which expects the item to be there, otherwise it errors out. > > > > What I want is to have a monitor_config item in the array for every > > monitor that exists. This simplifies the concept in the way that you > > only create the items in one place (here). In the update methods > > (spice-gtk API), you know the item should always be there and if it > > isn't you error out (a very good thing to have on the API). If the > > update methods were to create missing items, you couldn't error out if > > they're missing. And there are also fields (like the new output_id) > > which don't have a clear default value, so in the update method, you > > don't really know what to put in there (if the client app uses a wrong > > ID). So this approach is less error prone and has a better defined > > behaviour. > > > > About the preallocated (that would be "sparse", right?) array, that was > > there before. I find it an unnecessarily low-level approach here (sure, > > can just be me :)). > > Oh no, it’s not only you ;-) > > > > The only real advantage I see is the two entries > > problem. OTOH, you need to distinguish which items are set and which > > are unset. As I said, I prefer to have a managed dynamic array and be > > done with it :) > > I’m fine with a dynamic array. If you decide that’s what it is, then don’t > pre-populate it. It will only accelerate all the search loops. > > > > > > > + 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))); > > > > + > > > > > > DRY - See comment below on reuse of that expression > > > > > > > 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))); > > > > + > > > > > > DRY - See comment on expression reuse. > > > > > > > + 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 > > > > > > Frankly, the added comment does not clarify anything for me ;-) Maybe > > > remove “or equivalent”? Or explain what you mean by that. The code you > > > added has the channel_id+monitor_id hardcoded, AFAICT, not “equivalent" > > > > Yeah :) I don't know really, what to put in the comment for this mess > > :) > > I’d say just drop “or equivalent”. > > > The "equivalent" is supposed to mean "anything that turns out to have > > the same value", because under the assumptions that you either have a > > single channel with multiple monitors or multiple channels with a > > single monitor each, for example the expression "channel_id ? > > channel_id : monitor_id" happens to have the same value and is also > > used elsewhere in the code.... :) > > Well, it only has the same value under the assumption that one of them is > zero. If that’s really is the assumption you are talking about, then write it > as: > > assumed to be channel_id+monitor_id with at least one of them being zero Ok. > > > > > Or maybe phrase as “id must be channel_id+monitor_id for monitor > > > configuration to be found”. > > > > > > > * @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)); > > > > > > It looks like the test above was removed. You replaced that with a > > > g_warning and return. > > > > > > But what if we find the same channel_id+monitor_id twice? Should we fail > > > in that case too? > > > > No idea. I suppose not failing (e.g. returning without updating > > anything) is slightly better, because then it has some chance to > > "work". Adding a warning for that case might be a good idea though. > > OK > > > > > > > + GArray *monitor_configs = spice_session_get_monitor_configs( > > > > + spice_channel_get_session(SPICE_CHANNEL(channel))); > > > > > > You reuse that expression several times in the patch. Maybe add > > > spice_get_channel_monitor_configs(channel)? > > > > Yeah, maybe. I find these getters somewhat verbose and confusing, but > > I'm reluctant to add more "aggregate" getters that increase the amount > > of glue code. It doesn't save that much and adds another level when you > > go looking for what it actually does. But a matter of taste I guess, I > > can add that if people want. > > Adding a “spice_channel_get_monitor_configs(channel)”, whether it’s a macro > or a function, is a win in readability and encapsulation. I don't think it's such a win in readability, because I myself would then (if I saw this) think monitors_config is an attribute of the channel. But it is shorter, yeah. > > > > - SpiceDisplayConfig display = { > > > > - .x = x, .y = y, .width = width, .height = height, > > > > - .display_state = c->display[id].display_state > > > > - }; > > > > + int found = 0; > > > > > > Personal preference: either make ‘found' a boolean, or count how many you > > > have and make it an error to have more than one. > > > > I wasn't sure of boolean in C, I'll change it. > > > > > > + 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) { > > > > > > Extraneous parentheses (personal preference, please ignore if you > > > disagree) > > > > Yeah, not sure where they came from, I'll remove them. > > > > > > + found = 1; > > > > + break; > > > > > > Minor style preference: add !found to the for loop, makes it IMO clearer > > > when the loop exits. > > > > OK. > > > > > > + } > > > > + } > > > > > > > > - 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; > > > > + } > > > > > > There was a memcmp that checked the content fo the monitor config, > > > presumably to avoid sending updates if we call the function with the > > > existing values. Any reason for removing it? > > > > Ah, I missed that, need to fix it, thanks. > > > > > > > > > > - 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))); > > > > + > > > > > > DRY > > > > > > > + 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; > > > > + } > > > > + } > > > > > > DRY. Consider a function that returns a SpiceMonitorConfig * or NULL. > > > Also would make it simpler to update the “channel_id + monitor_id” > > > hypothesis. Right next to spice_session_find_monitor_config, just need to > > > find a different name ;-) > > > > TBH I considered that, but I just couldn't get myself to write such an > > ugly function (that could potentially be reused) :D > > > > But I suppose I should, though it's only repeated twice. > > > > > > > > + > > > > + 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); > > > > + > > > > > > Spurious space that I really don’t care about, keep it if you want, just > > > was in an “Annoying Mood” day ;-) > > > > > > > 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); > > > > > > Just curious why define an interface that barfs on negative value, but > > > does not take unsigned? Copy-paste inheritance, OK, but still, don’t know > > > why it’s that way elsewhere. > > > > True, I should fix that :) > > > > > > + > > > > + 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; > > > > +} > > > > > > Seems a bit redundant with spice_main_channel_update_display. What’s the > > > added value? Looks to me like one should call the other. > > > > Not really, this one takes channel_id and monitor_id, the other the id > > = channel_id + monitor_id. I think it's clear you can't call this one > > from there and it's a bad idea to call that one (which really should be > > deprecated and I plan to look into it in the next version of the > > series) from here… > > Ah, if the plan is to deprecate it, then it makes sense. > > > > > Thanks, > > Lukas > > > > > > + > > > > 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@lists.freedesktop.org > > > > https://lists.freedesktop.org/mailman/listinfo/spice-devel > > > > > > > > > > _______________________________________________ > > Spice-devel mailing list > > Spice-devel@lists.freedesktop.org > > https://lists.freedesktop.org/mailman/listinfo/spice-devel > > _______________________________________________ Spice-devel mailing list Spice-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/spice-devel