Bug 1431337 - Scale content for the actual monitor, not the first one; r?stransky draft
authorJan Horak <jhorak@redhat.com>
Thu, 18 Jan 2018 11:52:59 +0100
changeset 757269 3b70fc9f3a811e231573ebd64117af56ab20ac96
parent 754399 38b3c1d03a594664c6b32c35533734283c258f43
child 757270 12282fc61f3e7a08c20157bbff91a0dca5083fbb
push id99731
push userbmo:jhorak@redhat.com
push dateTue, 20 Feb 2018 15:10:49 +0000
reviewersstransky
bugs1431337
milestone60.0a1
Bug 1431337 - Scale content for the actual monitor, not the first one; r?stransky We need to use scaling factor of the monitor on which application is actually positioned. Previously we used ScreenHelperGTK::GetGTKMonitorScaleFactor() which use the first monitor. This does not work on hidpi+normal dpi monitors setup. The GetSystemFontInfo() cannot return scaled value of the font by default monitor scale factor. We need to scale it in nsLookAndFeel::GetFontImpl by aDevPixPerCSSPixel like implementation for Windows does. We also need to check layout.css.devPixelsPerPx because we cannot scale per monitor when this preference is set to positive number. MozReview-Commit-ID: AwT2NvkEqvz
widget/gtk/WindowSurfaceWayland.cpp
widget/gtk/WindowSurfaceWayland.h
widget/gtk/nsLookAndFeel.cpp
widget/gtk/nsNativeThemeGTK.cpp
widget/gtk/nsNativeThemeGTK.h
widget/gtk/nsWindow.cpp
--- a/widget/gtk/WindowSurfaceWayland.cpp
+++ b/widget/gtk/WindowSurfaceWayland.cpp
@@ -588,28 +588,40 @@ WindowSurfaceWayland::~WindowSurfaceWayl
       NewRunnableFunction("WaylandDisplayRelease",
                           &WaylandDisplayRelease,
                           mWaylandDisplay->GetDisplay()));
   } else {
     WaylandDisplayRelease(mWaylandDisplay->GetDisplay());
   }
 }
 
+void
+WindowSurfaceWayland::UpdateScaleFactor()
+{
+  wl_surface* waylandSurface = mWindow->GetWaylandSurface();
+  if (waylandSurface) {
+    wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor());
+  }
+}
+
 WindowBackBuffer*
 WindowSurfaceWayland::GetBufferToDraw(int aWidth, int aHeight)
 {
   if (!mFrontBuffer) {
     mFrontBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
     mBackBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
     return mFrontBuffer;
   }
 
   if (!mFrontBuffer->IsAttached()) {
     if (!mFrontBuffer->IsMatchingSize(aWidth, aHeight)) {
       mFrontBuffer->Resize(aWidth, aHeight);
+      // There's a chance that scale factor has been changed
+      // when buffer size changed
+      UpdateScaleFactor();
     }
     return mFrontBuffer;
   }
 
   // Front buffer is used by compositor, draw to back buffer
   if (mBackBuffer->IsAttached()) {
     NS_WARNING("No drawing buffer available");
     return nullptr;
@@ -696,20 +708,16 @@ WindowSurfaceWayland::Commit(const Layou
       // Delete frame callback connected to obsoleted wl_surface.
       wl_callback_destroy(mFrameCallback);
     }
 
     mFrameCallback = wl_surface_frame(waylandSurface);
     wl_callback_add_listener(mFrameCallback, &frame_listener, this);
     mFrameCallbackSurface = waylandSurface;
 
-    // Let the wayland know of the current scaling factor for the hdpi
-    // displays
-    wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor());
-
     // There's no pending frame callback so we can draw immediately
     // and create frame callback for possible subsequent drawing.
     mFrontBuffer->Attach(waylandSurface);
     mDelayedCommit = false;
   }
 }
 
 void
