firefox/D49289-wayland-monitor-size...

371 lines
15 KiB
Diff

diff --git a/layout/xul/nsMenuPopupFrame.cpp b/layout/xul/nsMenuPopupFrame.cpp
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -1502,7 +1502,7 @@
nscoord oldAlignmentOffset = mAlignmentOffset;
- bool inWayland = false;
+ static bool inWayland = false;
#ifdef MOZ_WAYLAND
inWayland = !GDK_IS_X11_DISPLAY(gdk_display_get_default());
#endif
@@ -1512,9 +1512,9 @@
// However, if a panel is already constrained or flipped (mIsOffset), then we
// want to continue to calculate this. Also, always do this for content
// shells, so that the popup doesn't extend outside the containing frame.
- if (!inWayland && (mInContentShell || (mFlip != FlipType_None &&
- (!aIsMove || mIsOffset ||
- mPopupType != ePopupTypePanel)))) {
+ if (mInContentShell ||
+ (mFlip != FlipType_None &&
+ (!aIsMove || mIsOffset || mPopupType != ePopupTypePanel))) {
int32_t appPerDev = presContext->AppUnitsPerDevPixel();
LayoutDeviceIntRect anchorRectDevPix =
LayoutDeviceIntRect::FromAppUnitsToNearest(anchorRect, appPerDev);
@@ -1532,60 +1532,66 @@
if (mRect.width > screenRect.width) mRect.width = screenRect.width;
if (mRect.height > screenRect.height) mRect.height = screenRect.height;
- // at this point the anchor (anchorRect) is within the available screen
- // area (screenRect) and the popup is known to be no larger than the screen.
+ // We can't get the subsequent change of the popup position under
+ // waylande where gdk_window_move_to_rect is used to place them
+ // because we don't know the absolute position of the window on the screen.
+ if (!inWayland) {
+ // at this point the anchor (anchorRect) is within the available screen
+ // area (screenRect) and the popup is known to be no larger than the
+ // screen.
- // We might want to "slide" an arrow if the panel is of the correct type -
- // but we can only slide on one axis - the other axis must be "flipped or
- // resized" as normal.
- bool slideHorizontal = false, slideVertical = false;
- if (mFlip == FlipType_Slide) {
- int8_t position = GetAlignmentPosition();
- slideHorizontal = position >= POPUPPOSITION_BEFORESTART &&
- position <= POPUPPOSITION_AFTEREND;
- slideVertical = position >= POPUPPOSITION_STARTBEFORE &&
- position <= POPUPPOSITION_ENDAFTER;
- }
+ // We might want to "slide" an arrow if the panel is of the correct type -
+ // but we can only slide on one axis - the other axis must be "flipped or
+ // resized" as normal.
+ bool slideHorizontal = false, slideVertical = false;
+ if (mFlip == FlipType_Slide) {
+ int8_t position = GetAlignmentPosition();
+ slideHorizontal = position >= POPUPPOSITION_BEFORESTART &&
+ position <= POPUPPOSITION_AFTEREND;
+ slideVertical = position >= POPUPPOSITION_STARTBEFORE &&
+ position <= POPUPPOSITION_ENDAFTER;
+ }
- // Next, check if there is enough space to show the popup at full size when
- // positioned at screenPoint. If not, flip the popups to the opposite side
- // of their anchor point, or resize them as necessary.
- bool endAligned = IsDirectionRTL()
- ? mPopupAlignment == POPUPALIGNMENT_TOPLEFT ||
- mPopupAlignment == POPUPALIGNMENT_BOTTOMLEFT
- : mPopupAlignment == POPUPALIGNMENT_TOPRIGHT ||
- mPopupAlignment == POPUPALIGNMENT_BOTTOMRIGHT;
- nscoord preOffsetScreenPoint = screenPoint.x;
- if (slideHorizontal) {
- mRect.width = SlideOrResize(screenPoint.x, mRect.width, screenRect.x,
- screenRect.XMost(), &mAlignmentOffset);
- } else {
- mRect.width = FlipOrResize(
- screenPoint.x, mRect.width, screenRect.x, screenRect.XMost(),
- anchorRect.x, anchorRect.XMost(), margin.left, margin.right,
- offsetForContextMenu.x, hFlip, endAligned, &mHFlip);
- }
- mIsOffset = preOffsetScreenPoint != screenPoint.x;
+ // Next, check if there is enough space to show the popup at full size
+ // when positioned at screenPoint. If not, flip the popups to the opposite
+ // side of their anchor point, or resize them as necessary.
+ bool endAligned = IsDirectionRTL()
+ ? mPopupAlignment == POPUPALIGNMENT_TOPLEFT ||
+ mPopupAlignment == POPUPALIGNMENT_BOTTOMLEFT
+ : mPopupAlignment == POPUPALIGNMENT_TOPRIGHT ||
+ mPopupAlignment == POPUPALIGNMENT_BOTTOMRIGHT;
+ nscoord preOffsetScreenPoint = screenPoint.x;
+ if (slideHorizontal) {
+ mRect.width = SlideOrResize(screenPoint.x, mRect.width, screenRect.x,
+ screenRect.XMost(), &mAlignmentOffset);
+ } else {
+ mRect.width = FlipOrResize(
+ screenPoint.x, mRect.width, screenRect.x, screenRect.XMost(),
+ anchorRect.x, anchorRect.XMost(), margin.left, margin.right,
+ offsetForContextMenu.x, hFlip, endAligned, &mHFlip);
+ }
+ mIsOffset = preOffsetScreenPoint != screenPoint.x;
- endAligned = mPopupAlignment == POPUPALIGNMENT_BOTTOMLEFT ||
- mPopupAlignment == POPUPALIGNMENT_BOTTOMRIGHT;
- preOffsetScreenPoint = screenPoint.y;
- if (slideVertical) {
- mRect.height = SlideOrResize(screenPoint.y, mRect.height, screenRect.y,
- screenRect.YMost(), &mAlignmentOffset);
- } else {
- mRect.height = FlipOrResize(
- screenPoint.y, mRect.height, screenRect.y, screenRect.YMost(),
- anchorRect.y, anchorRect.YMost(), margin.top, margin.bottom,
- offsetForContextMenu.y, vFlip, endAligned, &mVFlip);
+ endAligned = mPopupAlignment == POPUPALIGNMENT_BOTTOMLEFT ||
+ mPopupAlignment == POPUPALIGNMENT_BOTTOMRIGHT;
+ preOffsetScreenPoint = screenPoint.y;
+ if (slideVertical) {
+ mRect.height = SlideOrResize(screenPoint.y, mRect.height, screenRect.y,
+ screenRect.YMost(), &mAlignmentOffset);
+ } else {
+ mRect.height = FlipOrResize(
+ screenPoint.y, mRect.height, screenRect.y, screenRect.YMost(),
+ anchorRect.y, anchorRect.YMost(), margin.top, margin.bottom,
+ offsetForContextMenu.y, vFlip, endAligned, &mVFlip);
+ }
+ mIsOffset = mIsOffset || (preOffsetScreenPoint != screenPoint.y);
+
+ NS_ASSERTION(screenPoint.x >= screenRect.x &&
+ screenPoint.y >= screenRect.y &&
+ screenPoint.x + mRect.width <= screenRect.XMost() &&
+ screenPoint.y + mRect.height <= screenRect.YMost(),
+ "Popup is offscreen");
}
- mIsOffset = mIsOffset || (preOffsetScreenPoint != screenPoint.y);
-
- NS_ASSERTION(screenPoint.x >= screenRect.x &&
- screenPoint.y >= screenRect.y &&
- screenPoint.x + mRect.width <= screenRect.XMost() &&
- screenPoint.y + mRect.height <= screenRect.YMost(),
- "Popup is offscreen");
}
// snap the popup's position in screen coordinates to device pixels,
@@ -1687,6 +1693,14 @@
screen->GetAvailRect(&screenRectPixels.x, &screenRectPixels.y,
&screenRectPixels.width, &screenRectPixels.height);
}
+#ifdef MOZ_WAYLAND
+ else {
+ if (GetWidget() &&
+ GetWidget()->GetScreenRect(&screenRectPixels) != NS_OK) {
+ NS_WARNING("Cannot get screen rect from widget!");
+ }
+ }
+#endif
}
if (mInContentShell) {
diff --git a/widget/ScreenManager.cpp b/widget/ScreenManager.cpp
--- a/widget/ScreenManager.cpp
+++ b/widget/ScreenManager.cpp
@@ -11,6 +11,11 @@
#include "mozilla/dom/DOMTypes.h"
#include "mozilla/Logging.h"
#include "mozilla/StaticPtr.h"
+#ifdef MOZ_WAYLAND
+# include <gdk/gdk.h>
+# include <gdk/gdkx.h>
+# include <gdk/gdkwayland.h>
+#endif /* MOZ_WAYLAND */
static mozilla::LazyLogModule sScreenLog("WidgetScreen");
@@ -104,6 +109,15 @@
NS_IMETHODIMP
ScreenManager::ScreenForRect(int32_t aX, int32_t aY, int32_t aWidth,
int32_t aHeight, nsIScreen** aOutScreen) {
+#ifdef MOZ_WAYLAND
+ static bool inWayland = !GDK_IS_X11_DISPLAY(gdk_display_get_default());
+
+ if (inWayland) {
+ *aOutScreen = nullptr;
+ return NS_OK;
+ }
+#endif
+
if (mScreenList.IsEmpty()) {
MOZ_LOG(sScreenLog, LogLevel::Warning,
("No screen available. This can happen in xpcshell."));
diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -398,6 +398,9 @@
static bool HideTitlebarByDefault();
static bool GetTopLevelWindowActiveState(nsIFrame* aFrame);
static bool TitlebarCanUseShapeMask();
+#ifdef MOZ_WAYLAND
+ virtual nsresult GetScreenRect(LayoutDeviceIntRect* aRect) override;
+#endif
protected:
virtual ~nsWindow();
@@ -630,6 +633,7 @@
void HideWaylandTooltips();
void HideWaylandPopupAndAllChildren();
void CleanupWaylandPopups();
+ GtkWindow* GetCurrentTopmostWindow();
/**
* |mIMContext| takes all IME related stuff.
diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -1294,10 +1294,14 @@
GdkWindow* window, const GdkRectangle* flipped_rect,
const GdkRectangle* final_rect, gboolean flipped_x, gboolean flipped_y,
void* aWindow) {
- LOG(("%s [%p] flipped %d %d\n", __FUNCTION__, aWindow, flipped_rect->x,
- flipped_rect->y));
- LOG(("%s [%p] final %d %d\n", __FUNCTION__, aWindow, final_rect->x,
- final_rect->y));
+ LOG(("%s [%p] flipped_x %d flipped_y %d\n", __FUNCTION__, aWindow, flipped_x,
+ flipped_y));
+
+ LOG(("%s [%p] flipped %d %d w:%d h:%d\n", __FUNCTION__, aWindow,
+ flipped_rect->x, flipped_rect->y, flipped_rect->width,
+ flipped_rect->height));
+ LOG(("%s [%p] final %d %d w:%d h:%d\n", __FUNCTION__, aWindow, final_rect->x,
+ final_rect->y, final_rect->width, final_rect->height));
}
#endif
@@ -1384,6 +1388,16 @@
HideWaylandWindow();
}
+ LOG(
+ ("nsWindow::NativeMoveResizeWaylandPopup [%p]: requested rect: x%d y%d "
+ "w%d h%d\n",
+ this, rect.x, rect.y, rect.width, rect.height));
+ if (aSize) {
+ LOG((" aSize: x%d y%d w%d h%d\n", aSize->x, aSize->y, aSize->width,
+ aSize->height));
+ } else {
+ LOG((" No aSize given"));
+ }
sGdkWindowMoveToRect(gdkWindow, &rect, rectAnchor, menuAnchor, hints, 0, 0);
if (isWidgetVisible) {
@@ -1399,7 +1413,8 @@
LOG(("nsWindow::NativeMove [%p] %d %d\n", (void*)this, point.x, point.y));
if (IsWaylandPopup()) {
- NativeMoveResizeWaylandPopup(&point, nullptr);
+ GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size());
+ NativeMoveResizeWaylandPopup(&point, &size);
} else if (mIsTopLevel) {
gtk_window_move(GTK_WINDOW(mShell), point.x, point.y);
} else if (mGdkWindow) {
@@ -6724,6 +6739,16 @@
}
}
+GtkWindow* nsWindow::GetCurrentTopmostWindow() {
+ GtkWindow* parentWindow = GTK_WINDOW(GetGtkWidget());
+ GtkWindow* topmostParentWindow;
+ while (parentWindow) {
+ topmostParentWindow = parentWindow;
+ parentWindow = gtk_window_get_transient_for(parentWindow);
+ }
+ return topmostParentWindow;
+}
+
gint nsWindow::GdkScaleFactor() {
GdkWindow* scaledGdkWindow = mGdkWindow;
if (!mIsX11Display) {
@@ -6732,12 +6757,7 @@
// not updated during it's hidden.
if (mWindowType == eWindowType_popup || mWindowType == eWindowType_dialog) {
// Get toplevel window for scale factor:
- GtkWindow* parentWindow = GTK_WINDOW(GetGtkWidget());
- GtkWindow* topmostParentWindow;
- while (parentWindow) {
- topmostParentWindow = parentWindow;
- parentWindow = gtk_window_get_transient_for(parentWindow);
- }
+ GtkWindow* topmostParentWindow = GetCurrentTopmostWindow();
if (topmostParentWindow) {
scaledGdkWindow =
gtk_widget_get_window(GTK_WIDGET(topmostParentWindow));
@@ -7268,6 +7288,41 @@
return window.forget();
}
+#ifdef MOZ_WAYLAND
+nsresult nsWindow::GetScreenRect(LayoutDeviceIntRect* aRect) {
+ typedef struct _GdkMonitor GdkMonitor;
+ static auto s_gdk_display_get_monitor_at_window =
+ (GdkMonitor * (*)(GdkDisplay*, GdkWindow*))
+ dlsym(RTLD_DEFAULT, "gdk_display_get_monitor_at_window");
+
+ static auto s_gdk_monitor_get_workarea =
+ (void (*)(GdkMonitor*, GdkRectangle*))dlsym(RTLD_DEFAULT,
+ "gdk_monitor_get_workarea");
+
+ if (!s_gdk_display_get_monitor_at_window || !s_gdk_monitor_get_workarea) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ GtkWindow* topmostParentWindow = GetCurrentTopmostWindow();
+ GdkWindow* gdkWindow = gtk_widget_get_window(GTK_WIDGET(topmostParentWindow));
+
+ GdkMonitor* monitor =
+ s_gdk_display_get_monitor_at_window(gdk_display_get_default(), gdkWindow);
+ if (monitor) {
+ GdkRectangle workArea;
+ s_gdk_monitor_get_workarea(monitor, &workArea);
+ aRect->x = workArea.x;
+ aRect->y = workArea.y;
+ aRect->width = workArea.width;
+ aRect->height = workArea.height;
+ LOG((" workarea for [%p], monitor %p: x%d y%d w%d h%d\n", this, monitor,
+ workArea.x, workArea.y, workArea.width, workArea.height));
+ return NS_OK;
+ }
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+#endif
+
bool nsWindow::GetTopLevelWindowActiveState(nsIFrame* aFrame) {
// Used by window frame and button box rendering. We can end up in here in
// the content process when rendering one of these moz styles freely in a
diff --git a/widget/moz.build b/widget/moz.build
--- a/widget/moz.build
+++ b/widget/moz.build
@@ -210,7 +210,6 @@
'PuppetBidiKeyboard.cpp',
'PuppetWidget.cpp',
'Screen.cpp',
- 'ScreenManager.cpp',
'SharedWidgetUtils.cpp',
'TextEventDispatcher.cpp',
'VsyncDispatcher.cpp',
@@ -242,6 +241,7 @@
SOURCES += [
'nsBaseDragService.cpp',
'nsBaseWidget.cpp',
+ 'ScreenManager.cpp',
]
if CONFIG['MOZ_INSTRUMENT_EVENT_LOOP']:
diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -1713,6 +1713,15 @@
return NS_ERROR_NOT_IMPLEMENTED;
}
+ // Get rectangle of the screen where the window is placed.
+ // It's used to detect popup overflow under Wayland because
+ // Screenmanager does not work under it.
+#ifdef MOZ_WAYLAND
+ virtual nsresult GetScreenRect(LayoutDeviceIntRect* aRect) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+#endif
+
private:
class LongTapInfo {
public: