commit 48aa2404e61655d5597e7056b758b0b6a45c8f2d
parent 057c945f2af107bb8b34eb4bfd1645117274c14e
Author: Emilio Cobos Álvarez <emilio@crisal.io>
Date: Tue, 6 Jan 2026 12:22:51 +0000
Bug 2006480 - Factor client area + margin into its own struct. r=stransky
No behavior change but makes it a bit easier to reason about, and allows
us to compute the bounds out of band if we need to.
Differential Revision: https://phabricator.services.mozilla.com/D277320
Diffstat:
2 files changed, 94 insertions(+), 78 deletions(-)
diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp
@@ -3299,10 +3299,10 @@ LayoutDeviceIntCoord GetXWindowBorder(GdkWindow* aWin) {
top corner, so matches CSD size - (40,40).
*/
#ifdef MOZ_X11
-void nsWindow::RecomputeBoundsX11() {
- LOG("RecomputeBoundsX11()");
+auto nsWindow::Bounds::ComputeX11(const nsWindow* aWindow) -> Bounds {
+ LOG_WIN(aWindow, "Bounds::ComputeX11()");
- auto* toplevel = GetToplevelGdkWindow();
+ auto* toplevel = aWindow->GetToplevelGdkWindow();
// Window position and size with window decoration AND system titlebar.
auto GetFrameTitlebarBounds = [&](GdkWindow* aWin) {
@@ -3310,12 +3310,13 @@ void nsWindow::RecomputeBoundsX11() {
gdk_window_get_frame_extents(aWin, &b);
if (gtk_check_version(3, 24, 35) &&
gdk_window_get_window_type(aWin) == GDK_WINDOW_TEMP) {
- LOGVERBOSE(
+ LOG_WIN(
+ aWindow,
" GetFrameTitlebarBounds gtk 3.24.35 & GDK_WINDOW_TEMP workaround");
// Workaround for https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/4820
// Bug 1775017 Gtk < 3.24.35 returns scaled values for
// override redirected window on X11.
- double scale = FractionalScaleFactor();
+ double scale = aWindow->FractionalScaleFactor();
return DesktopIntRect(int(round(b.x / scale)), int(round(b.y / scale)),
int(round(b.width / scale)),
int(round(b.height / scale)));
@@ -3323,11 +3324,11 @@ void nsWindow::RecomputeBoundsX11() {
auto result = DesktopIntRect(b.x, b.y, b.width, b.height);
if (gtk_check_version(3, 24, 50)) {
if (auto border = GetXWindowBorder(aWin)) {
- LOGVERBOSE(" GetFrameTitlebarBounds gtk 3.24.50 workaround");
+ LOG_WIN(aWindow, " GetFrameTitlebarBounds gtk 3.24.50 workaround");
// Workaround for
// https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/8423
// Bug 1958174 Gtk doesn't account for window border sizes on X11.
- double scale = FractionalScaleFactor();
+ double scale = aWindow->FractionalScaleFactor();
result.width += 2 * border / scale;
result.height += 2 * border / scale;
}
@@ -3338,7 +3339,7 @@ void nsWindow::RecomputeBoundsX11() {
// Window position and size with decoration but WITHOUT system titlebar.
auto GetBounds = [&](GdkWindow* aWin) {
GdkRectangle b{0};
- if (IsTopLevelWidget() && aWin == toplevel) {
+ if (aWindow->IsTopLevelWidget() && aWin == toplevel) {
// We want the up-to-date size from the X server, not the last configure
// event size, to avoid spurious resizes on e.g. sizemode changes.
gdk_window_get_geometry(aWin, nullptr, nullptr, &b.width, &b.height);
@@ -3352,11 +3353,11 @@ void nsWindow::RecomputeBoundsX11() {
};
const auto toplevelBoundsWithTitlebar = GetFrameTitlebarBounds(toplevel);
- const auto toplevelBounds = GetBounds(toplevel);
+ LOG_WIN(aWindow, " toplevelBoundsWithTitlebar %s",
+ ToString(toplevelBoundsWithTitlebar).c_str());
- LOGVERBOSE(" toplevelBoundsWithTitlebar %s",
- ToString(toplevelBoundsWithTitlebar).c_str());
- LOGVERBOSE(" toplevelBounds %s", ToString(toplevelBounds).c_str());
+ const auto toplevelBounds = GetBounds(toplevel);
+ LOG_WIN(aWindow, " toplevelBounds %s", ToString(toplevelBounds).c_str());
// Offset from system decoration to gtk decoration.
const auto systemDecorationOffset = [&] {
@@ -3376,31 +3377,35 @@ void nsWindow::RecomputeBoundsX11() {
return offset;
}();
+ Bounds result;
// This is relative to our parent window, that is, to topLevelBounds.
- mClientArea = GetBounds(mGdkWindow);
+ result.mClientArea = GetBounds(aWindow->GetGdkWindow());
// Make it relative to topLevelBoundsWithTitlebar
- mClientArea.MoveBy(systemDecorationOffset);
- LOGVERBOSE(" mClientArea %s", ToString(mClientArea).c_str());
+ result.mClientArea.MoveBy(systemDecorationOffset);
+ LOG_WIN(aWindow, " mClientArea %s", ToString(result.mClientArea).c_str());
- if (mClientArea.X() < 0 || mClientArea.Y() < 0 || mClientArea.Width() <= 1 ||
- mClientArea.Height() <= 1) {
+ if (result.mClientArea.X() < 0 || result.mClientArea.Y() < 0 ||
+ result.mClientArea.Width() <= 1 || result.mClientArea.Height() <= 1) {
// If we don't have gdkwindow bounds, assume we take the whole toplevel
// except system decorations.
- mClientArea = DesktopIntRect(systemDecorationOffset, toplevelBounds.Size());
+ result.mClientArea =
+ DesktopIntRect(systemDecorationOffset, toplevelBounds.Size());
}
- mClientMargin =
+ result.mClientMargin =
DesktopIntRect(DesktopIntPoint(), toplevelBoundsWithTitlebar.Size()) -
- mClientArea;
- mClientMargin.EnsureAtLeast(DesktopIntMargin());
+ result.mClientArea;
+ result.mClientMargin.EnsureAtLeast(DesktopIntMargin());
// We want mClientArea in global coordinates. We derive everything from here,
// so move it to global coords.
- mClientArea.MoveBy(toplevelBoundsWithTitlebar.TopLeft());
+ result.mClientArea.MoveBy(toplevelBoundsWithTitlebar.TopLeft());
+ return result;
}
#endif
#ifdef MOZ_WAYLAND
-void nsWindow::RecomputeBoundsWayland() {
+auto nsWindow::Bounds::ComputeWayland(const nsWindow* aWindow) -> Bounds {
+ LOG_WIN(aWindow, "Bounds::ComputeWayland()");
auto GetBounds = [&](GdkWindow* aWin) {
GdkRectangle b{0};
gdk_window_get_position(aWin, &b.x, &b.y);
@@ -3409,27 +3414,41 @@ void nsWindow::RecomputeBoundsWayland() {
return DesktopIntRect(b.x, b.y, b.width, b.height);
};
- const auto toplevelBounds = GetBounds(GetToplevelGdkWindow());
- mClientArea = GetBounds(mGdkWindow);
+ const auto toplevelBounds = GetBounds(aWindow->GetToplevelGdkWindow());
+ LOG_WIN(aWindow, " toplevelBounds %s", ToString(toplevelBounds).c_str());
+ Bounds result;
+ result.mClientArea = GetBounds(aWindow->GetGdkWindow());
+ LOG_WIN(aWindow, " bounds %s", ToString(result.mClientArea).c_str());
- LOG("RecomputeBoundsWayland() GetBounds(mGdkWindow) [%d,%d] -> [%d x %d] "
- "GetBounds(mShell) [%d,%d] -> [%d x %d]",
- mClientArea.x, mClientArea.y, mClientArea.width, mClientArea.height,
- toplevelBounds.x, toplevelBounds.y, toplevelBounds.width,
- toplevelBounds.height);
-
- if (mClientArea.X() < 0 || mClientArea.Y() < 0 || mClientArea.Width() <= 1 ||
- mClientArea.Height() <= 1) {
+ if (result.mClientArea.X() < 0 || result.mClientArea.Y() < 0 ||
+ result.mClientArea.Width() <= 1 || result.mClientArea.Height() <= 1) {
// If we don't have gdkwindow bounds yet, assume we take the whole toplevel.
- mClientArea = toplevelBounds;
+ result.mClientArea = toplevelBounds;
}
- mClientMargin =
- DesktopIntRect(DesktopIntPoint(), toplevelBounds.Size()) - mClientArea;
- mClientMargin.EnsureAtLeast(DesktopIntMargin());
+ result.mClientMargin =
+ DesktopIntRect(DesktopIntPoint(), toplevelBounds.Size()) -
+ result.mClientArea;
+ result.mClientMargin.EnsureAtLeast(DesktopIntMargin());
+ return result;
}
#endif
+auto nsWindow::Bounds::Compute(const nsWindow* aWindow) -> Bounds {
+#ifdef MOZ_X11
+ if (GdkIsX11Display()) {
+ return ComputeX11(aWindow);
+ }
+#endif
+#ifdef MOZ_WAYLAND
+ if (GdkIsWaylandDisplay()) {
+ return ComputeWayland(aWindow);
+ }
+#endif
+ MOZ_ASSERT_UNREACHABLE("How?");
+ return {};
+}
+
void nsWindow::RecomputeBounds(bool aScaleChange) {
LOG("RecomputeBounds() scale change %d", aScaleChange);
mPendingBoundsChange = false;
@@ -3439,19 +3458,11 @@ void nsWindow::RecomputeBounds(bool aScaleChange) {
return;
}
- const auto oldClientArea = mClientArea;
const auto oldMargin = mClientMargin;
-
-#ifdef MOZ_X11
- if (GdkIsX11Display()) {
- RecomputeBoundsX11();
- }
-#endif
-#ifdef MOZ_WAYLAND
- if (GdkIsWaylandDisplay()) {
- RecomputeBoundsWayland();
- }
-#endif
+ const auto oldClientArea = mClientArea;
+ const auto newBounds = Bounds::Compute(this);
+ mClientArea = newBounds.mClientArea;
+ mClientMargin = newBounds.mClientMargin;
if (IsPopup()) {
// Popup windows is not moved by the window manager, and so any change in
@@ -9066,7 +9077,7 @@ gint nsWindow::GdkCeiledScaleFactor() {
return ScreenHelperGTK::GetGTKMonitorScaleFactor();
}
-double nsWindow::FractionalScaleFactor() {
+double nsWindow::FractionalScaleFactor() const {
#ifdef MOZ_WAYLAND
if (mSurface) {
auto scale = mSurface->GetScale();
diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h
@@ -60,10 +60,11 @@ extern mozilla::LazyLogModule gWidgetPopupLog;
extern mozilla::LazyLogModule gWidgetVsync;
extern mozilla::LazyLogModule gWidgetWaylandLog;
-# define LOG(str, ...) \
- MOZ_LOG(IsPopup() ? gWidgetPopupLog : gWidgetLog, \
- mozilla::LogLevel::Debug, \
- ("%s: " str, GetDebugTag().get(), ##__VA_ARGS__))
+# define LOG_WIN(win, str, ...) \
+ MOZ_LOG(win->IsPopup() ? gWidgetPopupLog : gWidgetLog, \
+ mozilla::LogLevel::Debug, \
+ ("%s: " str, win->GetDebugTag().get(), ##__VA_ARGS__))
+# define LOG(...) LOG_WIN(this, __VA_ARGS__)
# define LOGVERBOSE(str, ...) \
MOZ_LOG(IsPopup() ? gWidgetPopupLog : gWidgetLog, \
mozilla::LogLevel::Verbose, \
@@ -88,6 +89,7 @@ extern mozilla::LazyLogModule gWidgetWaylandLog;
#else
# define LOG(...)
+# define LOG_WIN(...)
# define LOGVERBOSE(...)
# define LOGW(...)
# define LOGDRAG(...)
@@ -224,12 +226,33 @@ class nsWindow final : public nsIWidget {
// Recomputes the bounds according to our current window position. Dispatches
// move / resizes as needed.
void RecomputeBounds(bool aScaleChange = false);
+ // Window bounds (as in GetBounds()) are composed as
+ // mClientArea.Inflate(mClientMargin)*scale, i.e.:
+ //
+ // mBounds.x = (mClientArea.x - mClientMargin.left) * scale;
+ // mBounds.y = (mClientArea.y - mClientMargin.top) * scale;
+ // mBounds.width = (mClientArea.width +
+ // (mClientMargin.right + mClientMargin.left)) * scale;
+ // mBounds.height = (mClientArea.height +
+ // (mClientMargin.top + mClientMargin.bottom)) * scale;
+ //
+ // We use mClientMargin and mClientArea in Gdk (logical, widget) coordinates
+ // instead of device pixel coordinates to avoid rounding errors.
+ struct Bounds {
+ // mClientArea is window rendering area in global coordinates.
+ DesktopIntRect mClientArea;
+ // mClientMargin contains CSD decorations size on Wayland and
+ // CSD decorations and system titlebar size on X11.
+ DesktopIntMargin mClientMargin;
+
+ static Bounds Compute(const nsWindow*);
#ifdef MOZ_X11
- void RecomputeBoundsX11();
+ static Bounds ComputeX11(const nsWindow*);
#endif
#ifdef MOZ_WAYLAND
- void RecomputeBoundsWayland();
+ static Bounds ComputeWayland(const nsWindow*);
#endif
+ };
void SchedulePendingBounds();
void MaybeRecomputeBounds();
@@ -436,7 +459,7 @@ class nsWindow final : public nsIWidget {
// HiDPI scale conversion
gint GdkCeiledScaleFactor();
- double FractionalScaleFactor();
+ double FractionalScaleFactor() const;
LayoutDeviceIntPoint ToLayoutDevicePixels(const DesktopIntPoint&);
LayoutDeviceIntSize ToLayoutDevicePixels(const DesktopIntSize&);
@@ -638,27 +661,9 @@ class nsWindow final : public nsIWidget {
// Same but for positioning. Used to track move requests.
DesktopIntPoint mLastMoveRequest;
- // Window bounds (mBounds) are composed as
- // mClientArea.Inflate(mClientMargin)*scale, i.e.:
- //
- // mBounds.x = (mClientArea.x - mClientMargin.left) * scale;
- // mBounds.y = (mClientArea.y - mClientMargin.top) * scale;
- // mBounds.width = (mClientArea.width +
- // (mClientMargin.right + mClientMargin.left)) * scale;
- // mBounds.height = (mClientArea.height +
- // (mClientMargin.top + mClientMargin.bottom)) * scale;
- //
- // We use mClientMargin and mClientArea in Gdk (logical, widget) coordinates
- // instead of mBounds i device pixel coordinates to avoid
- // rounding errors.
-
- // mClientMargin contains CSD decorations size on Wayland and
- // CSD decorations and system titlebar size on X11.
- DesktopIntMargin mClientMargin{};
-
- // mClientArea is window rendering area. mClientArea.x, mClientArea.y are
- // equal to mClientMargin.left, mClientMargin.top.
- DesktopIntRect mClientArea{};
+ // See Bounds for these members.
+ DesktopIntRect mClientArea;
+ DesktopIntMargin mClientMargin;
// This field omits duplicate scroll events caused by GNOME bug 726878.
guint32 mLastScrollEventTime = GDK_CURRENT_TIME;