[X11] Refactor bounds handling to use WM synchronization
* Replace the direct use of the bounds_in_pixels_ with a new sync-based mechanism that tracks the last set bounds (stored in last_set_bounds_px_) and relies on a WmSync object plus a GeometryCache to obtain current geometry via GetBoundsInPixels(). * Update SetBoundsInPixels() and fullscreen logic to call SetBoundsWithWmSync(), so that window bounds are only updated once the WM has synchronized the change. This replaces ad-hoc direct assignments with a coordinated update via OnWmSynced() and OnBoundsChanged(). * Modify OnConfigureEvent() to initialize and later use the new sync tracking (via last_configure_size_ and last_swapped_size_), and add a helper MaybeUpdateSyncCounter() to flush pending counter updates when the configured size matches the swapped size. * Update the X11Extension interface (and its usage in DesktopWindowTreeHostLinux) so that OnCompleteSwapAfterResize() now accepts a new size parameter. Bug: 40528928, 381224161 Change-Id: I1a25874fc47c6573ec77427b38532f53e9802684 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6289229 Reviewed-by: Maksim Sisov <msisov@igalia.com> Reviewed-by: Elly FJ <ellyjones@chromium.org> Commit-Queue: Thomas Anderson <thomasanderson@chromium.org> Reviewed-by: Nick Yamane <nickdiego@igalia.com> Cr-Commit-Position: refs/heads/main@{#1429329}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
41ae968453
commit
d13172e652
chrome/browser/ui/views/tabs/dragging
ui
gfx
ozone
platform
platform_window
extensions
views
@ -487,7 +487,7 @@ Browser* TabDragControllerTest::CreateAnotherBrowserAndResize() {
|
||||
gfx::Rect browser_rect(work_area.origin() + gfx::Vector2d(50, 50), size);
|
||||
|
||||
if (test::PlatformSupportsScreenCoordinates()) {
|
||||
browser()->window()->SetBounds(browser_rect);
|
||||
ui_test_utils::SetAndWaitForBounds(*browser(), browser_rect);
|
||||
browser_rect.set_x(browser_rect.right());
|
||||
} else {
|
||||
test::ResizeUsingMouseEmulation(browser(), browser_rect);
|
||||
@ -509,7 +509,7 @@ Browser* TabDragControllerTest::CreateAnotherBrowserAndResize() {
|
||||
Browser* browser2 = CreateBrowser(browser()->profile());
|
||||
ResetIDs(browser2->tab_strip_model(), 100);
|
||||
if (test::PlatformSupportsScreenCoordinates()) {
|
||||
browser2->window()->SetBounds(browser_rect);
|
||||
ui_test_utils::SetAndWaitForBounds(*browser2, browser_rect);
|
||||
} else {
|
||||
test::ResizeUsingMouseEmulation(browser2, browser_rect);
|
||||
}
|
||||
|
@ -412,6 +412,17 @@ int Connection::GetFd() {
|
||||
return Ready() ? xcb_get_file_descriptor(XcbConnection()) : -1;
|
||||
}
|
||||
|
||||
bool Connection::CanSyncWithWm() const {
|
||||
// For some WMs, we don't need to experimentally sync with them to determine
|
||||
// sync support, so we can use WmSync right away. For now, only check for
|
||||
// Openbox since that's what is used in tests. The list may be expanded as
|
||||
// nearly all WMs should work with WmSync.
|
||||
if (GetWmName() == "Openbox") {
|
||||
return true;
|
||||
}
|
||||
return synced_with_wm_;
|
||||
}
|
||||
|
||||
const std::string& Connection::DisplayString() const {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
return display_string_;
|
||||
|
@ -255,9 +255,10 @@ class COMPONENT_EXPORT(X11) Connection final : public XProto,
|
||||
|
||||
WindowEventManager& window_event_manager() { return window_event_manager_; }
|
||||
|
||||
// Indicates if the connection was able to successfully sync with the
|
||||
// window manager.
|
||||
bool synced_with_wm() const { return synced_with_wm_; }
|
||||
// Indicates if the connection is able to sync with the WM, either because the
|
||||
// WM is on an allowlist or the connection successfully synced with the WM to
|
||||
// test support experimentally.
|
||||
bool CanSyncWithWm() const;
|
||||
|
||||
// Returns the underlying socket's FD if the connection is valid, or -1
|
||||
// otherwise.
|
||||
|
@ -52,11 +52,17 @@ gfx::Rect GeometryCache::GetBoundsPx() {
|
||||
}
|
||||
|
||||
void GeometryCache::OnQueryTreeResponse(QueryTreeResponse response) {
|
||||
if (have_parent_) {
|
||||
return;
|
||||
}
|
||||
OnParentChanged(response ? response->parent : Window::None,
|
||||
geometry_.origin());
|
||||
}
|
||||
|
||||
void GeometryCache::OnGetGeometryResponse(GetGeometryResponse response) {
|
||||
if (have_geometry_) {
|
||||
return;
|
||||
}
|
||||
OnGeometryChanged(response ? gfx::Rect(response->x, response->y,
|
||||
response->width, response->height)
|
||||
: gfx::Rect());
|
||||
|
@ -27,7 +27,7 @@ Window GetWindowForEvent(const Event& event) {
|
||||
} // namespace
|
||||
|
||||
WmSync::WmSync(Connection* connection, base::OnceClosure on_synced)
|
||||
: WmSync(connection, std::move(on_synced), connection->synced_with_wm()) {}
|
||||
: WmSync(connection, std::move(on_synced), connection->CanSyncWithWm()) {}
|
||||
|
||||
WmSync::WmSync(Connection* connection,
|
||||
base::OnceClosure on_synced,
|
||||
|
@ -45,8 +45,10 @@
|
||||
#include "ui/gfx/geometry/transform.h"
|
||||
#include "ui/gfx/image/image_skia_rep.h"
|
||||
#include "ui/gfx/x/atom_cache.h"
|
||||
#include "ui/gfx/x/geometry_cache.h"
|
||||
#include "ui/gfx/x/visual_manager.h"
|
||||
#include "ui/gfx/x/window_event_manager.h"
|
||||
#include "ui/gfx/x/wm_sync.h"
|
||||
#include "ui/gfx/x/x11_path.h"
|
||||
#include "ui/gfx/x/xproto.h"
|
||||
#include "ui/ozone/platform/x11/hit_test_x11.h"
|
||||
@ -494,9 +496,9 @@ void X11Window::SetBoundsInPixels(const gfx::Rect& bounds) {
|
||||
AdjustSizeForDisplay(bounds.size()));
|
||||
|
||||
const bool size_changed =
|
||||
bounds_in_pixels_.size() != new_bounds_in_pixels.size();
|
||||
GetBoundsInPixels().size() != new_bounds_in_pixels.size();
|
||||
const bool origin_changed =
|
||||
bounds_in_pixels_.origin() != new_bounds_in_pixels.origin();
|
||||
GetBoundsInPixels().origin() != new_bounds_in_pixels.origin();
|
||||
|
||||
// Assume that the resize will go through as requested, which should be the
|
||||
// case if we're running without a window manager. If there's a window
|
||||
@ -537,7 +539,6 @@ void X11Window::SetBoundsInPixels(const gfx::Rect& bounds) {
|
||||
}
|
||||
|
||||
if (origin_changed || size_changed) {
|
||||
bounds_change_in_flight_ = true;
|
||||
connection_->ConfigureWindow(req);
|
||||
}
|
||||
|
||||
@ -546,7 +547,7 @@ void X11Window::SetBoundsInPixels(const gfx::Rect& bounds) {
|
||||
// manager, it can modify or ignore the request, but (per ICCCM) we'll get a
|
||||
// (possibly synthetic) ConfigureNotify about the actual size and correct
|
||||
// |bounds_in_pixels_| later.
|
||||
bounds_in_pixels_ = new_bounds_in_pixels;
|
||||
SetBoundsWithWmSync(new_bounds_in_pixels);
|
||||
ResetWindowRegion();
|
||||
|
||||
// Even if the pixel bounds didn't change this call to the delegate should
|
||||
@ -556,7 +557,8 @@ void X11Window::SetBoundsInPixels(const gfx::Rect& bounds) {
|
||||
}
|
||||
|
||||
gfx::Rect X11Window::GetBoundsInPixels() const {
|
||||
return bounds_in_pixels_;
|
||||
return bounds_wm_sync_ || !geometry_cache_ ? last_set_bounds_px_
|
||||
: geometry_cache_->GetBoundsPx();
|
||||
}
|
||||
|
||||
void X11Window::SetBoundsInDIP(const gfx::Rect& bounds_in_dip) {
|
||||
@ -565,7 +567,7 @@ void X11Window::SetBoundsInDIP(const gfx::Rect& bounds_in_dip) {
|
||||
}
|
||||
|
||||
gfx::Rect X11Window::GetBoundsInDIP() const {
|
||||
return platform_window_delegate_->ConvertRectToDIP(bounds_in_pixels_);
|
||||
return platform_window_delegate_->ConvertRectToDIP(GetBoundsInPixels());
|
||||
}
|
||||
|
||||
void X11Window::SetTitle(const std::u16string& title) {
|
||||
@ -673,23 +675,11 @@ void X11Window::SetFullscreen(bool fullscreen, int64_t target_display_id) {
|
||||
|
||||
UpdateDecorationInsets();
|
||||
|
||||
// Do not go through SetBounds as long as it adjusts bounds and sets them to X
|
||||
// Server. Instead, we just store the bounds and notify the client that the
|
||||
// window occupies the entire screen.
|
||||
bool origin_changed = bounds_in_pixels_.origin() != new_bounds_px.origin();
|
||||
bounds_in_pixels_ = new_bounds_px;
|
||||
// Pretend the bounds changed immediately, and wait for a WM sync to use the
|
||||
// server's bounds.
|
||||
bool origin_changed = GetBoundsInPixels().origin() != new_bounds_px.origin();
|
||||
SetBoundsWithWmSync(new_bounds_px);
|
||||
|
||||
// If there is a restore and/or bounds change in flight, then set a flag to
|
||||
// ignore the next one or two configure events (hopefully) coming from those
|
||||
// requests. This prevents any in-flight restore requests from changing the
|
||||
// bounds in a way that conflicts with the `bounds_in_pixels_` setting above.
|
||||
// This is not perfect, and if there is some other in-flight bounds change for
|
||||
// some reason, or if the ordering of events from the WM behaves differently,
|
||||
// this will not prevent the issue. See: http://crbug.com/1227451
|
||||
ignore_next_configures_ = restore_in_flight_ ? 1 : 0;
|
||||
if (bounds_change_in_flight_) {
|
||||
ignore_next_configures_++;
|
||||
}
|
||||
// This must be the final call in this function, as `this` may be deleted
|
||||
// during the observation of this event.
|
||||
platform_window_delegate_->OnBoundsChanged({origin_changed});
|
||||
@ -736,11 +726,9 @@ void X11Window::Minimize() {
|
||||
|
||||
void X11Window::Restore() {
|
||||
if (IsMinimized()) {
|
||||
restore_in_flight_ = true;
|
||||
SetWMSpecState(false, x11::GetAtom("_NET_WM_STATE_HIDDEN"),
|
||||
x11::Atom::None);
|
||||
} else if (IsMaximized()) {
|
||||
restore_in_flight_ = true;
|
||||
should_maximize_after_map_ = false;
|
||||
SetWMSpecState(false, x11::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"),
|
||||
x11::GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ"));
|
||||
@ -852,8 +840,8 @@ void X11Window::SetCursor(scoped_refptr<PlatformCursor> cursor) {
|
||||
void X11Window::MoveCursorTo(const gfx::Point& location_px) {
|
||||
connection_->WarpPointer(x11::WarpPointerRequest{
|
||||
.dst_window = x_root_window_,
|
||||
.dst_x = static_cast<int16_t>(bounds_in_pixels_.x() + location_px.x()),
|
||||
.dst_y = static_cast<int16_t>(bounds_in_pixels_.y() + location_px.y()),
|
||||
.dst_x = static_cast<int16_t>(GetBoundsInPixels().x() + location_px.x()),
|
||||
.dst_y = static_cast<int16_t>(GetBoundsInPixels().y() + location_px.y()),
|
||||
});
|
||||
// The cached cursor location is no longer valid.
|
||||
X11EventSource::GetInstance()->ClearLastCursorLocation();
|
||||
@ -866,7 +854,7 @@ void X11Window::ConfineCursorToBounds(const gfx::Rect& bounds) {
|
||||
return;
|
||||
}
|
||||
|
||||
gfx::Rect barrier = bounds + bounds_in_pixels_.OffsetFromOrigin();
|
||||
gfx::Rect barrier = bounds + GetBoundsInPixels().OffsetFromOrigin();
|
||||
|
||||
auto make_barrier = [&](uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2,
|
||||
x11::XFixes::BarrierDirections directions) {
|
||||
@ -1165,6 +1153,7 @@ void X11Window::NotifyStartupComplete(const std::string& startup_id) {
|
||||
event.type = net_startup_info;
|
||||
}
|
||||
|
||||
geometry_cache_.reset();
|
||||
connection_->DestroyWindow(window);
|
||||
connection_->Flush();
|
||||
}
|
||||
@ -1214,11 +1203,10 @@ bool X11Window::IsWmTiling() const {
|
||||
return ui::IsWmTiling(ui::GuessWindowManager());
|
||||
}
|
||||
|
||||
void X11Window::OnCompleteSwapAfterResize() {
|
||||
if (configure_counter_value_ && have_configure_) {
|
||||
connection_->sync().SetCounter(update_counter_, *configure_counter_value_);
|
||||
configure_counter_value_.reset();
|
||||
have_configure_ = false;
|
||||
void X11Window::OnCompleteSwapAfterResize(const gfx::Size& new_size) {
|
||||
last_swapped_size_ = new_size;
|
||||
if (configure_counter_value_) {
|
||||
MaybeUpdateSyncCounter();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1260,6 +1248,10 @@ void X11Window::SetX11ExtensionDelegate(X11ExtensionDelegate* delegate) {
|
||||
x11_extension_delegate_ = delegate;
|
||||
}
|
||||
|
||||
bool X11Window::IsWmSyncActiveForTest() {
|
||||
return bounds_wm_sync_.get();
|
||||
}
|
||||
|
||||
bool X11Window::HandleAsAtkEvent(const x11::KeyEvent& key_event,
|
||||
bool send_event,
|
||||
bool transient) {
|
||||
@ -1414,10 +1406,6 @@ void X11Window::OnXWindowStateChanged() {
|
||||
new_state = PlatformWindowState::kMaximized;
|
||||
}
|
||||
|
||||
if (restore_in_flight_ && !IsMaximized()) {
|
||||
restore_in_flight_ = false;
|
||||
}
|
||||
|
||||
// fullscreen state is set syschronously at ToggleFullscreen() and must be
|
||||
// kept and propagated to the client only when explicitly requested by upper
|
||||
// layers, as it means we are in "browser fullscreen mode" (where
|
||||
@ -1445,16 +1433,8 @@ void X11Window::OnXWindowStateChanged() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (restored_bounds_in_pixels_.IsEmpty()) {
|
||||
if (IsMaximized()) {
|
||||
// The request that we become maximized originated from a different
|
||||
// process. |bounds_in_pixels_| already contains our maximized bounds. Do
|
||||
// a best effort attempt to get restored bounds by setting it to our
|
||||
// previously set bounds (and if we get this wrong, we aren't any worse
|
||||
// off since we'd otherwise be returning our maximized bounds).
|
||||
restored_bounds_in_pixels_ = previous_bounds_in_pixels_;
|
||||
}
|
||||
} else if (!IsMaximized() && !IsFullscreen()) {
|
||||
if (!restored_bounds_in_pixels_.IsEmpty() && !IsMaximized() &&
|
||||
!IsFullscreen()) {
|
||||
// If we have restored bounds, but WM_STATE no longer claims to be
|
||||
// maximized or fullscreen, we should clear our restored bounds.
|
||||
restored_bounds_in_pixels_ = gfx::Rect();
|
||||
@ -1719,7 +1699,7 @@ scoped_refptr<X11Cursor> X11Window::GetLastCursor() {
|
||||
}
|
||||
|
||||
gfx::Size X11Window::GetSize() {
|
||||
return bounds_in_pixels_.size();
|
||||
return GetBoundsInPixels().size();
|
||||
}
|
||||
|
||||
void X11Window::QuitDragLoop() {
|
||||
@ -1846,12 +1826,12 @@ void X11Window::CreateXWindow(const PlatformWindowInitProperties& properties) {
|
||||
// same as the parent depth.
|
||||
req.border_pixel = 0;
|
||||
|
||||
bounds_in_pixels_ = SanitizeBounds(bounds);
|
||||
last_set_bounds_px_ = SanitizeBounds(bounds);
|
||||
req.parent = x_root_window_;
|
||||
req.x = bounds_in_pixels_.x();
|
||||
req.y = bounds_in_pixels_.y();
|
||||
req.width = bounds_in_pixels_.width();
|
||||
req.height = bounds_in_pixels_.height();
|
||||
req.x = last_set_bounds_px_.x();
|
||||
req.y = last_set_bounds_px_.y();
|
||||
req.width = last_set_bounds_px_.width();
|
||||
req.height = last_set_bounds_px_.height();
|
||||
req.depth = depth;
|
||||
req.c_class = x11::WindowClass::InputOutput;
|
||||
req.visual = visual_id;
|
||||
@ -1859,6 +1839,10 @@ void X11Window::CreateXWindow(const PlatformWindowInitProperties& properties) {
|
||||
xwindow_ = connection_->GenerateId<x11::Window>();
|
||||
req.wid = xwindow_;
|
||||
connection_->CreateWindow(req);
|
||||
// Unretained is safe since we own `geometry_cache_`.
|
||||
geometry_cache_ = std::make_unique<x11::GeometryCache>(
|
||||
&*connection_, xwindow_,
|
||||
base::BindRepeating(&X11Window::OnBoundsChanged, base::Unretained(this)));
|
||||
}
|
||||
|
||||
void X11Window::CloseXWindow() {
|
||||
@ -1875,6 +1859,7 @@ void X11Window::CloseXWindow() {
|
||||
security_surfaces.end());
|
||||
}
|
||||
|
||||
geometry_cache_.reset();
|
||||
connection_->DestroyWindow({xwindow_});
|
||||
xwindow_ = x11::Window::None;
|
||||
|
||||
@ -1891,8 +1876,8 @@ void X11Window::Map(bool inactive) {
|
||||
memset(&size_hints, 0, sizeof(size_hints));
|
||||
connection_->GetWmNormalHints(xwindow_, &size_hints);
|
||||
size_hints.flags |= x11::SIZE_HINT_P_POSITION;
|
||||
size_hints.x = bounds_in_pixels_.x();
|
||||
size_hints.y = bounds_in_pixels_.y();
|
||||
size_hints.x = GetBoundsInPixels().x();
|
||||
size_hints.y = GetBoundsInPixels().y();
|
||||
// Set STATIC_GRAVITY so that the window position is not affected by the
|
||||
// frame width when running with window manager.
|
||||
size_hints.flags |= x11::SIZE_HINT_P_WIN_GRAVITY;
|
||||
@ -1964,7 +1949,7 @@ bool X11Window::IsFullscreen() const {
|
||||
}
|
||||
|
||||
gfx::Rect X11Window::GetOuterBounds() const {
|
||||
gfx::Rect outer_bounds(bounds_in_pixels_);
|
||||
gfx::Rect outer_bounds(GetBoundsInPixels());
|
||||
outer_bounds.Inset(-native_window_frame_borders_in_pixels_);
|
||||
return outer_bounds;
|
||||
}
|
||||
@ -2272,23 +2257,6 @@ void X11Window::HandleEvent(const x11::Event& xev) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We can lose track of the window's position when the window is reparented.
|
||||
// When the parent window is moved, we won't get an event, so the window's
|
||||
// position relative to the root window will get out-of-sync. We can re-sync
|
||||
// when getting pointer events (EnterNotify, LeaveNotify, ButtonPress,
|
||||
// ButtonRelease, MotionNotify) which include the pointer location both
|
||||
// relative to this window and relative to the root window, so we can
|
||||
// calculate this window's position from that information.
|
||||
gfx::Point window_point = EventLocationFromXEvent(xev);
|
||||
gfx::Point root_point = EventSystemLocationFromXEvent(xev);
|
||||
if (!window_point.IsOrigin() && !root_point.IsOrigin()) {
|
||||
gfx::Point window_origin = gfx::Point() + (root_point - window_point);
|
||||
if (bounds_in_pixels_.origin() != window_origin) {
|
||||
bounds_in_pixels_.set_origin(window_origin);
|
||||
NotifyBoundsChanged(/*origin changed=*/true);
|
||||
}
|
||||
}
|
||||
|
||||
// May want to factor CheckXEventForConsistency(xev); into a common location
|
||||
// since it is called here.
|
||||
if (auto* crossing = xev.As<x11::CrossingEvent>()) {
|
||||
@ -2303,7 +2271,7 @@ void X11Window::HandleEvent(const x11::Event& xev) {
|
||||
OnFocusEvent(focus->opcode == x11::FocusEvent::In, focus->mode,
|
||||
focus->detail);
|
||||
} else if (auto* configure = xev.As<x11::ConfigureNotifyEvent>()) {
|
||||
OnConfigureEvent(*configure, xev.send_event());
|
||||
OnConfigureEvent(*configure);
|
||||
} else if (auto* crossing_input = xev.As<x11::Input::CrossingEvent>()) {
|
||||
TouchFactory* factory = TouchFactory::GetInstance();
|
||||
if (factory->ShouldProcessCrossingEvent(*crossing_input)) {
|
||||
@ -2347,7 +2315,6 @@ void X11Window::HandleEvent(const x11::Event& xev) {
|
||||
x11::EventMask::SubstructureRedirect);
|
||||
} else if (protocol == x11::GetAtom("_NET_WM_SYNC_REQUEST")) {
|
||||
configure_counter_value_.reset();
|
||||
have_configure_ = false;
|
||||
const int32_t hi = static_cast<int32_t>(client->data.data32[3]);
|
||||
const uint32_t lo = client->data.data32[2];
|
||||
// The spec says the WM should never send a counter value of 0, so
|
||||
@ -2405,57 +2372,12 @@ void X11Window::OnWindowMapped() {
|
||||
}
|
||||
}
|
||||
|
||||
void X11Window::OnConfigureEvent(const x11::ConfigureNotifyEvent& configure,
|
||||
bool send_event) {
|
||||
void X11Window::OnConfigureEvent(const x11::ConfigureNotifyEvent& configure) {
|
||||
DCHECK_EQ(xwindow_, configure.window);
|
||||
|
||||
if (configure_counter_value_) {
|
||||
have_configure_ = true;
|
||||
}
|
||||
|
||||
// During a Restore() -> ToggleFullscreen() or Restore() -> SetBounds() ->
|
||||
// ToggleFullscreen() sequence, ignore the configure events from the Restore
|
||||
// and SetBounds requests, if we're waiting on fullscreen. After
|
||||
// OnXWindowStateChanged unsets this flag, there will be a configuration event
|
||||
// that will set the bounds to the final fullscreen bounds.
|
||||
if (ignore_next_configures_ > 0) {
|
||||
ignore_next_configures_--;
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: This OnConfigureEvent might not necessarily correspond to a previous
|
||||
// SetBounds request. Due to limitations in X11 there isn't a way to
|
||||
// match events to its original request. For now, we assume that the next
|
||||
// OnConfigureEvent event after a SetBounds (ConfigureWindow) request is from
|
||||
// that request. This would break in some scenarios (for example calling
|
||||
// SetBounds more than once quickly). See crbug.com/1227451.
|
||||
bounds_change_in_flight_ = false;
|
||||
|
||||
// It's possible that the X window may be resized by some other means than
|
||||
// from within aura (e.g. the X window manager can change the size). Make
|
||||
// sure the root window size is maintained properly.
|
||||
int translated_x_in_pixels = configure.x;
|
||||
int translated_y_in_pixels = configure.y;
|
||||
if (!send_event && !configure.override_redirect) {
|
||||
auto future =
|
||||
connection_->TranslateCoordinates({xwindow_, x_root_window_, 0, 0});
|
||||
if (auto coords = future.Sync()) {
|
||||
translated_x_in_pixels = coords->dst_x;
|
||||
translated_y_in_pixels = coords->dst_y;
|
||||
}
|
||||
}
|
||||
gfx::Rect new_bounds_px(translated_x_in_pixels, translated_y_in_pixels,
|
||||
configure.width, configure.height);
|
||||
const bool size_changed = bounds_in_pixels_.size() != new_bounds_px.size();
|
||||
const bool origin_changed =
|
||||
bounds_in_pixels_.origin() != new_bounds_px.origin();
|
||||
previous_bounds_in_pixels_ = bounds_in_pixels_;
|
||||
bounds_in_pixels_ = new_bounds_px;
|
||||
|
||||
if (size_changed) {
|
||||
DispatchResize(origin_changed);
|
||||
} else if (origin_changed) {
|
||||
NotifyBoundsChanged(/*origin changed=*/true);
|
||||
if (configure_counter_value_ && !last_configure_size_) {
|
||||
last_configure_size_ = gfx::Size(configure.width, configure.height);
|
||||
MaybeUpdateSyncCounter();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2537,6 +2459,7 @@ void X11Window::OnFrameExtentsUpdated() {
|
||||
}
|
||||
|
||||
void X11Window::DispatchResize(bool origin_changed) {
|
||||
last_swapped_size_.reset();
|
||||
if (configure_counter_value_) {
|
||||
// WM handles resize throttling. No need to delay resize events.
|
||||
DelayedResize(origin_changed);
|
||||
@ -2656,4 +2579,39 @@ bool X11Window::InitializeAsStatusIcon() {
|
||||
return !future.Sync().error;
|
||||
}
|
||||
|
||||
void X11Window::SetBoundsWithWmSync(const gfx::Rect& bounds_px) {
|
||||
last_set_bounds_px_ = bounds_px;
|
||||
// Unretained is safe since we own `bounds_wm_sync_`.
|
||||
bounds_wm_sync_ = std::make_unique<x11::WmSync>(
|
||||
&*connection_,
|
||||
base::BindOnce(&X11Window::OnWmSynced, base::Unretained(this)));
|
||||
}
|
||||
|
||||
void X11Window::OnWmSynced() {
|
||||
bounds_wm_sync_.reset();
|
||||
OnBoundsChanged(last_set_bounds_px_, GetBoundsInPixels());
|
||||
}
|
||||
|
||||
void X11Window::OnBoundsChanged(const std::optional<gfx::Rect>& old_bounds_px,
|
||||
const gfx::Rect& new_bounds_px) {
|
||||
const bool size_changed = !old_bounds_px.has_value() ||
|
||||
old_bounds_px->size() != new_bounds_px.size();
|
||||
const bool origin_changed = !old_bounds_px.has_value() ||
|
||||
old_bounds_px->origin() != new_bounds_px.origin();
|
||||
|
||||
if (size_changed) {
|
||||
DispatchResize(origin_changed);
|
||||
} else if (origin_changed) {
|
||||
NotifyBoundsChanged(/*origin changed=*/true);
|
||||
}
|
||||
}
|
||||
|
||||
void X11Window::MaybeUpdateSyncCounter() {
|
||||
if (last_configure_size_ == last_swapped_size_) {
|
||||
connection_->sync().SetCounter(update_counter_, *configure_counter_value_);
|
||||
configure_counter_value_.reset();
|
||||
last_configure_size_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
@ -36,6 +36,11 @@
|
||||
|
||||
class SkPath;
|
||||
|
||||
namespace x11 {
|
||||
class GeometryCache;
|
||||
class WmSync;
|
||||
} // namespace x11
|
||||
|
||||
namespace ui {
|
||||
|
||||
class PlatformWindowDelegate;
|
||||
@ -134,12 +139,13 @@ class X11Window : public PlatformWindow,
|
||||
// X11Extension:
|
||||
bool IsSyncExtensionAvailable() const override;
|
||||
bool IsWmTiling() const override;
|
||||
void OnCompleteSwapAfterResize() override;
|
||||
void OnCompleteSwapAfterResize(const gfx::Size& new_size) override;
|
||||
gfx::Rect GetXRootWindowOuterBounds() const override;
|
||||
void LowerXWindow() override;
|
||||
void SetOverrideRedirect(bool override_redirect) override;
|
||||
bool CanResetOverrideRedirect() const override;
|
||||
void SetX11ExtensionDelegate(X11ExtensionDelegate* delegate) override;
|
||||
bool IsWmSyncActiveForTest() override;
|
||||
|
||||
// x11::EventObserver:
|
||||
void OnEvent(const x11::Event& event) override;
|
||||
@ -283,8 +289,7 @@ class X11Window : public PlatformWindow,
|
||||
// Called when |xwindow_|'s _NET_FRAME_EXTENTS property is updated.
|
||||
void OnFrameExtentsUpdated();
|
||||
|
||||
void OnConfigureEvent(const x11::ConfigureNotifyEvent& event,
|
||||
bool send_event);
|
||||
void OnConfigureEvent(const x11::ConfigureNotifyEvent& event);
|
||||
|
||||
void OnWorkspaceUpdated();
|
||||
|
||||
@ -323,6 +328,15 @@ class X11Window : public PlatformWindow,
|
||||
// Initializes as a status icon window.
|
||||
bool InitializeAsStatusIcon();
|
||||
|
||||
void SetBoundsWithWmSync(const gfx::Rect& bounds_px);
|
||||
|
||||
void OnWmSynced();
|
||||
|
||||
void OnBoundsChanged(const std::optional<gfx::Rect>& old_bounds_px,
|
||||
const gfx::Rect& new_bounds_px);
|
||||
|
||||
void MaybeUpdateSyncCounter();
|
||||
|
||||
// Stores current state of this window.
|
||||
PlatformWindowState state_ = PlatformWindowState::kUnknown;
|
||||
|
||||
@ -393,8 +407,12 @@ class X11Window : public PlatformWindow,
|
||||
// Whether the window is mapped with respect to the X server.
|
||||
bool window_mapped_in_server_ = false;
|
||||
|
||||
// The bounds of |xwindow_|.
|
||||
gfx::Rect bounds_in_pixels_;
|
||||
// The bounds of `xwindow_`. If `bounds_wm_sync_` is active, then
|
||||
// `last_set_bounds_px_` should be treated as the current bounds. Otherwise,
|
||||
// the bounds from `geometry_cache_` should be used.
|
||||
gfx::Rect last_set_bounds_px_;
|
||||
std::unique_ptr<x11::WmSync> bounds_wm_sync_;
|
||||
std::unique_ptr<x11::GeometryCache> geometry_cache_;
|
||||
|
||||
x11::VisualId visual_id_{};
|
||||
|
||||
@ -450,15 +468,6 @@ class X11Window : public PlatformWindow,
|
||||
bool had_pointer_grab_ = false;
|
||||
bool had_window_focus_ = false;
|
||||
|
||||
// Whenever the bounds are set, we keep the previous set of bounds around so
|
||||
// we can have a better chance of getting the real
|
||||
// |restored_bounds_in_pixels_|. Window managers tend to send a Configure
|
||||
// message with the maximized bounds, and then set the window maximized
|
||||
// property. (We don't rely on this for when we request that the window be
|
||||
// maximized, only when we detect that some other process has requested that
|
||||
// we become the maximized window.)
|
||||
gfx::Rect previous_bounds_in_pixels_;
|
||||
|
||||
// True if a Maximize() call should be done after mapping the window.
|
||||
bool should_maximize_after_map_ = false;
|
||||
|
||||
@ -490,22 +499,10 @@ class X11Window : public PlatformWindow,
|
||||
gfx::Insets native_window_frame_borders_in_pixels_;
|
||||
|
||||
// Used for synchronizing between `xwindow_` and the WM during resizing.
|
||||
std::optional<x11::Sync::Int64> configure_counter_value_;
|
||||
bool have_configure_ = false;
|
||||
x11::Sync::Counter update_counter_{};
|
||||
|
||||
// Used for ignoring bounds changes during the fullscreening process. For
|
||||
// cross-display fullscreening, there is a Restore() (called by BrowserView)
|
||||
// that may cause configuration bounds updates that make this window appear to
|
||||
// temporarily be on a different screen than its destination screen. This
|
||||
// restore only happens if the window is maximized. The integer represents how
|
||||
// many events to ignore.
|
||||
int ignore_next_configures_ = 0;
|
||||
// True between Restore() and the next OnXWindowStateChanged().
|
||||
bool restore_in_flight_ = false;
|
||||
// True between SetBoundsInPixels (when the bounds actually change) and the
|
||||
// next OnConfigureEvent.
|
||||
bool bounds_change_in_flight_ = false;
|
||||
std::optional<x11::Sync::Int64> configure_counter_value_;
|
||||
std::optional<gfx::Size> last_configure_size_;
|
||||
std::optional<gfx::Size> last_swapped_size_;
|
||||
|
||||
base::CancelableOnceClosure delayed_resize_task_;
|
||||
|
||||
|
@ -27,7 +27,7 @@ class COMPONENT_EXPORT(PLATFORM_WINDOW) X11Extension {
|
||||
virtual bool IsWmTiling() const = 0;
|
||||
|
||||
// Handles CompleteSwapAfterResize event coming from the compositor observer.
|
||||
virtual void OnCompleteSwapAfterResize() = 0;
|
||||
virtual void OnCompleteSwapAfterResize(const gfx::Size& new_size) = 0;
|
||||
|
||||
// Returns the current bounds in terms of the X11 Root Window including the
|
||||
// borders provided by the window manager (if any).
|
||||
@ -46,6 +46,9 @@ class COMPONENT_EXPORT(PLATFORM_WINDOW) X11Extension {
|
||||
|
||||
virtual void SetX11ExtensionDelegate(X11ExtensionDelegate* delegate) = 0;
|
||||
|
||||
// Indicates whether a WmSync is pending. Should only be used for testing.
|
||||
virtual bool IsWmSyncActiveForTest() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~X11Extension();
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "ui/aura/window_tree_host_platform.h"
|
||||
#include "ui/ozone/public/ozone_platform.h"
|
||||
#include "ui/platform_window/extensions/wayland_extension.h"
|
||||
#include "ui/platform_window/extensions/x11_extension.h"
|
||||
|
||||
#endif // BUILDFLAG(IS_LINUX)
|
||||
|
||||
@ -48,11 +49,11 @@ AsyncWidgetRequestWaiter::~AsyncWidgetRequestWaiter() {
|
||||
void AsyncWidgetRequestWaiter::Wait() {
|
||||
CHECK(!waited_) << "`Wait` may only be called once.";
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
auto* host = aura::WindowTreeHostPlatform::GetHostForWindow(
|
||||
widget_->GetNativeWindow());
|
||||
if (ui::OzonePlatform::GetPlatformNameForTest() == "wayland") {
|
||||
// Wait for a Wayland roundtrip to ensure all side effects have been
|
||||
// processed.
|
||||
auto* host = aura::WindowTreeHostPlatform::GetHostForWindow(
|
||||
widget_->GetNativeWindow());
|
||||
auto* wayland_extension = ui::GetWaylandExtension(*host->platform_window());
|
||||
wayland_extension->RoundTripQueue();
|
||||
|
||||
@ -72,6 +73,15 @@ void AsyncWidgetRequestWaiter::Wait() {
|
||||
// to be processed on the server side.
|
||||
wayland_extension->RoundTripQueue();
|
||||
wayland_extension->SetLatchImmediately(true);
|
||||
} else if (ui::OzonePlatform::GetPlatformNameForTest() == "x11") {
|
||||
// Setting the window bounds on X11 is asynchronous, so the platform window
|
||||
// pretends the bounds change completed successfully until it can sync with
|
||||
// the WM. This may cause inconsistent state with respect to other caches
|
||||
// such as x11::WindowCache. Wait for the WM sync to complete to ensure
|
||||
// consistency.
|
||||
auto* x11_extension = ui::GetX11Extension(*host->platform_window());
|
||||
CHECK(base::test::RunUntil(
|
||||
[&]() { return !x11_extension->IsWmSyncActiveForTest(); }));
|
||||
} else {
|
||||
NOTIMPLEMENTED_LOG_ONCE();
|
||||
}
|
||||
|
@ -348,7 +348,7 @@ DesktopWindowTreeHostLinux::GetKeyboardLayoutMap() {
|
||||
void DesktopWindowTreeHostLinux::OnCompleteSwapWithNewSize(
|
||||
const gfx::Size& size) {
|
||||
if (GetX11Extension()) {
|
||||
GetX11Extension()->OnCompleteSwapAfterResize();
|
||||
GetX11Extension()->OnCompleteSwapAfterResize(size);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user