diff --git a/sommelier-output.cc b/sommelier-output.cc index 87d3b93..305afd2 100644 --- a/sommelier-output.cc +++ b/sommelier-output.cc @@ -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; diff --git a/sommelier-window.cc b/sommelier-window.cc index 968fd50..6522dd1 100644 --- a/sommelier-window.cc +++ b/sommelier-window.cc @@ -5,6 +5,7 @@ #include "sommelier-window.h" // NOLINT(build/include_directory) #include +#include #include #include #include @@ -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); @@ -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; } @@ -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; @@ -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(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(output->logical_width) / window->viewport_width; + } else { + // Map movement from viewport to the window's space. + window->viewport_pointer_scale = + static_cast(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 @@ -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; @@ -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; @@ -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; +} diff --git a/sommelier-window.h b/sommelier-window.h index 16f6116..b16dc45 100644 --- a/sommelier-window.h +++ b/sommelier-window.h @@ -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; @@ -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_ diff --git a/sommelier.cc b/sommelier.cc index be9f068..a08e19c 100644 --- a/sommelier.cc +++ b/sommelier.cc @@ -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; @@ -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 @@ -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; + } } } @@ -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); diff --git a/sommelier.h b/sommelier.h index cc6c503..2af5b90 100644 --- a/sommelier.h +++ b/sommelier.h @@ -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);