diff -up mozilla-release/widget/gtk/nsGtkUtils.h.975919-gtk3-hidpi mozilla-release/widget/gtk/nsGtkUtils.h --- mozilla-release/widget/gtk/nsGtkUtils.h.975919-gtk3-hidpi 2015-01-09 05:38:28.000000000 +0100 +++ mozilla-release/widget/gtk/nsGtkUtils.h 2015-01-19 18:43:30.101191071 +0100 @@ -9,6 +9,7 @@ #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*. diff -up mozilla-release/widget/gtk/nsLookAndFeel.cpp.975919-gtk3-hidpi mozilla-release/widget/gtk/nsLookAndFeel.cpp --- mozilla-release/widget/gtk/nsLookAndFeel.cpp.975919-gtk3-hidpi 2015-01-19 18:43:30.081191055 +0100 +++ mozilla-release/widget/gtk/nsLookAndFeel.cpp 2015-01-20 13:16:48.005399364 +0100 @@ -7,6 +7,7 @@ // for strtod() #include +#include #include "nsLookAndFeel.h" @@ -733,6 +734,17 @@ GetSystemFontInfo(GtkWidget *aWidget, 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; diff -up mozilla-release/widget/gtk/nsScreenGtk.cpp.975919-gtk3-hidpi mozilla-release/widget/gtk/nsScreenGtk.cpp --- mozilla-release/widget/gtk/nsScreenGtk.cpp.975919-gtk3-hidpi 2015-01-09 05:38:28.000000000 +0100 +++ mozilla-release/widget/gtk/nsScreenGtk.cpp 2015-01-19 18:43:30.102191072 +0100 @@ -11,10 +11,10 @@ #include #endif #include +#include static uint32_t sScreenId = 0; - nsScreenGtk :: nsScreenGtk ( ) : mScreenNum(0), mRect(0, 0, 0, 0), @@ -40,6 +40,37 @@ nsScreenGtk :: GetId(uint32_t *aId) 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; @@ -47,11 +78,11 @@ nsScreenGtk :: GetRect(int32_t *outLeft, 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; @@ -60,7 +91,7 @@ nsScreenGtk :: GetAvailRect(int32_t *out return NS_OK; -} // GetAvailRect +} // GetAvailRectDisplayPix NS_IMETHODIMP @@ -82,6 +113,23 @@ nsScreenGtk :: GetColorDepth(int32_t *aC } // 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) { diff -up mozilla-release/widget/gtk/nsScreenGtk.h.975919-gtk3-hidpi mozilla-release/widget/gtk/nsScreenGtk.h --- mozilla-release/widget/gtk/nsScreenGtk.h.975919-gtk3-hidpi 2015-01-09 05:38:28.000000000 +0100 +++ mozilla-release/widget/gtk/nsScreenGtk.h 2015-01-19 18:43:30.102191072 +0100 @@ -33,8 +33,11 @@ public: 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 diff -up mozilla-release/widget/gtk/nsWindow.cpp.975919-gtk3-hidpi mozilla-release/widget/gtk/nsWindow.cpp --- mozilla-release/widget/gtk/nsWindow.cpp.975919-gtk3-hidpi 2015-01-19 18:43:30.084191057 +0100 +++ mozilla-release/widget/gtk/nsWindow.cpp 2015-01-19 18:43:30.104191074 +0100 @@ -471,6 +471,9 @@ nsWindow::DispatchEvent(WidgetGUIEvent* 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 = @@ -729,6 +732,12 @@ nsWindow::GetDPI() return float(DisplayHeight(dpy, defaultScreen)/heightInches); } +double +nsWindow::GetDefaultScaleInternal() +{ + return GdkScaleFactor(); +} + NS_IMETHODIMP nsWindow::SetParent(nsIWidget *aNewParent) { @@ -827,8 +836,9 @@ nsWindow::ReparentNativeWidgetInternal(n } if (!mIsTopLevel) { - gdk_window_reparent(mGdkWindow, aNewParentWindow, mBounds.x, - mBounds.y); + gdk_window_reparent(mGdkWindow, aNewParentWindow, + DevicePixelsToGdkCoordRoundDown(mBounds.x), + DevicePixelsToGdkCoordRoundDown(mBounds.y)); } } @@ -863,26 +873,26 @@ 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; @@ -895,10 +905,14 @@ void nsWindow::SetSizeConstraints(const 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, @@ -1161,11 +1175,13 @@ nsWindow::Move(double aX, double aY) 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(); @@ -1432,7 +1448,7 @@ nsWindow::GetScreenBounds(nsIntRect &aRe // 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()); @@ -1602,17 +1618,12 @@ nsWindow::Invalidate(const nsIntRect &aR 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; } @@ -1750,7 +1761,7 @@ nsWindow::WidgetToScreenOffset() gdk_window_get_origin(mGdkWindow, &x, &y); } - return nsIntPoint(x, y); + return GdkPointToDevicePixels({ x, y }); } NS_IMETHODIMP @@ -2042,7 +2053,9 @@ nsWindow::OnExposeEvent(cairo_t *cr) return FALSE; } - nsIntRegion ®ion = exposeRegion.mRegion; + gint scale = GdkScaleFactor(); + nsIntRegion& region = exposeRegion.mRegion; + region.ScaleRoundOut(scale, scale); ClientLayerManager *clientLayers = (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) @@ -2382,21 +2395,24 @@ nsWindow::OnSizeAllocate(GtkAllocation * (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); } @@ -3848,14 +3864,17 @@ nsWindow::SetWindowClass(const nsAString 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); @@ -3863,12 +3882,12 @@ nsWindow::NativeResize(int32_t aWidth, i 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); } } @@ -3877,28 +3896,33 @@ nsWindow::NativeResize(int32_t aX, int32 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); } } @@ -6109,8 +6133,8 @@ nsWindow::GetThebesSurface(cairo_t *cr) #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); @@ -6135,8 +6159,17 @@ nsWindow::GetThebesSurface(cairo_t *cr) #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; @@ -6217,6 +6250,8 @@ nsWindow::BeginMoveDrag(WidgetMouseEvent } // 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); @@ -6308,6 +6343,70 @@ nsWindow::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, diff -up mozilla-release/widget/gtk/nsWindow.h.975919-gtk3-hidpi mozilla-release/widget/gtk/nsWindow.h --- mozilla-release/widget/gtk/nsWindow.h.975919-gtk3-hidpi 2015-01-09 05:38:28.000000000 +0100 +++ mozilla-release/widget/gtk/nsWindow.h 2015-01-19 18:43:30.104191074 +0100 @@ -97,6 +97,7 @@ public: 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; @@ -467,6 +468,20 @@ private: * 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 {