371 lines
15 KiB
Diff
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:
|
|
|