Skip to content
This repository was archived by the owner on Jan 30, 2025. It is now read-only.

Commit

Permalink
vm_tools: sommelier: improve randr emulation behaviour
Browse files Browse the repository at this point in the history
1. Fetch virtual screen info for the window to find the appropriate
   emulation rect.
2. Send ConfigureWindow event after emulation. Some games require
   manual triggering (ex. to re-enter fullscreen, etc).
3. Some games set max dimensions as INT_MAX. Take account of this
   in sl_window_is_containerized and
   sl_internal_toplevel_configure_size_containerized.
4. When emulation is active, override viewport destination size
   when window is non-fullscreen in compositor (so that window can
   be resized like non-emulated mode).
5. Make toplevel_configure_position always use emulated position.

BUG=b:328699937,b:331688838
TEST=Test games: Stardew Valley, CS2

Change-Id: I79b945e15348ecdad61f63cac6b1192a4117ddf2
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/5460650
Commit-Queue: Max Lee <[email protected]>
Reviewed-by: Chloe Pelling <[email protected]>
Tested-by: Max Lee <[email protected]>
  • Loading branch information
Max Lee authored and Chromeos LUCI committed Apr 23, 2024
1 parent cbaa703 commit b0dd93e
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 22 deletions.
1 change: 1 addition & 0 deletions sommelier-output.cc
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,7 @@ static void sl_bind_host_output(struct wl_client* client,
host->x = 0;
host->y = 0;
host->virt_x = 0;
host->virt_y = 0;
host->logical_x = 0;
host->logical_y = 0;
host->physical_width = 0;
Expand Down
80 changes: 69 additions & 11 deletions sommelier-window.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "sommelier-window.h" // NOLINT(build/include_directory)

#include <algorithm>
#include <climits>
#include <fstream>
#include <assert.h>
#include <cstdint>
Expand Down Expand Up @@ -413,17 +414,14 @@ void sl_internal_toplevel_configure_size_containerized(struct sl_window* window,
int32_t width_in_pixels,
int32_t height_in_pixels,
int& mut_config_idx) {
// TODO(b/328699937): Re-visit the correctness of this function when XWayland
// is emulating screen size for the window.

// Forward resizes to the client if requested size fits within the min&max
// dimensions. Note that min&max dimensions are strictly set by the X11 client
// only (and forwarded to Exo immediately). If min&max dimensions are not set,
// we will set the size of the window to screen size (see below).
if (window->max_width >= width_in_pixels &&
if ((window->max_width >= width_in_pixels || !window->max_width) &&
window->min_width <= width_in_pixels &&
window->max_height >= height_in_pixels &&
window->max_height <= height_in_pixels) {
(window->max_height >= height_in_pixels || !window->max_height) &&
window->min_height <= height_in_pixels && !window->use_emulated_rects) {
// TODO(b/330639760): Consider unset aspect ratio every frame in
// surface_commit.
zaura_surface_set_aspect_ratio(window->aura_surface, -1, -1);
Expand Down Expand Up @@ -453,7 +451,14 @@ void sl_internal_toplevel_configure_size_containerized(struct sl_window* window,
window->max_width ? window->max_width : window->min_width;
int safe_window_height =
window->max_height ? window->max_height : window->min_height;
if (!safe_window_width || !safe_window_height || window->fullscreen) {
if (window->use_emulated_rects) {
// If screen size emulation is set by XWayland, set the window size to the
// emulated size and adjust the viewport size as Exo had requested it to be.
safe_window_width = window->emulated_width;
safe_window_height = window->emulated_height;
} else if (!safe_window_width || !safe_window_height ||
window->max_width > output->width ||
window->max_height > output->height || window->fullscreen) {
safe_window_width = output->width;
safe_window_height = output->height;
}
Expand All @@ -465,7 +470,23 @@ void sl_internal_toplevel_configure_size_containerized(struct sl_window* window,
window->next_config.values[mut_config_idx++] = safe_window_height;
window->next_config.values[mut_config_idx++] = 0;

// Use viewport to resize surface as per Exo request.
if (window->use_emulated_rects && window->compositor_fullscreen) {
// If we are using emulated rects and the window is fullscreen in
// compositor, we only have to report the emulated size (done above). Aspect
// ratio changes are moot (since compositor fullscreen). We don't have to
// set viewport overrides since we can fully rely on viewport setup by
// Xwayland.
// If the window is windowed in compositor, then we have to do additional
// viewport operations to make the window fit into the size that compositor
// wants it to be, without disrupting the emulation. This has identical code
// path as non-emulated mode, except pointer movement scaling (see below).
sl_window_reset_viewport(window);
return;
}

// Use viewport to resize surface as per Exo request if we are not using
// emulated rects, or window is not fullscreen in Exo (and using emulated
// rects).
window->viewport_override = true;

int32_t safe_window_width_in_wl = safe_window_width;
Expand Down Expand Up @@ -516,8 +537,18 @@ void sl_internal_toplevel_configure_size_containerized(struct sl_window* window,
safe_window_width;
window->viewport_width = host_width;
}
window->viewport_pointer_scale =
static_cast<float>(safe_window_width_in_wl) / window->viewport_width;

if (window->use_emulated_rects) {
// Pointer scaling is being done in XWayland as well, assuming the viewport
// is the size of the screen. Map the movement from viewport to logical
// width of the screen (which XWayland is expecting).
window->viewport_pointer_scale =
static_cast<float>(output->logical_width) / window->viewport_width;
} else {
// Map movement from viewport to the window's space.
window->viewport_pointer_scale =
static_cast<float>(safe_window_width_in_wl) / window->viewport_width;
}

if (window->xdg_toplevel) {
// Override X11 client-defined min and max size to a value relative to
Expand Down Expand Up @@ -601,6 +632,10 @@ void sl_internal_toplevel_configure_size(struct sl_window* window,
window->next_config.values[mut_config_idx++] = window->width;
window->next_config.values[mut_config_idx++] = window->height;
window->next_config.values[mut_config_idx++] = 0;
} else if (window->use_emulated_rects) {
window->next_config.values[mut_config_idx++] = window->emulated_width;
window->next_config.values[mut_config_idx++] = window->emulated_height;
window->next_config.values[mut_config_idx++] = 0;
} else {
window->next_config.values[mut_config_idx++] = width_in_pixels;
window->next_config.values[mut_config_idx++] = height_in_pixels;
Expand All @@ -614,7 +649,14 @@ void sl_internal_toplevel_configure_position(struct sl_window* window,
int32_t width_in_pixels,
int32_t height_in_pixels,
int& mut_config_idx) {
if (x != kUnspecifiedCoord && y != kUnspecifiedCoord) {
if (window->use_emulated_rects) {
// Ignore requests from the compositor and set the coordinates as emulation
// requested.
window->next_config.mask |= XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y;
window->next_config.values[mut_config_idx++] = window->emulated_x;
window->next_config.values[mut_config_idx++] = window->emulated_y;

} else if (x != kUnspecifiedCoord && y != kUnspecifiedCoord) {
// Convert to virtual coordinates
int32_t guest_x = x;
int32_t guest_y = y;
Expand Down Expand Up @@ -1274,3 +1316,19 @@ void sl_window_get_width_height(struct sl_window* window,
}
}
}

bool sl_window_get_output_virt_position(struct sl_window* window,
uint32_t& mut_x,
uint32_t& mut_y) {
for (auto output : window->ctx->host_outputs) {
if (window->x >= output->virt_x &&
window->x < output->virt_x + output->virt_width &&
window->y >= output->virt_y &&
window->y < output->virt_y + output->virt_height) {
mut_x = output->virt_x;
mut_y = output->virt_y;
return true;
}
}
return false;
}
8 changes: 8 additions & 0 deletions sommelier-window.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ struct sl_window {
bool use_emulated_rects = false;
int emulated_width = 0;
int emulated_height = 0;
int emulated_x = 0;
int emulated_y = 0;

int border_width = 0;
int depth = 0;
Expand Down Expand Up @@ -333,4 +335,10 @@ void sl_window_update_should_be_containerized_from_pid(
bool sl_window_is_containerized(struct sl_window* window);
void sl_window_reset_viewport(struct sl_window* window);

// Get the virtual screen position that the window is in. Returns true if mut_x
// and mut_y were updated with the found screen, false otherwise.
bool sl_window_get_output_virt_position(struct sl_window* window,
uint32_t& mut_x,
uint32_t& mut_y);

#endif // VM_TOOLS_SOMMELIER_SOMMELIER_WINDOW_H_
59 changes: 48 additions & 11 deletions sommelier.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1459,8 +1459,16 @@ void sl_handle_map_request(struct sl_context* ctx,
window->min_height = size_hints.min_height;
}
if (window->size_flags & P_MAX_SIZE) {
window->max_width = size_hints.max_width;
window->max_height = size_hints.max_height;
if (size_hints.max_width < INT_MAX) {
window->max_width = size_hints.max_width;
} else {
window->max_width = 0;
}
if (size_hints.max_height < INT_MAX) {
window->max_height = size_hints.max_height;
} else {
window->max_height = 0;
}
}

window->border_width = 0;
Expand Down Expand Up @@ -1900,6 +1908,8 @@ void sl_handle_client_message(struct sl_context* ctx,
if (window && window->xdg_toplevel) {
xdg_toplevel_set_minimized(window->xdg_toplevel);
#ifdef QUIRKS_SUPPORT
// TODO(b/333819234): Migrate FEATURE_BLACK_SCREEN_FIX to quirk condition
// `runtime: Proton`.
if (window->ctx->quirks.IsEnabled(window,
quirks::FEATURE_BLACK_SCREEN_FIX)) {
// Workaround for some borealis apps showing a black screen after losing
Expand Down Expand Up @@ -2311,8 +2321,16 @@ void sl_handle_property_notify(struct sl_context* ctx,
window->min_height = size_hints.min_height;
}
if (window->size_flags & P_MAX_SIZE) {
window->max_width = size_hints.max_width;
window->max_height = size_hints.max_height;
if (size_hints.max_width < INT_MAX) {
window->max_width = size_hints.max_width;
} else {
window->max_width = 0;
}
if (size_hints.max_height < INT_MAX) {
window->max_height = size_hints.max_height;
} else {
window->max_height = 0;
}
}
}

Expand Down Expand Up @@ -2490,23 +2508,42 @@ void sl_handle_property_notify(struct sl_context* ctx,
return;
}

// We need to select the appropriate emulated screen based on the screen the
// window is in.
uint32_t target_x = 0;
uint32_t target_y = 0;
if (!sl_window_get_output_virt_position(window, target_x, target_y)) {
window->use_emulated_rects = false;
free(reply);
return;
}

for (uint32_t i = 0; i < reply->value_len; i += 4) {
// Assume the fullscreen window position is the screen's position.
// XWayland emulation must match this x/y coordinates.
if (xywh[i] == window->x && xywh[i + 1] == window->y) {
// NOTE: XWayland will send a wp_viewport.set_source request based on
// ConfigureNotify request. ConfigureNotify with emulated values will be
// sent in the next sl_host_surface_commit().
window->use_emulated_rects = true;
if (xywh[i] == target_x && xywh[i + 1] == target_y) {
window->emulated_x = xywh[i];
window->emulated_y = xywh[i + 1];
window->emulated_width = xywh[i + 2];
window->emulated_height = xywh[i + 3];
window->use_emulated_rects = true;
// XWayland will send a wp_viewport.set_source and destination request
// (plus, do other busy work such as setting pointer scaling) based on
// window property set. Configure window manually here to trigger
// necessary changes since some games do not do anything further than
// adding the attributes.
xcb()->configure_window(window->ctx->connection, window->id,
XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
XCB_CONFIG_WINDOW_WIDTH |
XCB_CONFIG_WINDOW_HEIGHT,
&xywh[i]);
free(reply);
return;
}
}

fprintf(stderr, "failed to find screen with position %u,%u\n", window->x,
window->y);
fprintf(stderr, "failed to find screen with position %u,%u\n", target_x,
target_y);
window->use_emulated_rects = false;
free(reply);

Expand Down
5 changes: 5 additions & 0 deletions sommelier.h
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,11 @@ struct sl_host_output {
double xdg_scale_x;
double xdg_scale_y;
int virt_x;
// Note that all virtual screens are repositioned laterally in a row
// (regardless of host's positioning), which means y coordinate is always 0.
// However, in case future implementation changes, utilize this attribute
// whenever we reference virtual y coordinates.
int virt_y;
};
MAP_STRUCTS(wl_output, sl_host_output);

Expand Down

0 comments on commit b0dd93e

Please sign in to comment.