diff -up firefox-35.0/mozilla-release/widget/gtk/nsLookAndFeel.cpp.975919-gtk3-hidpi firefox-35.0/mozilla-release/widget/gtk/nsLookAndFeel.cpp --- firefox-35.0/mozilla-release/widget/gtk/nsLookAndFeel.cpp.975919-gtk3-hidpi 2015-01-22 13:06:41.099114353 +0100 +++ firefox-35.0/mozilla-release/widget/gtk/nsLookAndFeel.cpp 2015-01-22 13:06:41.118114478 +0100 @@ -22,6 +22,9 @@ #include "gtkdrawing.h" #include "nsStyleConsts.h" #include "gfxFontConstants.h" + +#include + #include "mozilla/gfx/2D.h" using mozilla::LookAndFeel; @@ -733,6 +736,16 @@ 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 auto sGdkScreenGetMonitorScaleFactorPtr = (gint (*)(GdkScreen*,gint)) + dlsym(RTLD_DEFAULT, "gdk_screen_get_monitor_scale_factor"); + if (sGdkScreenGetMonitorScaleFactorPtr) { + GdkScreen *screen = gdk_screen_get_default(); + size *= (*sGdkScreenGetMonitorScaleFactorPtr)(screen, 0); + } + // |size| is now pixels aFontStyle->size = size; diff -up firefox-35.0/mozilla-release/widget/gtk/nsWindow.cpp.975919-gtk3-hidpi firefox-35.0/mozilla-release/widget/gtk/nsWindow.cpp --- firefox-35.0/mozilla-release/widget/gtk/nsWindow.cpp.975919-gtk3-hidpi 2015-01-22 13:06:41.101114366 +0100 +++ firefox-35.0/mozilla-release/widget/gtk/nsWindow.cpp 2015-01-22 13:08:08.152683204 +0100 @@ -10,6 +10,7 @@ #include "mozilla/MouseEvents.h" #include "mozilla/TextEvents.h" #include +#include #include "prlink.h" #include "nsGTKToolkit.h" @@ -471,6 +472,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 +733,12 @@ nsWindow::GetDPI() return float(DisplayHeight(dpy, defaultScreen)/heightInches); } +double +nsWindow::GetDefaultScaleInternal() +{ + return GdkScaleFactor(); +} + NS_IMETHODIMP nsWindow::SetParent(nsIWidget *aNewParent) { @@ -827,8 +837,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 +874,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 +906,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 = DevicePixelsToGdkCoordRoundDown( + mSizeConstraints.mMaxSize.width); + geometry.max_height = DevicePixelsToGdkCoordRoundDown( + 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 +1176,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 +1449,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 +1619,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 +1762,7 @@ nsWindow::WidgetToScreenOffset() gdk_window_get_origin(mGdkWindow, &x, &y); } - return nsIntPoint(x, y); + return GdkPointToDevicePixels({ x, y }); } NS_IMETHODIMP @@ -2042,7 +2054,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 +2396,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 +3865,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 +3883,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 +3897,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); } } @@ -6104,8 +6129,8 @@ 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); + width = GdkCoordToDevicePixels(gdk_window_get_width(mGdkWindow)); + height = GdkCoordToDevicePixels(gdk_window_get_height(mGdkWindow)); #endif // Owen Taylor says this is the right thing to do! @@ -6131,25 +6156,11 @@ 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 - if (cr) { - cairo_surface_t *surf = cairo_get_target(cr); - 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) - mThebesSurface = new gfxXlibSurface - (GDK_WINDOW_XDISPLAY(mGdkWindow), - gdk_x11_window_get_xid(mGdkWindow), - visual, - size); + mThebesSurface = new gfxXlibSurface + (GDK_WINDOW_XDISPLAY(mGdkWindow), + gdk_x11_window_get_xid(mGdkWindow), + visual, + size); } #endif // MOZ_X11 @@ -6217,6 +6228,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 +6321,69 @@ nsWindow::ClearCachedResources() } } +gint +nsWindow::GdkScaleFactor() +{ +#if (MOZ_WIDGET_GTK >= 3) + // Available as of GTK 3.10+ + static auto sGdkWindowGetScaleFactorPtr = (gint (*)(GdkWindow*)) + dlsym(RTLD_DEFAULT, "gdk_window_get_scale_factor"); + if (sGdkWindowGetScaleFactorPtr) + return (*sGdkWindowGetScaleFactorPtr)(mGdkWindow); +#endif + return 1; +} + + +gint +nsWindow::DevicePixelsToGdkCoordRoundUp(int pixels) { + gint scale = GdkScaleFactor(); + return (pixels + scale - 1) / scale; +} + +gint +nsWindow::DevicePixelsToGdkCoordRoundDown(int pixels) { + gint scale = GdkScaleFactor(); + return pixels / scale; +} + +GdkPoint +nsWindow::DevicePixelsToGdkPointRoundDown(nsIntPoint point) { + gint scale = GdkScaleFactor(); + return { point.x / scale, point.y / scale }; +} + +GdkRectangle +nsWindow::DevicePixelsToGdkRectRoundOut(nsIntRect rect) { + gint scale = GdkScaleFactor(); + int x = rect.x / scale; + int y = rect.y / scale; + int right = (rect.x + rect.width + scale - 1) / scale; + int bottom = (rect.y + rect.height + scale - 1) / scale; + return { x, y, right - x, bottom - y }; +} + +int +nsWindow::GdkCoordToDevicePixels(gint coord) { + return coord * 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 firefox-35.0/mozilla-release/widget/gtk/nsWindow.h.975919-gtk3-hidpi firefox-35.0/mozilla-release/widget/gtk/nsWindow.h --- firefox-35.0/mozilla-release/widget/gtk/nsWindow.h.975919-gtk3-hidpi 2015-01-09 05:38:28.000000000 +0100 +++ firefox-35.0/mozilla-release/widget/gtk/nsWindow.h 2015-01-22 13:07:51.064571540 +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 coord); + nsIntPoint GdkPointToDevicePixels(GdkPoint point); + nsIntRect GdkRectToDevicePixels(GdkRectangle rect); }; class nsChildWindow : public nsWindow {