--- a/widget/gtk/WindowSurfaceWayland.h
+++ b/widget/gtk/WindowSurfaceWayland.h
@@ -108,16 +108,17 @@ public:
   ~WindowSurfaceWayland();
 
   already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion) override;
   void                      Commit(const LayoutDeviceIntRegion& aInvalidRegion) final override;
   void                      FrameCallbackHandler();
 
 private:
   WindowBackBuffer*         GetBufferToDraw(int aWidth, int aHeight);
+  void                      UpdateScaleFactor();
 
   // TODO: Do we need to hold a reference to nsWindow object?
   nsWindow*                 mWindow;
   nsWaylandDisplay*         mWaylandDisplay;
   WindowBackBuffer*         mFrontBuffer;
   WindowBackBuffer*         mBackBuffer;
   wl_callback*              mFrameCallback;
   wl_surface*               mFrameCallbackSurface;
--- a/widget/gtk/nsLookAndFeel.cpp
+++ b/widget/gtk/nsLookAndFeel.cpp
@@ -732,51 +732,47 @@ GetSystemFontInfo(GtkStyleContext *aStyl
     float size = float(pango_font_description_get_size(desc)) / PANGO_SCALE;
 
     // |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::GetFontScaleDPI()) / 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.
-    size *= mozilla::widget::ScreenHelperGTK::GetGTKMonitorScaleFactor();
-
-    // |size| is now pixels
+    // |size| is now pixels but not scaled for the hidpi displays,
+    // this needs to be done in GetFontImpl where the aDevPixPerCSSPixel
+    // parameter is provided.
 
     aFontStyle->size = size;
 
     pango_font_description_free(desc);
 }
 
 bool
 nsLookAndFeel::GetFontImpl(FontID aID, nsString& aFontName,
                            gfxFontStyle& aFontStyle,
                            float aDevPixPerCSSPixel)
 {
   switch (aID) {
     case eFont_Menu:         // css2
     case eFont_PullDownMenu: // css3
       aFontName = mMenuFontName;
       aFontStyle = mMenuFontStyle;
-      return true;
+      break;
 
     case eFont_Field:        // css3
     case eFont_List:         // css3
       aFontName = mFieldFontName;
       aFontStyle = mFieldFontStyle;
-      return true;
+      break;
 
     case eFont_Button:       // css3
       aFontName = mButtonFontName;
       aFontStyle = mButtonFontStyle;
-      return true;
+      break;
 
     case eFont_Caption:      // css2
     case eFont_Icon:         // css2
     case eFont_MessageBox:   // css2
     case eFont_SmallCaption: // css2
     case eFont_StatusBar:    // css2
     case eFont_Window:       // css3
     case eFont_Document:     // css3
@@ -784,18 +780,28 @@ nsLookAndFeel::GetFontImpl(FontID aID, n
     case eFont_Desktop:      // css3
     case eFont_Info:         // css3
     case eFont_Dialog:       // css3
     case eFont_Tooltips:     // moz
     case eFont_Widget:       // moz
     default:
       aFontName = mDefaultFontName;
       aFontStyle = mDefaultFontStyle;
-      return true;
+      break;
   }
+  // Scale the font for the current monitor
+  double scaleFactor = nsIWidget::DefaultScaleOverride();
+  if (scaleFactor > 0) {
+    aFontStyle.size *= aDevPixPerCSSPixel;
+  } else {
+    // Remove effect of font scale because it has been already applied in
+    // GetSystemFontInfo
+    aFontStyle.size *= aDevPixPerCSSPixel / gfxPlatformGtk::GetFontScaleFactor();
+  }
+  return true;
 }
 
 void
 nsLookAndFeel::EnsureInit()
 {
     GdkColor colorValue;
     GdkColor *colorValuePtr;
 
--- a/widget/gtk/nsNativeThemeGTK.cpp
+++ b/widget/gtk/nsNativeThemeGTK.cpp
@@ -32,16 +32,17 @@
 #include <gtk/gtkx.h>
 
 #include "gfxContext.h"
 #include "gfxPlatformGtk.h"
 #include "gfxGdkNativeRenderer.h"
 #include "mozilla/gfx/BorrowedContext.h"
 #include "mozilla/gfx/HelpersCairo.h"
 #include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/Preferences.h"
 
 #ifdef MOZ_X11
 #  ifdef CAIRO_HAS_XLIB_SURFACE
 #    include "cairo-xlib.h"
 #  endif
 #  ifdef CAIRO_HAS_XLIB_XRENDER_SURFACE
 #    include "cairo-xlib-xrender.h"
 #  endif
@@ -54,16 +55,39 @@ using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::widget;
 
 NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeGTK, nsNativeTheme, nsITheme,
                                                              nsIObserver)
 
 static int gLastGdkError;
 
+// Return scale factor of the monitor where the window is located
+// by the most part or layout.css.devPixelsPerPx pref if set to > 0.
+static inline gint
+GetMonitorScaleFactor(nsIFrame* aFrame)
+{
+  // When the layout.css.devPixelsPerPx is set the scale can be < 1,
+  // the real monitor scale cannot go under 1.
+  double scale = nsIWidget::DefaultScaleOverride();
+  if (scale <= 0) {
+    nsIWidget* rootWidget = aFrame->PresContext()->GetRootWidget();
+    if (rootWidget) {
+        // We need to use GetDefaultScale() despite it returns monitor scale
+        // factor multiplied by font scale factor because it is the only scale
+        // updated in nsPuppetWidget.
+        // Since we don't want to apply font scale factor for UI elements
+        // (because GTK does not do so) we need to remove that from returned value.
+        return rootWidget->GetDefaultScale().scale / gfxPlatformGtk::GetFontScaleFactor();
+    }
+  }
+  // We cannot return zero scale because that would lead to divide by zero
+  return (scale < 1) ? 1 : int(round(scale));
+}
+
 nsNativeThemeGTK::nsNativeThemeGTK()
 {
   if (moz_gtk_init() != MOZ_GTK_SUCCESS) {
     memset(mDisabledWidgetTypes, 0xff, sizeof(mDisabledWidgetTypes));
     return;
   }
 
   // We have to call moz_gtk_shutdown before the event loop stops running.
@@ -1040,17 +1064,17 @@ nsNativeThemeGTK::GetExtraSizeForWidget(
       } else {
         aExtra->bottom = extra;
       }
       return false;
     }
   default:
     return false;
   }
