Bug 975919 - Add support for HiDPI on GTK 3.10+. r=karlt
authorAndrew Comminos <andrew@morlunk.com>
Tue, 20 Jan 2015 23:53:00 -0500
changeset 239127 f842d2ba5600b44d61394cd0ef376d83e8713a62
parent 239126 d6bdb55ff8bc48265188fdb6656b447877f202a6
child 239128 83b51c132519b71f935e42b5cd038792ffda12f6
push id487
push userbcampen@mozilla.com
push dateMon, 26 Jan 2015 23:32:56 +0000
reviewerskarlt
bugs975919
milestone38.0a1
Bug 975919 - Add support for HiDPI on GTK 3.10+. r=karlt
widget/gtk/nsLookAndFeel.cpp
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
--- a/widget/gtk/nsLookAndFeel.cpp
+++ b/widget/gtk/nsLookAndFeel.cpp
@@ -17,16 +17,19 @@
 #include <pango/pango-fontmap.h>
 
 #include <fontconfig/fontconfig.h>
 #include "gfxPlatformGtk.h"
 
 #include "gtkdrawing.h"
 #include "nsStyleConsts.h"
 #include "gfxFontConstants.h"
+
+#include <dlfcn.h>
+
 #include "mozilla/gfx/2D.h"
 
 using mozilla::LookAndFeel;
 
 #define GDK_COLOR_TO_NS_RGB(c) \
     ((nscolor) NS_RGB(c.red>>8, c.green>>8, c.blue>>8))
 #define GDK_RGBA_TO_NS_RGBA(c) \
     ((nscolor) NS_RGBA((int)((c).red*255), (int)((c).green*255), \
@@ -728,16 +731,26 @@ 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 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;
 
     pango_font_description_free(desc);
 }
 
 static void
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -118,16 +118,18 @@ extern "C" {
   
 #include "nsShmImage.h"
 
 #include "nsIDOMWheelEvent.h"
 
 #include "NativeKeyBindings.h"
 #include "nsWindow.h"
 
+#include <dlfcn.h>
+
 #include "mozilla/layers/APZCTreeManager.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::widget;
 using namespace mozilla::layers;
 using mozilla::gl::GLContext;
 
@@ -468,16 +470,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);
     }
 
@@ -726,16 +731,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;
     }
 
@@ -824,18 +835,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<nsWindow*>(aNewParent);
     bool parentHasMappedToplevel =
         newParent && newParent->mHasMappedToplevel;
     if (mHasMappedToplevel != parentHasMappedToplevel) {
         SetHasMappedToplevel(parentHasMappedToplevel);
@@ -860,52 +872,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 = 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,
                                       &geometry, GdkWindowHints(hints));
     }
 }
 
 NS_IMETHODIMP
@@ -1158,21 +1174,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,
@@ -1429,17 +1447,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.
@@ -1599,27 +1617,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:
@@ -1747,17 +1760,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;
 }
 
@@ -2039,17 +2052,19 @@ nsWindow::OnExposeEvent(cairo_t *cr)
 #if (MOZ_WIDGET_GTK == 2)
     if (!exposeRegion.Init(aEvent)) {
 #else
     if (!exposeRegion.Init(cr)) {
 #endif
         return FALSE;
     }
 
-    nsIntRegion &region = exposeRegion.mRegion;
+    gint scale = GdkScaleFactor();
+    nsIntRegion& region = exposeRegion.mRegion;
+    region.ScaleRoundOut(scale, scale);
 
     ClientLayerManager *clientLayers =
         (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT)
         ? static_cast<ClientLayerManager*>(GetLayerManager())
         : nullptr;
 
     if (clientLayers && mCompositorParent) {
         // We need to paint to the screen even if nothing changed, since if we
@@ -2379,31 +2394,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;
 
@@ -3856,67 +3874,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
@@ -6186,18 +6212,18 @@ nsWindow::GetThebesSurface(cairo_t *cr)
         return nullptr;
 
 #ifdef MOZ_X11
     gint width, height;
 
 #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!
     width = std::min(32767, width);
     height = std::min(32767, height);
     gfxIntSize size(width, height);
 
     GdkVisual *gdkVisual = gdk_window_get_visual(mGdkWindow);
@@ -6213,35 +6239,21 @@ nsWindow::GetThebesSurface(cairo_t *cr)
             nsShmImage::EnsureShmImage(size,
                                        visual, gdk_visual_get_depth(gdkVisual),
                                        mShmImage);
         usingShm = mThebesSurface != nullptr;
     }
     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
 
     // if the surface creation is reporting an error, then
     // we don't have a surface to give back
     if (mThebesSurface && mThebesSurface->CairoStatus() != 0) {
         mThebesSurface = nullptr;
     }
@@ -6299,16 +6311,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,
@@ -6390,16 +6404,79 @@ 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 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,
                                      uint32_t aModifierFlags)
 {
   if (!mGdkWindow) {
     return NS_OK;
   }
--- 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) MOZ_OVERRIDE;
     NS_IMETHOD         Destroy(void) MOZ_OVERRIDE;
     virtual nsIWidget *GetParent() MOZ_OVERRIDE;
     virtual float      GetDPI() MOZ_OVERRIDE;
+    virtual double     GetDefaultScaleInternal() MOZ_OVERRIDE; 
     virtual nsresult   SetParent(nsIWidget* aNewParent) MOZ_OVERRIDE;
     NS_IMETHOD         SetModal(bool aModal) MOZ_OVERRIDE;
     virtual bool       IsVisible() const MOZ_OVERRIDE;
     NS_IMETHOD         ConstrainPosition(bool aAllowSlop,
                                          int32_t *aX,
                                          int32_t *aY) MOZ_OVERRIDE;
     virtual void       SetSizeConstraints(const SizeConstraints& aConstraints) MOZ_OVERRIDE;
     NS_IMETHOD         Move(double aX,
@@ -470,16 +471,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<nsGtkIMModule> 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 {
 public:
     nsChildWindow();
     ~nsChildWindow();
 };