mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 12:51:06 +00:00
Bug 1565401 - return valid screen size on multimonitor setup under Wayland; r=stransky,NeilDeakin
By getting the right screen size we can shrink the popup menus which overflows the screen size under Wayland. The ScreenManager does not help us, because we can't get absolute window position, but we can use gdk_display_get_monitor_at_window and gdk_monitor_get_workarea to get the correct screen rectangle. Differential Revision: https://phabricator.services.mozilla.com/D49289 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
9d78fc40f2
commit
4d0cec02a5
@ -1502,7 +1502,7 @@ nsresult nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame,
|
||||
|
||||
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 @@ nsresult nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame,
|
||||
// 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 @@ nsresult nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame,
|
||||
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;
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
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 @@ LayoutDeviceIntRect nsMenuPopupFrame::GetConstraintRect(
|
||||
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) {
|
||||
|
@ -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 @@ void ScreenManager::CopyScreensToAllRemotesIfIsParent() {
|
||||
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."));
|
||||
|
@ -1294,10 +1294,14 @@ static void NativeMoveResizeWaylandPopupCallback(
|
||||
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 @@ void nsWindow::NativeMoveResizeWaylandPopup(GdkPoint* aPosition,
|
||||
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 @@ void nsWindow::NativeMove() {
|
||||
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 @@ void nsWindow::SetDrawsInTitlebar(bool aState) {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ gint nsWindow::GdkScaleFactor() {
|
||||
// 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 @@ already_AddRefed<nsIWidget> nsIWidget::CreateChildWindow() {
|
||||
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
|
||||
|
@ -398,6 +398,9 @@ class nsWindow final : public nsBaseWidget {
|
||||
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 @@ class nsWindow final : public nsBaseWidget {
|
||||
void HideWaylandTooltips();
|
||||
void HideWaylandPopupAndAllChildren();
|
||||
void CleanupWaylandPopups();
|
||||
GtkWindow* GetCurrentTopmostWindow();
|
||||
|
||||
/**
|
||||
* |mIMContext| takes all IME related stuff.
|
||||
|
@ -210,7 +210,6 @@ UNIFIED_SOURCES += [
|
||||
'PuppetBidiKeyboard.cpp',
|
||||
'PuppetWidget.cpp',
|
||||
'Screen.cpp',
|
||||
'ScreenManager.cpp',
|
||||
'SharedWidgetUtils.cpp',
|
||||
'TextEventDispatcher.cpp',
|
||||
'VsyncDispatcher.cpp',
|
||||
@ -242,6 +241,7 @@ if CONFIG['MOZ_XUL'] and CONFIG['NS_PRINTING']:
|
||||
SOURCES += [
|
||||
'nsBaseDragService.cpp',
|
||||
'nsBaseWidget.cpp',
|
||||
'ScreenManager.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_INSTRUMENT_EVENT_LOOP']:
|
||||
|
@ -1713,6 +1713,15 @@ class nsIWidget : public nsISupports {
|
||||
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:
|
||||
|
Loading…
Reference in New Issue
Block a user