-  gint scale = ScreenHelperGTK::GetGTKMonitorScaleFactor();
+  gint scale = GetMonitorScaleFactor(aFrame);
   aExtra->top *= scale;
   aExtra->right *= scale;
   aExtra->bottom *= scale;
   aExtra->left *= scale;
   return true;
 }
 
 NS_IMETHODIMP
@@ -1068,17 +1092,17 @@ nsNativeThemeGTK::DrawWidgetBackground(g
                             &flags))
     return NS_OK;
 
   gfxContext* ctx = aContext;
   nsPresContext *presContext = aFrame->PresContext();
 
   gfxRect rect = presContext->AppUnitsToGfxUnits(aRect);
   gfxRect dirtyRect = presContext->AppUnitsToGfxUnits(aDirtyRect);
-  gint scaleFactor = ScreenHelperGTK::GetGTKMonitorScaleFactor();
+  gint scaleFactor = GetMonitorScaleFactor(aFrame);
 
   // Align to device pixels where sensible
   // to provide crisper and faster drawing.
   // Don't snap if it's a non-unit scale factor. We're going to have to take
   // slow paths then in any case.
   bool snapped = ctx->UserToDevicePixelSnapped(rect);
   if (snapped) {
     // Leave rect in device coords but make dirtyRect consistent.
@@ -1314,17 +1338,17 @@ nsNativeThemeGTK::GetWidgetBorder(nsDevi
       break;
     MOZ_FALLTHROUGH;
   default:
     {
       GetCachedWidgetBorder(aFrame, aWidgetType, direction, aResult);
     }
   }
 
-  gint scale = ScreenHelperGTK::GetGTKMonitorScaleFactor();
+  gint scale = GetMonitorScaleFactor(aFrame);
   aResult->top *= scale;
   aResult->right *= scale;
   aResult->bottom *= scale;
   aResult->left *= scale;
   return NS_OK;
 }
 
 bool
