# HG changeset patch # Parent 7b33ee7fd162d784f382250d3fa811e86a1b7348 # User Andrew Comminos Bug 975919 - Added support for HiDPI on GTK 3.10+ diff --git a/widget/gtk/nsGtkUtils.h b/widget/gtk/nsGtkUtils.h --- a/widget/gtk/nsGtkUtils.h +++ b/widget/gtk/nsGtkUtils.h @@ -4,16 +4,17 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef nsGtkUtils_h__ #define nsGtkUtils_h__ #include +#include // Some gobject functions expect functions for gpointer arguments. // gpointer is void* but C++ doesn't like casting functions to void*. template static inline gpointer FuncToGpointer(T aFunction) { return reinterpret_cast (reinterpret_cast diff --git a/widget/gtk/nsLookAndFeel.cpp b/widget/gtk/nsLookAndFeel.cpp --- a/widget/gtk/nsLookAndFeel.cpp +++ b/widget/gtk/nsLookAndFeel.cpp @@ -728,16 +728,27 @@ GetSystemFontInfo(GtkWidget *aWidget, // |size| is now either pixels or pango-points (not Mozilla-points!) if (!pango_font_description_get_size_is_absolute(desc)) { // |size| is in pango-points, so convert to pixels. size *= float(gfxPlatformGtk::GetDPI()) / POINTS_PER_INCH_FLOAT; } + // Scale fonts up on HiDPI displays. + // This would be done automatically with cairo, but we manually manage + // the display scale for platform consistency. + static gint (*GdkScreenGetMonitorScaleFactorPtr)(GdkScreen*,gint) = + (gint (*)(GdkScreen*,gint)) dlsym(RTLD_DEFAULT, + "gdk_screen_get_monitor_scale_factor"); + if (GdkScreenGetMonitorScaleFactorPtr) { + GdkScreen *screen = gdk_screen_get_default(); + size *= (*GdkScreenGetMonitorScaleFactorPtr)(screen, 0); + } + // |size| is now pixels aFontStyle->size = size; pango_font_description_free(desc); } static void diff --git a/widget/gtk/nsScreenGtk.cpp b/widget/gtk/nsScreenGtk.cpp --- a/widget/gtk/nsScreenGtk.cpp +++ b/widget/gtk/nsScreenGtk.cpp @@ -6,20 +6,20 @@ #include "nsScreenGtk.h" #include #ifdef MOZ_X11 #include #include #endif #include +#include static uint32_t sScreenId = 0; - nsScreenGtk :: nsScreenGtk ( ) : mScreenNum(0), mRect(0, 0, 0, 0), mAvailRect(0, 0, 0, 0), mId(++sScreenId) { } @@ -35,37 +35,68 @@ nsScreenGtk :: GetId(uint32_t *aId) *aId = mId; return NS_OK; } // GetId NS_IMETHODIMP nsScreenGtk :: GetRect(int32_t *outLeft, int32_t *outTop, int32_t *outWidth, int32_t *outHeight) { + double scale; + GetContentsScaleFactor(&scale); + + *outLeft = NSToIntRound(mRect.x * scale); + *outTop = NSToIntRound(mRect.y * scale); + *outWidth = NSToIntRound(mRect.width * scale); + *outHeight = NSToIntRound(mRect.height * scale); + + return NS_OK; + +} // GetRect + + +NS_IMETHODIMP +nsScreenGtk :: GetAvailRect(int32_t *outLeft, int32_t *outTop, int32_t *outWidth, int32_t *outHeight) +{ + double scale; + GetContentsScaleFactor(&scale); + + *outLeft = NSToIntRound(mAvailRect.x * scale); + *outTop = NSToIntRound(mAvailRect.y * scale); + *outWidth = NSToIntRound(mAvailRect.width * scale); + *outHeight = NSToIntRound(mAvailRect.height * scale); + + return NS_OK; + +} // GetAvailRect + +NS_IMETHODIMP +nsScreenGtk :: GetRectDisplayPix(int32_t *outLeft, int32_t *outTop, int32_t *outWidth, int32_t *outHeight) +{ *outLeft = mRect.x; *outTop = mRect.y; *outWidth = mRect.width; *outHeight = mRect.height; return NS_OK; -} // GetRect +} // GetRectDisplayPix NS_IMETHODIMP -nsScreenGtk :: GetAvailRect(int32_t *outLeft, int32_t *outTop, int32_t *outWidth, int32_t *outHeight) +nsScreenGtk :: GetAvailRectDisplayPix(int32_t *outLeft, int32_t *outTop, int32_t *outWidth, int32_t *outHeight) { *outLeft = mAvailRect.x; *outTop = mAvailRect.y; *outWidth = mAvailRect.width; *outHeight = mAvailRect.height; return NS_OK; -} // GetAvailRect +} // GetAvailRectDisplayPix NS_IMETHODIMP nsScreenGtk :: GetPixelDepth(int32_t *aPixelDepth) { GdkVisual * visual = gdk_screen_get_system_visual(gdk_screen_get_default()); *aPixelDepth = gdk_visual_get_depth(visual); @@ -77,16 +108,33 @@ nsScreenGtk :: GetPixelDepth(int32_t *aP NS_IMETHODIMP nsScreenGtk :: GetColorDepth(int32_t *aColorDepth) { return GetPixelDepth ( aColorDepth ); } // GetColorDepth +NS_IMETHODIMP +nsScreenGtk :: GetContentsScaleFactor(double* aContentsScaleFactor) +{ + static gint (*GdkScreenGetMonitorScaleFactorPtr)(GdkScreen*,gint) = + (gint (*)(GdkScreen*,gint)) dlsym(RTLD_DEFAULT, + "gdk_screen_get_monitor_scale_factor"); + if (GdkScreenGetMonitorScaleFactorPtr) { + GdkScreen *screen = gdk_screen_get_default(); + *aContentsScaleFactor = (*GdkScreenGetMonitorScaleFactorPtr) + (screen, mScreenNum); + } else { + *aContentsScaleFactor = 1; + } + return NS_OK; +} + + void nsScreenGtk :: Init (GdkWindow *aRootWindow) { // We listen for configure events on the root window to pick up // changes to this rect. We could listen for "size_changed" signals // on the default screen to do this, except that doesn't work with // versions of GDK predating the GdkScreen object. See bug 256646. mAvailRect = mRect = nsIntRect(0, 0, gdk_screen_width(), gdk_screen_height()); diff --git a/widget/gtk/nsScreenGtk.h b/widget/gtk/nsScreenGtk.h --- a/widget/gtk/nsScreenGtk.h +++ b/widget/gtk/nsScreenGtk.h @@ -28,18 +28,21 @@ class nsScreenGtk : public nsBaseScreen { public: nsScreenGtk(); ~nsScreenGtk(); NS_IMETHOD GetId(uint32_t* aId); NS_IMETHOD GetRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); NS_IMETHOD GetAvailRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); + NS_IMETHOD GetRectDisplayPix(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); + NS_IMETHOD GetAvailRectDisplayPix(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); NS_IMETHOD GetPixelDepth(int32_t* aPixelDepth); NS_IMETHOD GetColorDepth(int32_t* aColorDepth); + NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor); void Init(GdkWindow *aRootWindow); #ifdef MOZ_X11 void Init(XineramaScreenInfo *aScreenInfo); #endif /* MOZ_X11 */ private: uint32_t mScreenNum; diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -466,16 +466,19 @@ nsWindow::DispatchResized(int32_t aWidth nsresult nsWindow::DispatchEvent(WidgetGUIEvent* aEvent, nsEventStatus& aStatus) { #ifdef DEBUG debug_DumpEvent(stdout, aEvent->widget, aEvent, nsAutoCString("something"), 0); #endif + // Translate the mouse event into device pixels. + aEvent->refPoint.x = GdkCoordToDevicePixels(aEvent->refPoint.x); + aEvent->refPoint.y = GdkCoordToDevicePixels(aEvent->refPoint.y); aStatus = nsEventStatus_eIgnore; nsIWidgetListener* listener = mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener; if (listener) { aStatus = listener->HandleEvent(aEvent, mUseAttachedEvents); } @@ -724,16 +727,22 @@ nsWindow::GetDPI() double heightInches = DisplayHeightMM(dpy, defaultScreen)/MM_PER_INCH_FLOAT; if (heightInches < 0.25) { // Something's broken, but we'd better not crash. return 96.0f; } return float(DisplayHeight(dpy, defaultScreen)/heightInches); } +double +nsWindow::GetDefaultScaleInternal() +{ + return GdkScaleFactor(); +} + NS_IMETHODIMP nsWindow::SetParent(nsIWidget *aNewParent) { if (mContainer || !mGdkWindow) { NS_NOTREACHED("nsWindow::SetParent called illegally"); return NS_ERROR_NOT_IMPLEMENTED; } @@ -822,18 +831,19 @@ nsWindow::ReparentNativeWidgetInternal(n SetWidgetForHierarchy(mGdkWindow, aOldContainer, aNewContainer); if (aOldContainer == gInvisibleContainer) { CheckDestroyInvisibleContainer(); } } if (!mIsTopLevel) { - gdk_window_reparent(mGdkWindow, aNewParentWindow, mBounds.x, - mBounds.y); + gdk_window_reparent(mGdkWindow, aNewParentWindow, + DevicePixelsToGdkCoordRoundDown(mBounds.x), + DevicePixelsToGdkCoordRoundDown(mBounds.y)); } } nsWindow* newParent = static_cast(aNewParent); bool parentHasMappedToplevel = newParent && newParent->mHasMappedToplevel; if (mHasMappedToplevel != parentHasMappedToplevel) { SetHasMappedToplevel(parentHasMappedToplevel); @@ -858,52 +868,56 @@ nsWindow::IsVisible() const { return mIsShown; } NS_IMETHODIMP nsWindow::ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY) { if (mIsTopLevel && mShell) { - int32_t screenWidth = gdk_screen_width(); - int32_t screenHeight = gdk_screen_height(); + int width = GdkCoordToDevicePixels(gdk_screen_width()); + int height = GdkCoordToDevicePixels(gdk_screen_height()); if (aAllowSlop) { if (*aX < (kWindowPositionSlop - mBounds.width)) *aX = kWindowPositionSlop - mBounds.width; - if (*aX > (screenWidth - kWindowPositionSlop)) - *aX = screenWidth - kWindowPositionSlop; + if (*aX > (width - kWindowPositionSlop)) + *aX = width - kWindowPositionSlop; if (*aY < (kWindowPositionSlop - mBounds.height)) *aY = kWindowPositionSlop - mBounds.height; - if (*aY > (screenHeight - kWindowPositionSlop)) - *aY = screenHeight - kWindowPositionSlop; + if (*aY > (height - kWindowPositionSlop)) + *aY = height - kWindowPositionSlop; } else { if (*aX < 0) *aX = 0; - if (*aX > (screenWidth - mBounds.width)) - *aX = screenWidth - mBounds.width; + if (*aX > (width - mBounds.width)) + *aX = width - mBounds.width; if (*aY < 0) *aY = 0; - if (*aY > (screenHeight - mBounds.height)) - *aY = screenHeight - mBounds.height; + if (*aY > (height - mBounds.height)) + *aY = height - mBounds.height; } } return NS_OK; } void nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints) { mSizeConstraints.mMinSize = GetSafeWindowSize(aConstraints.mMinSize); mSizeConstraints.mMaxSize = GetSafeWindowSize(aConstraints.mMaxSize); if (mShell) { GdkGeometry geometry; - geometry.min_width = mSizeConstraints.mMinSize.width; - geometry.min_height = mSizeConstraints.mMinSize.height; - geometry.max_width = mSizeConstraints.mMaxSize.width; - geometry.max_height = mSizeConstraints.mMaxSize.height; + geometry.min_width = DevicePixelsToGdkCoordRoundUp( + mSizeConstraints.mMinSize.width); + geometry.min_height = DevicePixelsToGdkCoordRoundUp( + mSizeConstraints.mMinSize.height); + geometry.max_width = DevicePixelsToGdkCoordRoundUp( + mSizeConstraints.mMaxSize.width); + geometry.max_height = DevicePixelsToGdkCoordRoundUp( + mSizeConstraints.mMaxSize.height); uint32_t hints = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE; gtk_window_set_geometry_hints(GTK_WINDOW(mShell), nullptr, &geometry, GdkWindowHints(hints)); } } NS_IMETHODIMP @@ -1156,21 +1170,23 @@ nsWindow::Move(double aX, double aY) mBounds.x = x; mBounds.y = y; if (!mCreated) return NS_OK; mNeedsMove = false; + GdkPoint point = DevicePixelsToGdkPointRoundDown(nsIntPoint(x, y)); + if (mIsTopLevel) { - gtk_window_move(GTK_WINDOW(mShell), x, y); + gtk_window_move(GTK_WINDOW(mShell), point.x, point.y); } else if (mGdkWindow) { - gdk_window_move(mGdkWindow, x, y); + gdk_window_move(mGdkWindow, point.x, point.y); } NotifyRollupGeometryChange(); return NS_OK; } NS_IMETHODIMP nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement, @@ -1427,17 +1443,17 @@ nsWindow::SetFocus(bool aRaise) NS_IMETHODIMP nsWindow::GetScreenBounds(nsIntRect &aRect) { if (mIsTopLevel && mContainer) { // use the point including window decorations gint x, y; gdk_window_get_root_origin(gtk_widget_get_window(GTK_WIDGET(mContainer)), &x, &y); - aRect.MoveTo(x, y); + aRect.MoveTo(GdkPointToDevicePixels({ x, y })); } else { aRect.MoveTo(WidgetToScreenOffset()); } // mBounds.Size() is the window bounds, not the window-manager frame // bounds (bug 581863). gdk_window_get_frame_extents would give the // frame bounds, but mBounds.Size() is returned here for consistency // with Resize. @@ -1597,27 +1613,22 @@ nsWindow::SetCursor(imgIContainer* aCurs } NS_IMETHODIMP nsWindow::Invalidate(const nsIntRect &aRect) { if (!mGdkWindow) return NS_OK; - GdkRectangle rect; - rect.x = aRect.x; - rect.y = aRect.y; - rect.width = aRect.width; - rect.height = aRect.height; + GdkRectangle rect = DevicePixelsToGdkRectRoundOut(aRect); + gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE); LOGDRAW(("Invalidate (rect) [%p]: %d %d %d %d\n", (void *)this, rect.x, rect.y, rect.width, rect.height)); - gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE); - return NS_OK; } void* nsWindow::GetNativeData(uint32_t aDataType) { switch (aDataType) { case NS_NATIVE_WINDOW: @@ -1745,17 +1756,17 @@ nsIntPoint nsWindow::WidgetToScreenOffset() { gint x = 0, y = 0; if (mGdkWindow) { gdk_window_get_origin(mGdkWindow, &x, &y); } - return nsIntPoint(x, y); + return GdkPointToDevicePixels({ x, y }); } NS_IMETHODIMP nsWindow::EnableDragDrop(bool aEnable) { return NS_OK; } @@ -2037,17 +2048,19 @@ nsWindow::OnExposeEvent(cairo_t *cr) #if (MOZ_WIDGET_GTK == 2) if (!exposeRegion.Init(aEvent)) { #else if (!exposeRegion.Init(cr)) { #endif return FALSE; } - nsIntRegion ®ion = exposeRegion.mRegion; + gint scale = GdkScaleFactor(); + nsIntRegion& region = exposeRegion.mRegion; + region.ScaleRoundOut(scale, scale); ClientLayerManager *clientLayers = (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) ? static_cast(GetLayerManager()) : nullptr; if (clientLayers && mCompositorParent) { // We need to paint to the screen even if nothing changed, since if we @@ -2377,31 +2390,34 @@ nsWindow::OnContainerUnrealize() void nsWindow::OnSizeAllocate(GtkAllocation *aAllocation) { LOG(("size_allocate [%p] %d %d %d %d\n", (void *)this, aAllocation->x, aAllocation->y, aAllocation->width, aAllocation->height)); - nsIntSize size(aAllocation->width, aAllocation->height); + nsIntSize size = GdkRectToDevicePixels(*aAllocation).Size(); + if (mBounds.Size() == size) return; + nsIntRect rect; + // Invalidate the new part of the window now for the pending paint to // minimize background flashes (GDK does not do this for external resizes // of toplevels.) if (mBounds.width < size.width) { - GdkRectangle rect = - { mBounds.width, 0, size.width - mBounds.width, size.height }; + GdkRectangle rect = DevicePixelsToGdkRectRoundOut( + { mBounds.width, 0, size.width - mBounds.width, size.height }); gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE); } if (mBounds.height < size.height) { - GdkRectangle rect = - { 0, mBounds.height, size.width, size.height - mBounds.height }; + GdkRectangle rect = DevicePixelsToGdkRectRoundOut( + { 0, mBounds.height, size.width, size.height - mBounds.height }); gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE); } mBounds.SizeTo(size); if (!mGdkWindow) return; @@ -3843,67 +3859,75 @@ nsWindow::SetWindowClass(const nsAString nsMemory::Free(res_name); return NS_OK; } void nsWindow::NativeResize(int32_t aWidth, int32_t aHeight, bool aRepaint) { + gint width = DevicePixelsToGdkCoordRoundUp(aWidth); + gint height = DevicePixelsToGdkCoordRoundUp(aHeight); + LOG(("nsWindow::NativeResize [%p] %d %d\n", (void *)this, - aWidth, aHeight)); + width, height)); // clear our resize flag mNeedsResize = false; if (mIsTopLevel) { - gtk_window_resize(GTK_WINDOW(mShell), aWidth, aHeight); + gtk_window_resize(GTK_WINDOW(mShell), width, height); } else if (mContainer) { GtkWidget *widget = GTK_WIDGET(mContainer); GtkAllocation allocation, prev_allocation; gtk_widget_get_allocation(widget, &prev_allocation); allocation.x = prev_allocation.x; allocation.y = prev_allocation.y; - allocation.width = aWidth; - allocation.height = aHeight; + allocation.width = width; + allocation.height = height; gtk_widget_size_allocate(widget, &allocation); } else if (mGdkWindow) { - gdk_window_resize(mGdkWindow, aWidth, aHeight); + gdk_window_resize(mGdkWindow, width, height); } } void nsWindow::NativeResize(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, bool aRepaint) { + gint width = DevicePixelsToGdkCoordRoundUp(aWidth); + gint height = DevicePixelsToGdkCoordRoundUp(aHeight); + gint x = DevicePixelsToGdkCoordRoundDown(aX); + gint y = DevicePixelsToGdkCoordRoundDown(aY); + mNeedsResize = false; mNeedsMove = false; LOG(("nsWindow::NativeResize [%p] %d %d %d %d\n", (void *)this, - aX, aY, aWidth, aHeight)); + x, y, width, height)); if (mIsTopLevel) { - // aX and aY give the position of the window manager frame top-left. - gtk_window_move(GTK_WINDOW(mShell), aX, aY); + // x and y give the position of the window manager frame top-left. + gtk_window_move(GTK_WINDOW(mShell), x, y); // This sets the client window size. - gtk_window_resize(GTK_WINDOW(mShell), aWidth, aHeight); + gtk_window_resize(GTK_WINDOW(mShell), width, height); } else if (mContainer) { GtkAllocation allocation; - allocation.x = aX; - allocation.y = aY; - allocation.width = aWidth; - allocation.height = aHeight; + allocation.x = x; + allocation.y = y; + allocation.width = width; + allocation.height = height; gtk_widget_size_allocate(GTK_WIDGET(mContainer), &allocation); } else if (mGdkWindow) { - gdk_window_move_resize(mGdkWindow, aX, aY, aWidth, aHeight); + gdk_window_move_resize(mGdkWindow, x, y, width, height); } } void nsWindow::NativeShow(bool aAction) { if (aAction) { // unset our flag now that our window has been shown @@ -6178,18 +6202,18 @@ nsWindow::GetThebesSurface(cairo_t *cr) #if (MOZ_WIDGET_GTK == 2) gdk_drawable_get_size(GDK_DRAWABLE(mGdkWindow), &width, &height); #else width = gdk_window_get_width(mGdkWindow); height = gdk_window_get_height(mGdkWindow); #endif // Owen Taylor says this is the right thing to do! - width = std::min(32767, width); - height = std::min(32767, height); + width = std::min(32767, (int)std::ceil(GdkCoordToDevicePixels(width))); + height = std::min(32767, (int)std::ceil(GdkCoordToDevicePixels(height))); gfxIntSize size(width, height); GdkVisual *gdkVisual = gdk_window_get_visual(mGdkWindow); Visual* visual = gdk_x11_visual_get_xvisual(gdkVisual); # ifdef MOZ_HAVE_SHMIMAGE bool usingShm = false; if (nsShmImage::UseShm()) { @@ -6204,18 +6228,27 @@ nsWindow::GetThebesSurface(cairo_t *cr) } if (!usingShm) # endif // MOZ_HAVE_SHMIMAGE { #if (MOZ_WIDGET_GTK == 3) #if MOZ_TREE_CAIRO #error "cairo-gtk3 target must be built with --enable-system-cairo" #else + // Available as of Cairo 1.14 + static void (*CairoSurfaceSetDeviceScalePtr) (cairo_surface_t*,double,double) = + (void (*)(cairo_surface_t*,double,double)) dlsym(RTLD_DEFAULT, + "cairo_surface_set_device_scale"); + if (cr) { cairo_surface_t *surf = cairo_get_target(cr); + if (GdkScaleFactor() > 1) { + // Disable auto-scaling on HiDPI devices, let mozilla manage it. + (*CairoSurfaceSetDeviceScalePtr)(surf, 1, 1); + } if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) { NS_NOTREACHED("Missing cairo target?"); return nullptr; } mThebesSurface = gfxASurface::Wrap(surf); } else #endif #endif // (MOZ_WIDGET_GTK == 3) @@ -6286,16 +6319,18 @@ nsWindow::BeginMoveDrag(WidgetMouseEvent GdkWindow *gdk_window; gint button, screenX, screenY; if (!GetDragInfo(aEvent, &gdk_window, &button, &screenX, &screenY)) { return NS_ERROR_FAILURE; } // tell the window manager to start the move + screenX = DevicePixelsToGdkCoordRoundDown(screenX); + screenY = DevicePixelsToGdkCoordRoundDown(screenY); gdk_window_begin_move_drag(gdk_window, button, screenX, screenY, aEvent->time); return NS_OK; } NS_IMETHODIMP nsWindow::BeginResizeDrag(WidgetGUIEvent* aEvent, @@ -6377,16 +6412,80 @@ nsWindow::ClearCachedResources() for (GList* list = children; list; list = list->next) { nsWindow* window = get_window_for_gdk_window(GDK_WINDOW(list->data)); if (window) { window->ClearCachedResources(); } } } +gint +nsWindow::GdkScaleFactor() +{ +#if (MOZ_WIDGET_GTK >= 3) + // Available as of GTK 3.10+ + static gint (*GdkWindowGetScaleFactorPtr) (GdkWindow*) = + (gint (*)(GdkWindow*)) dlsym(RTLD_DEFAULT, + "gdk_window_get_scale_factor"); + if (GdkWindowGetScaleFactorPtr) + return (*GdkWindowGetScaleFactorPtr)(mGdkWindow); +#endif + return 1; +} + + +gint +nsWindow::DevicePixelsToGdkCoordRoundUp(int pixels) { + return NSToIntCeil(float(pixels)/float(GdkScaleFactor())); +} + +gint +nsWindow::DevicePixelsToGdkCoordRoundDown(int pixels) { + return NSToIntFloor(float(pixels)/float(GdkScaleFactor())); +} + +GdkPoint +nsWindow::DevicePixelsToGdkPointRoundDown(nsIntPoint point) { + float scale = GdkScaleFactor(); + return { NSToIntFloor(float(point.x)/scale), + NSToIntFloor(float(point.y)/scale) }; +} + +GdkRectangle +nsWindow::DevicePixelsToGdkRectRoundOut(nsIntRect rect) { + gint scale = GdkScaleFactor(); + nsIntRect scaledRect = rect; + scaledRect.ScaleInverseRoundOut(scale); + return { scaledRect.x, + scaledRect.y, + scaledRect.width, + scaledRect.height }; +} + +int +nsWindow::GdkCoordToDevicePixels(gint coords) { + return coords * GdkScaleFactor(); +} + +nsIntPoint +nsWindow::GdkPointToDevicePixels(GdkPoint point) { + gint scale = GdkScaleFactor(); + return nsIntPoint(point.x * scale, + point.y * scale); +} + +nsIntRect +nsWindow::GdkRectToDevicePixels(GdkRectangle rect) { + gint scale = GdkScaleFactor(); + return nsIntRect(rect.x * scale, + rect.y * scale, + rect.width * scale, + rect.height * scale); +} + nsresult nsWindow::SynthesizeNativeMouseEvent(nsIntPoint aPoint, uint32_t aNativeMessage, uint32_t aModifierFlags) { if (!mGdkWindow) { return NS_OK; } diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h --- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -92,16 +92,17 @@ public: NS_IMETHOD Create(nsIWidget *aParent, nsNativeWidget aNativeParent, const nsIntRect &aRect, nsDeviceContext *aContext, nsWidgetInitData *aInitData); NS_IMETHOD Destroy(void); virtual nsIWidget *GetParent(); virtual float GetDPI(); + virtual double GetDefaultScaleInternal(); virtual nsresult SetParent(nsIWidget* aNewParent); NS_IMETHOD SetModal(bool aModal); virtual bool IsVisible() const; NS_IMETHOD ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY); virtual void SetSizeConstraints(const SizeConstraints& aConstraints); NS_IMETHOD Move(double aX, @@ -468,16 +469,30 @@ private: * The instance is created when the top level widget is created. And when * the widget is destroyed, it's released. All child windows refer its * ancestor widget's instance. So, one set of IM contexts is created for * all windows in a hierarchy. If the children are released after the top * level window is released, the children still have a valid pointer, * however, IME doesn't work at that time. */ nsRefPtr mIMModule; + + // HiDPI scale conversion + gint GdkScaleFactor(); + + // To GDK + gint DevicePixelsToGdkCoordRoundUp(int pixels); + gint DevicePixelsToGdkCoordRoundDown(int pixels); + GdkPoint DevicePixelsToGdkPointRoundDown(nsIntPoint point); + GdkRectangle DevicePixelsToGdkRectRoundOut(nsIntRect rect); + + // From GDK + int GdkCoordToDevicePixels(gint coords); + nsIntPoint GdkPointToDevicePixels(GdkPoint point); + nsIntRect GdkRectToDevicePixels(GdkRectangle rect); }; class nsChildWindow : public nsWindow { public: nsChildWindow(); ~nsChildWindow(); };