Bug 890156 - patch 4 - Update widget/windows code for per-monitor DPI support. r=emk

This commit is contained in:
Jonathan Kew 2015-12-04 16:59:14 +00:00
parent 9217770d7b
commit 1409a51999
7 changed files with 183 additions and 77 deletions

View File

@ -1359,13 +1359,12 @@ NS_METHOD nsBaseWidget::MoveClient(double aX, double aY)
// GetClientOffset returns device pixels; scale back to desktop pixels
// if that's what this widget uses for the Move/Resize APIs
CSSToLayoutDeviceScale scale = BoundsUseDesktopPixels()
? GetDefaultScale()
: CSSToLayoutDeviceScale(1.0);
aX -= clientOffset.x * 1.0 / scale.scale;
aY -= clientOffset.y * 1.0 / scale.scale;
return Move(aX, aY);
if (BoundsUseDesktopPixels()) {
DesktopPoint desktopOffset = clientOffset / GetDesktopToDeviceScale();
return Move(aX - desktopOffset.x, aY - desktopOffset.y);
} else {
return Move(aX - clientOffset.x, aY - clientOffset.y);
}
}
NS_METHOD nsBaseWidget::ResizeClient(double aWidth,
@ -1380,14 +1379,16 @@ NS_METHOD nsBaseWidget::ResizeClient(double aWidth,
// GetClientBounds and mBounds are device pixels; scale back to desktop pixels
// if that's what this widget uses for the Move/Resize APIs
CSSToLayoutDeviceScale scale = BoundsUseDesktopPixels()
? GetDefaultScale()
: CSSToLayoutDeviceScale(1.0);
double invScale = 1.0 / scale.scale;
aWidth = mBounds.width * invScale + (aWidth - clientBounds.width * invScale);
aHeight = mBounds.height * invScale + (aHeight - clientBounds.height * invScale);
return Resize(aWidth, aHeight, aRepaint);
if (BoundsUseDesktopPixels()) {
DesktopSize desktopDelta =
(LayoutDeviceIntSize(mBounds.width, mBounds.height) -
clientBounds.Size()) / GetDesktopToDeviceScale();
return Resize(aWidth + desktopDelta.width, aHeight + desktopDelta.height,
aRepaint);
} else {
return Resize(mBounds.width + (aWidth - clientBounds.width),
mBounds.height + (aHeight - clientBounds.height), aRepaint);
}
}
NS_METHOD nsBaseWidget::ResizeClient(double aX,
@ -1402,15 +1403,23 @@ NS_METHOD nsBaseWidget::ResizeClient(double aX,
LayoutDeviceIntRect clientBounds;
GetClientBounds(clientBounds);
double scale = BoundsUseDesktopPixels() ? 1.0 / GetDefaultScale().scale : 1.0;
aWidth = mBounds.width * scale + (aWidth - clientBounds.width * scale);
aHeight = mBounds.height * scale + (aHeight - clientBounds.height * scale);
LayoutDeviceIntPoint clientOffset(GetClientOffset());
aX -= clientOffset.x * scale;
aY -= clientOffset.y * scale;
return Resize(aX, aY, aWidth, aHeight, aRepaint);
if (BoundsUseDesktopPixels()) {
DesktopToLayoutDeviceScale scale = GetDesktopToDeviceScale();
DesktopPoint desktopOffset = clientOffset / scale;
DesktopSize desktopDelta =
(LayoutDeviceIntSize(mBounds.width, mBounds.height) -
clientBounds.Size()) / scale;
return Resize(aX - desktopOffset.x, aY - desktopOffset.y,
aWidth + desktopDelta.width, aHeight + desktopDelta.height,
aRepaint);
} else {
return Resize(aX - clientOffset.x, aY - clientOffset.y,
aWidth + mBounds.width - clientBounds.width,
aHeight + mBounds.height - clientBounds.height,
aRepaint);
}
}
//-------------------------------------------------------------------------
@ -1657,7 +1666,7 @@ void nsBaseWidget::SetSizeConstraints(const SizeConstraints& aConstraints)
// probably in the middle of a reflow.
}
const widget::SizeConstraints& nsBaseWidget::GetSizeConstraints() const
const widget::SizeConstraints nsBaseWidget::GetSizeConstraints()
{
return mSizeConstraints;
}
@ -1841,18 +1850,6 @@ nsBaseWidget::StartAsyncScrollbarDrag(const AsyncDragMetrics& aDragMetrics)
NewRunnableMethod(mAPZC.get(), &APZCTreeManager::StartScrollbarDrag, guid, aDragMetrics));
}
CSSIntRect
nsBaseWidget::GetScaledScreenBounds()
{
LayoutDeviceIntRect bounds;
GetScreenBounds(bounds);
// *Dividing* a LayoutDeviceIntRect by a CSSToLayoutDeviceScale gives a
// CSSIntRect.
CSSToLayoutDeviceScale scale = GetDefaultScale();
return RoundedToInt(bounds / scale);
}
already_AddRefed<nsIScreen>
nsBaseWidget::GetWidgetScreen()
{
@ -1862,7 +1859,8 @@ nsBaseWidget::GetWidgetScreen()
return nullptr;
}
CSSIntRect bounds = GetScaledScreenBounds();
LayoutDeviceIntRect bounds;
GetScreenBounds(bounds);
nsCOMPtr<nsIScreen> screen;
screenManager->ScreenForRect(bounds.x, bounds.y,
bounds.width, bounds.height,

View File

@ -298,10 +298,6 @@ public:
return aClientSize;
}
// return the widget's outside dimensions
// in global coordinates in display pixel.
CSSIntRect GetScaledScreenBounds();
// return the screen the widget is in.
already_AddRefed<nsIScreen> GetWidgetScreen();
@ -317,7 +313,7 @@ public:
virtual uint32_t GetGLFrameBufferFormat() override;
virtual const SizeConstraints& GetSizeConstraints() const override;
virtual const SizeConstraints GetSizeConstraints() override;
virtual void SetSizeConstraints(const SizeConstraints& aConstraints) override;
virtual bool CaptureWidgetOnScreen(RefPtr<mozilla::gfx::DrawTarget> aDT) override {
@ -471,12 +467,13 @@ protected:
* @param aWidth width to constrain
* @param aHeight height to constrain
*/
void ConstrainSize(int32_t* aWidth, int32_t* aHeight) const
void ConstrainSize(int32_t* aWidth, int32_t* aHeight)
{
*aWidth = std::max(mSizeConstraints.mMinSize.width,
std::min(mSizeConstraints.mMaxSize.width, *aWidth));
*aHeight = std::max(mSizeConstraints.mMinSize.height,
std::min(mSizeConstraints.mMaxSize.height, *aHeight));
SizeConstraints c = GetSizeConstraints();
*aWidth = std::max(c.mMinSize.width,
std::min(c.mMaxSize.width, *aWidth));
*aHeight = std::max(c.mMinSize.height,
std::min(c.mMaxSize.height, *aHeight));
}
virtual CompositorChild* GetRemoteRenderer() override;

View File

@ -2027,7 +2027,7 @@ public:
*
* @return the constraints in device pixels
*/
virtual const SizeConstraints& GetSizeConstraints() const = 0;
virtual const SizeConstraints GetSizeConstraints() = 0;
/**
* If this is owned by a TabChild, return that. Otherwise return

View File

@ -124,7 +124,6 @@
#include "nsISound.h"
#include "SystemTimeConverter.h"
#include "WinTaskbar.h"
#include "WinUtils.h"
#include "WidgetUtils.h"
#include "nsIWidgetListener.h"
#include "mozilla/dom/Touch.h"
@ -184,6 +183,10 @@
#define SM_CONVERTIBLESLATEMODE 0x2003
#endif
#if !defined(WM_DPICHANGED)
#define WM_DPICHANGED 0x02E0
#endif
#include "mozilla/layers/APZCTreeManager.h"
#include "mozilla/layers/InputAPZContext.h"
#include "InputData.h"
@ -426,6 +429,7 @@ nsWindow::nsWindow()
DWORD background = ::GetSysColor(COLOR_BTNFACE);
mBrush = ::CreateSolidBrush(NSRGB_2_COLOREF(background));
mSendingSetText = false;
mDefaultScale = -1.0; // not yet set, will be calculated on first use
mTaskbarPreview = nullptr;
@ -456,6 +460,7 @@ nsWindow::nsWindow()
mIdleService = nullptr;
::InitializeCriticalSection(&mPresentLock);
mSizeConstraintsScale = GetDefaultScale().scale;
sInstanceCount++;
}
@ -1079,6 +1084,9 @@ float nsWindow::GetDPI()
double nsWindow::GetDefaultScaleInternal()
{
if (mDefaultScale <= 0.0) {
mDefaultScale = WinUtils::LogToPhysFactor(mWnd);
}
return WinUtils::LogToPhysFactor(mWnd);
}
@ -1402,10 +1410,35 @@ nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints)
c.mMinSize.width = std::max(int32_t(::GetSystemMetrics(SM_CXMINTRACK)), c.mMinSize.width);
c.mMinSize.height = std::max(int32_t(::GetSystemMetrics(SM_CYMINTRACK)), c.mMinSize.height);
}
mSizeConstraintsScale = GetDefaultScale().scale;
nsBaseWidget::SetSizeConstraints(c);
}
const SizeConstraints
nsWindow::GetSizeConstraints()
{
double scale = GetDefaultScale().scale;
if (mSizeConstraintsScale == scale || mSizeConstraintsScale == 0.0) {
return mSizeConstraints;
}
scale /= mSizeConstraintsScale;
SizeConstraints c = mSizeConstraints;
if (c.mMinSize.width != NS_UNCONSTRAINEDSIZE) {
c.mMinSize.width = NSToIntRound(c.mMinSize.width * scale);
}
if (c.mMinSize.height != NS_UNCONSTRAINEDSIZE) {
c.mMinSize.height = NSToIntRound(c.mMinSize.height * scale);
}
if (c.mMaxSize.width != NS_UNCONSTRAINEDSIZE) {
c.mMaxSize.width = NSToIntRound(c.mMaxSize.width * scale);
}
if (c.mMaxSize.height != NS_UNCONSTRAINEDSIZE) {
c.mMaxSize.height = NSToIntRound(c.mMaxSize.height * scale);
}
return c;
}
// Move this component
NS_METHOD nsWindow::Move(double aX, double aY)
{
@ -1416,10 +1449,9 @@ NS_METHOD nsWindow::Move(double aX, double aY)
// for top-level windows only, convert coordinates from desktop pixels
// (the "parent" coordinate space) to the window's device pixel space
CSSToLayoutDeviceScale scale = BoundsUseDesktopPixels() ? GetDefaultScale()
: CSSToLayoutDeviceScale(1.0);
int32_t x = NSToIntRound(aX * scale.scale);
int32_t y = NSToIntRound(aY * scale.scale);
double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
int32_t x = NSToIntRound(aX * scale);
int32_t y = NSToIntRound(aY * scale);
// Check to see if window needs to be moved first
// to avoid a costly call to SetWindowPos. This check
@ -1471,7 +1503,13 @@ NS_METHOD nsWindow::Move(double aX, double aY)
(mClipRectCount != 1 || !mClipRects[0].IsEqualInterior(LayoutDeviceIntRect(0, 0, mBounds.width, mBounds.height)))) {
flags |= SWP_NOCOPYBITS;
}
double oldScale = mDefaultScale;
mResizeState = IN_SIZEMOVE;
VERIFY(::SetWindowPos(mWnd, nullptr, x, y, 0, 0, flags));
mResizeState = NOT_RESIZING;
if (WinUtils::LogToPhysFactor(mWnd) != oldScale) {
ChangedDPI();
}
SetThemeRegion();
}
@ -1484,10 +1522,9 @@ NS_METHOD nsWindow::Resize(double aWidth, double aHeight, bool aRepaint)
{
// for top-level windows only, convert coordinates from desktop pixels
// (the "parent" coordinate space) to the window's device pixel space
CSSToLayoutDeviceScale scale = BoundsUseDesktopPixels() ? GetDefaultScale()
: CSSToLayoutDeviceScale(1.0);
int32_t width = NSToIntRound(aWidth * scale.scale);
int32_t height = NSToIntRound(aHeight * scale.scale);
double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
int32_t width = NSToIntRound(aWidth * scale);
int32_t height = NSToIntRound(aHeight * scale);
NS_ASSERTION((width >= 0) , "Negative width passed to nsWindow::Resize");
NS_ASSERTION((height >= 0), "Negative height passed to nsWindow::Resize");
@ -1519,8 +1556,12 @@ NS_METHOD nsWindow::Resize(double aWidth, double aHeight, bool aRepaint)
}
ClearThemeRegion();
double oldScale = mDefaultScale;
VERIFY(::SetWindowPos(mWnd, nullptr, 0, 0,
width, GetHeight(height), flags));
if (WinUtils::LogToPhysFactor(mWnd) != oldScale) {
ChangedDPI();
}
SetThemeRegion();
}
@ -1536,12 +1577,11 @@ NS_METHOD nsWindow::Resize(double aX, double aY, double aWidth, double aHeight,
{
// for top-level windows only, convert coordinates from desktop pixels
// (the "parent" coordinate space) to the window's device pixel space
CSSToLayoutDeviceScale scale = BoundsUseDesktopPixels() ? GetDefaultScale()
: CSSToLayoutDeviceScale(1.0);
int32_t x = NSToIntRound(aX * scale.scale);
int32_t y = NSToIntRound(aY * scale.scale);
int32_t width = NSToIntRound(aWidth * scale.scale);
int32_t height = NSToIntRound(aHeight * scale.scale);
double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
int32_t x = NSToIntRound(aX * scale);
int32_t y = NSToIntRound(aY * scale);
int32_t width = NSToIntRound(aWidth * scale);
int32_t height = NSToIntRound(aHeight * scale);
NS_ASSERTION((width >= 0), "Negative width passed to nsWindow::Resize");
NS_ASSERTION((height >= 0), "Negative height passed to nsWindow::Resize");
@ -1575,8 +1615,12 @@ NS_METHOD nsWindow::Resize(double aX, double aY, double aWidth, double aHeight,
}
ClearThemeRegion();
double oldScale = mDefaultScale;
VERIFY(::SetWindowPos(mWnd, nullptr, x, y,
width, GetHeight(height), flags));
if (WinUtils::LogToPhysFactor(mWnd) != oldScale) {
ChangedDPI();
}
if (mTransitionWnd) {
// If we have a fullscreen transition window, we need to make
// it topmost again, otherwise the taskbar may be raised by
@ -1753,21 +1797,23 @@ nsWindow::SetSizeMode(nsSizeMode aMode) {
}
// Constrain a potential move to fit onscreen
// Position (aX, aY) is specified in Windows screen (logical) pixels
// Position (aX, aY) is specified in Windows screen (logical) pixels,
// except when using per-monitor DPI, in which case it's device pixels.
NS_METHOD nsWindow::ConstrainPosition(bool aAllowSlop,
int32_t *aX, int32_t *aY)
{
if (!mIsTopWidgetWindow) // only a problem for top-level windows
return NS_OK;
double dpiScale = GetDefaultScale().scale;
double dpiScale = GetDesktopToDeviceScale().scale;
// we need to use the window size in logical screen pixels
// We need to use the window size in the kind of pixels used for window-
// manipulation APIs.
int32_t logWidth = std::max<int32_t>(NSToIntRound(mBounds.width / dpiScale), 1);
int32_t logHeight = std::max<int32_t>(NSToIntRound(mBounds.height / dpiScale), 1);
/* get our playing field. use the current screen, or failing that
for any reason, use device caps for the default screen. */
for any reason, use device caps for the default screen. */
RECT screenRect;
nsCOMPtr<nsIScreenManager> screenmgr = do_GetService(sScreenManagerContractID);
@ -3055,11 +3101,11 @@ nsWindow::PrepareForFullscreenTransition(nsISupports** aData)
screen->GetRectDisplayPix(&x, &y, &width, &height);
MOZ_ASSERT(BoundsUseDesktopPixels(),
"Should only be called on top-level window");
CSSToLayoutDeviceScale scale = GetDefaultScale();
initData.mBounds.x = NSToIntRound(x * scale.scale);
initData.mBounds.y = NSToIntRound(y * scale.scale);
initData.mBounds.width = NSToIntRound(width * scale.scale);
initData.mBounds.height = NSToIntRound(height * scale.scale);
double scale = GetDesktopToDeviceScale().scale; // XXX or GetDefaultScale() ?
initData.mBounds.x = NSToIntRound(x * scale);
initData.mBounds.y = NSToIntRound(y * scale);
initData.mBounds.width = NSToIntRound(width * scale);
initData.mBounds.height = NSToIntRound(height * scale);
// Create a semaphore for synchronizing the window handle which will
// be created by the transition thread and used by the main thread for
@ -3801,7 +3847,7 @@ nsWindow::UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries)
RECT rect;
::GetWindowRect(mWnd, &rect);
// We want 1 pixel of border for every whole 100% of scaling
double borderSize = RoundDown(GetDefaultScale().scale);
double borderSize = std::min(1, RoundDown(GetDesktopToDeviceScale().scale));
clearRegion.Or(clearRegion, nsIntRect(0, 0, rect.right - rect.left, borderSize));
}
@ -5385,13 +5431,12 @@ nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam,
case WM_EXITSIZEMOVE:
{
if (mResizeState == RESIZING) {
mResizeState = NOT_RESIZING;
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
if (observerService) {
observerService->NotifyObservers(nullptr, "live-resize-end", nullptr);
}
}
mResizeState = NOT_RESIZING;
if (!sIsInMouseCapture) {
NotifySizeMoveDone();
@ -5597,6 +5642,14 @@ nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam,
Invalidate(true, true, true);
break;
case WM_DPICHANGED:
{
LPRECT rect = (LPRECT) lParam;
OnDPIChanged(rect->left, rect->top, rect->right - rect->left,
rect->bottom - rect->top);
break;
}
case WM_UPDATEUISTATE:
{
// If the UI state has changed, fire an event so the UI updates the
@ -6872,6 +6925,37 @@ nsWindow::OnSysColorChanged()
}
}
void
nsWindow::OnDPIChanged(int32_t x, int32_t y, int32_t width, int32_t height)
{
if (DefaultScaleOverride() > 0.0) {
return;
}
double oldScale = mDefaultScale;
mDefaultScale = -1.0; // force recomputation of scale factor
double newScale = GetDefaultScaleInternal();
if (mResizeState != NOT_RESIZING) {
// We want to try and maintain the size of the client area, rather than
// the overall size of the window including non-client area, so we prefer
// to calculate the new size instead of using Windows' suggested values.
if (oldScale > 0.0) {
double ratio = newScale / oldScale;
LayoutDeviceIntRect cr, sr;
GetClientBounds(cr);
GetScreenBounds(sr);
int32_t w = sr.width - cr.width + NSToIntRound(cr.width * ratio);
int32_t h = sr.height - cr.height + NSToIntRound(cr.height * ratio);
// Adjust x and y to preserve the center point of the suggested rect.
x -= (w - width) / 2;
y -= (h - height) / 2;
width = w;
height = h;
}
Resize(x, y, width, height, true);
}
ChangedDPI();
}
/**************************************************************
**************************************************************
**

View File

@ -31,7 +31,7 @@
#include "nsRegionFwd.h"
#include "nsWinGesture.h"
#include "WinUtils.h"
#include "WindowHook.h"
#include "TaskbarWindowPreview.h"
@ -104,12 +104,22 @@ public:
NS_IMETHOD SetParent(nsIWidget *aNewParent) override;
virtual nsIWidget* GetParent(void) override;
virtual float GetDPI() override;
virtual double GetDefaultScaleInternal() override;
virtual int32_t LogToPhys(double aValue) override final;
double GetDefaultScaleInternal() final;
int32_t LogToPhys(double aValue) final;
mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() final
{
if (mozilla::widget::WinUtils::IsPerMonitorDPIAware()) {
return mozilla::DesktopToLayoutDeviceScale(1.0);
} else {
return mozilla::DesktopToLayoutDeviceScale(GetDefaultScaleInternal());
}
}
NS_IMETHOD Show(bool bState) override;
virtual bool IsVisible() const override;
NS_IMETHOD ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY) override;
virtual void SetSizeConstraints(const SizeConstraints& aConstraints) override;
virtual const SizeConstraints GetSizeConstraints() override;
NS_IMETHOD Move(double aX, double aY) override;
NS_IMETHOD Resize(double aWidth, double aHeight, bool aRepaint) override;
NS_IMETHOD Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint) override;
@ -407,6 +417,8 @@ protected:
void OnWindowPosChanged(WINDOWPOS* wp);
void OnWindowPosChanging(LPWINDOWPOS& info);
void OnSysColorChanged();
void OnDPIChanged(int32_t x, int32_t y,
int32_t width, int32_t height);
/**
* Function that registers when the user has been active (used for detecting
@ -543,6 +555,8 @@ protected:
// Height of the caption plus border
int32_t mCaptionHeight;
double mDefaultScale;
nsCOMPtr<nsIIdleServiceInternal> mIdleService;
// Draggable titlebar region maintained by UpdateWindowDraggingRegion
@ -620,6 +634,8 @@ protected:
static void InitMouseWheelScrollData();
CRITICAL_SECTION mPresentLock;
double mSizeConstraintsScale; // scale in effect when setting constraints
};
/**

View File

@ -105,6 +105,16 @@ nsWindowBase::InjectTouchPoint(uint32_t aId, ScreenIntPoint& aPointerScreenPoint
return true;
}
void nsWindowBase::ChangedDPI()
{
if (mWidgetListener) {
nsIPresShell* presShell = mWidgetListener->GetPresShell();
if (presShell) {
presShell->BackingScaleFactorChanged();
}
}
}
nsresult
nsWindowBase::SynthesizeNativeTouchPoint(uint32_t aPointerId,
nsIWidget::TouchPointerState aPointerState,

View File

@ -97,6 +97,7 @@ public:
protected:
virtual int32_t LogToPhys(double aValue) = 0;
void ChangedDPI();
static bool InitTouchInjection();
bool InjectTouchPoint(uint32_t aId, ScreenIntPoint& aPointerScreenPoint,