@@ -1372,17 +1396,17 @@ nsNativeThemeGTK::GetWidgetPadding(nsDev
         if (aWidgetType == NS_THEME_MENUITEM)
           moz_gtk_menuitem_get_horizontal_padding(&horizontal_padding);
         else
           moz_gtk_checkmenuitem_get_horizontal_padding(&horizontal_padding);
 
         aResult->left += horizontal_padding;
         aResult->right += horizontal_padding;
 
-        gint scale = ScreenHelperGTK::GetGTKMonitorScaleFactor();
+        gint scale = GetMonitorScaleFactor(aFrame);
         aResult->top *= scale;
         aResult->right *= scale;
         aResult->bottom *= scale;
         aResult->left *= scale;
 
         return true;
       }
   }
@@ -1621,18 +1645,17 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n
         moz_gtk_get_arrow_size(MOZ_GTK_DROPDOWN,
                                &aResult->width, &aResult->height);
       }
       // else the minimum size is missing consideration of container
       // descendants; the value returned here will not be helpful, but the
       // box model may consider border and padding with child minimum sizes.
 
       nsIntMargin border;
-      nsNativeThemeGTK::GetWidgetBorder(aFrame->PresContext()->DeviceContext(),
-                                        aFrame, aWidgetType, &border);
+      GetCachedWidgetBorder(aFrame, aWidgetType, GetTextDirection(aFrame), &border);
       aResult->width += border.left + border.right;
       aResult->height += border.top + border.bottom;
     }
     break;
 #ifdef MOZ_WIDGET_GTK
   case NS_THEME_NUMBER_INPUT:
   case NS_THEME_TEXTFIELD:
     {
@@ -1674,17 +1697,17 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n
 
       moz_gtk_get_treeview_expander_size(&expander_size);
       aResult->width = aResult->height = expander_size;
       *aIsOverridable = false;
     }
     break;
   }
 
-  *aResult = *aResult * ScreenHelperGTK::GetGTKMonitorScaleFactor();
+  *aResult = *aResult * GetMonitorScaleFactor(aFrame);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNativeThemeGTK::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType,
                                      nsAtom* aAttribute, bool* aShouldRepaint,
                                      const nsAttrValue* aOldValue)
--- a/widget/gtk/nsNativeThemeGTK.h
+++ b/widget/gtk/nsNativeThemeGTK.h
@@ -71,17 +71,16 @@ public:
   NS_IMETHOD_(bool) WidgetIsContainer(uint8_t aWidgetType) override;
 
   NS_IMETHOD_(bool) ThemeDrawsFocusForWidget(uint8_t aWidgetType) override;
 
   virtual bool ThemeNeedsComboboxDropmarker() override;
 
   virtual Transparency GetWidgetTransparency(nsIFrame* aFrame,
                                              uint8_t aWidgetType) override;
-
   nsNativeThemeGTK();
 
 protected:
   virtual ~nsNativeThemeGTK();
 
 private:
   GtkTextDirection GetTextDirection(nsIFrame* aFrame);
   gint GetTabMarginPixels(nsIFrame* aFrame);
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -5926,16 +5926,17 @@ widget_composited_changed_cb (GtkWidget*
 
 static void
 scale_changed_cb (GtkWidget* widget, GParamSpec* aPSpec, gpointer aPointer)
 {
     RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
     if (!window) {
       return;
     }
+    // This eventually propagate new scale to the PuppetWidgets
     window->OnDPIChanged();
 
     // configure_event is already fired before scale-factor signal,
     // but size-allocate isn't fired by changing scale
     GtkAllocation allocation;
     gtk_widget_get_allocation(widget, &allocation);
     window->OnSizeAllocate(&allocation);
 }