[mutter] theme: Scale window decorations on HiDPI displays



commit 57c1078ee742d9f01e80e7fe05c88adfec4b4ac3
Author: Florian Müllner <fmuellner gnome org>
Date:   Thu Oct 2 01:05:18 2014 +0200

    theme: Scale window decorations on HiDPI displays
    
    As we opt out of GTK+/Clutter's HiDPI handling, we need to apply the
    window scaling factor manually to decorations, both the geometry and
    when drawing.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=744354

 src/ui/frames.c        |    9 +++-
 src/ui/theme-private.h |    1 +
 src/ui/theme.c         |  104 +++++++++++++++++++++++++++++++++++-------------
 3 files changed, 84 insertions(+), 30 deletions(-)
---
diff --git a/src/ui/frames.c b/src/ui/frames.c
index 74135d9..362d7b6 100644
--- a/src/ui/frames.c
+++ b/src/ui/frames.c
@@ -1389,6 +1389,7 @@ meta_ui_frame_get_mask (MetaUIFrame *frame,
   MetaFrameBorders borders;
   MetaFrameFlags flags;
   MetaRectangle frame_rect;
+  int scale = meta_theme_get_window_scaling_factor ();
 
   meta_window_get_frame_rect (frame->meta_window, &frame_rect);
 
@@ -1396,9 +1397,13 @@ meta_ui_frame_get_mask (MetaUIFrame *frame,
 
   meta_style_info_set_flags (frame->style_info, flags);
   meta_ui_frame_get_borders (frame, &borders);
+
+  /* See comment in meta_frame_layout_draw_with_style() for details on HiDPI handling */
+  cairo_scale (cr, scale, scale);
   gtk_render_background (frame->style_info->styles[META_STYLE_ELEMENT_FRAME], cr,
-                         borders.invisible.left, borders.invisible.top,
-                         frame_rect.width, frame_rect.height);
+                         borders.invisible.left / scale,
+                         borders.invisible.top / scale,
+                         frame_rect.width / scale, frame_rect.height / scale);
 }
 
 /* XXX -- this is disgusting. Find a better approach here.
diff --git a/src/ui/theme-private.h b/src/ui/theme-private.h
index 4a5eddb..cdf23a0 100644
--- a/src/ui/theme-private.h
+++ b/src/ui/theme-private.h
@@ -276,5 +276,6 @@ void meta_theme_calc_geometry (MetaTheme              *theme,
 
 int                   meta_pango_font_desc_get_text_height (const PangoFontDescription *font_desc,
                                                             PangoContext         *context);
+int                   meta_theme_get_window_scaling_factor (void);
 
 #endif /* META_THEME_PRIVATE_H */
diff --git a/src/ui/theme.c b/src/ui/theme.c
index 243d369..f73b428 100644
--- a/src/ui/theme.c
+++ b/src/ui/theme.c
@@ -30,6 +30,8 @@
 
 #define DEBUG_FILL_STRUCT(s) memset ((s), 0xef, sizeof (*(s)))
 
+static void scale_border (GtkBorder *border, double factor);
+
 static MetaFrameLayout *
 meta_frame_layout_new  (void)
 {
@@ -65,6 +67,7 @@ meta_frame_layout_get_borders (const MetaFrameLayout *layout,
                                MetaFrameBorders      *borders)
 {
   int buttons_height, content_height, draggable_borders;
+  int scale = meta_theme_get_window_scaling_factor ();
 
   meta_frame_borders_clear (borders);
 
@@ -110,6 +113,26 @@ meta_frame_layout_get_borders (const MetaFrameLayout *layout,
   borders->total.right  = borders->invisible.right  + borders->visible.right;
   borders->total.bottom = borders->invisible.bottom + borders->visible.bottom;
   borders->total.top    = borders->invisible.top    + borders->visible.top;
+
+  /* Scale geometry for HiDPI, see comment in meta_frame_layout_draw_with_style() */
+  scale_border (&borders->visible, scale);
+  scale_border (&borders->invisible, scale);
+  scale_border (&borders->total, scale);
+}
+
+int
+meta_theme_get_window_scaling_factor ()
+{
+  GdkScreen *screen;
+  GValue value = G_VALUE_INIT;
+
+  g_value_init (&value, G_TYPE_INT);
+
+  screen = gdk_screen_get_default ();
+  if (gdk_screen_get_setting (screen, "gdk-window-scaling-factor", &value))
+    return g_value_get_int (&value);
+  else
+    return 1;
 }
 
 void
@@ -117,8 +140,8 @@ meta_frame_layout_apply_scale (const MetaFrameLayout *layout,
                                PangoFontDescription  *font_desc)
 {
   int size = pango_font_description_get_size (font_desc);
-  pango_font_description_set_size (font_desc,
-                                   MAX (size * layout->title_scale, 1));
+  double scale = layout->title_scale / meta_theme_get_window_scaling_factor ();
+  pango_font_description_set_size (font_desc, MAX (size * scale, 1));
 }
 
 static MetaButtonSpace*
@@ -306,6 +329,7 @@ meta_frame_layout_calc_geometry (MetaFrameLayout        *layout,
   int content_width, content_height;
   int button_width, button_height;
   int min_size_for_rounding;
+  int scale = meta_theme_get_window_scaling_factor ();
 
   /* the left/right rects in order; the max # of rects
    * is the number of button functions
@@ -325,11 +349,12 @@ meta_frame_layout_calc_geometry (MetaFrameLayout        *layout,
 
   fgeom->borders = borders;
 
+  /* Scale geometry for HiDPI, see comment in meta_frame_layout_draw_with_style() */
   fgeom->content_border = layout->frame_border;
-  fgeom->content_border.left   += layout->titlebar_border.left;
-  fgeom->content_border.right  += layout->titlebar_border.right;
-  fgeom->content_border.top    += layout->titlebar_border.top;
-  fgeom->content_border.bottom += layout->titlebar_border.bottom;
+  fgeom->content_border.left   += layout->titlebar_border.left * scale;
+  fgeom->content_border.right  += layout->titlebar_border.right * scale;
+  fgeom->content_border.top    += layout->titlebar_border.top * scale;
+  fgeom->content_border.bottom += layout->titlebar_border.bottom * scale;
 
   width = client_width + borders.total.left + borders.total.right;
 
@@ -349,6 +374,8 @@ meta_frame_layout_calc_geometry (MetaFrameLayout        *layout,
                  layout->button_border.left + layout->button_border.right;
   button_height = layout->icon_size +
                   layout->button_border.top + layout->button_border.bottom;
+  button_width *= scale;
+  button_height *= scale;
 
   /* FIXME all this code sort of pretends that duplicate buttons
    * with the same function are allowed, but that breaks the
@@ -408,11 +435,11 @@ meta_frame_layout_calc_geometry (MetaFrameLayout        *layout,
 
       space_used_by_buttons += button_width * n_left;
       space_used_by_buttons += (button_width * 0.75) * n_left_spacers;
-      space_used_by_buttons += layout->titlebar_spacing * MAX (n_left - 1, 0);
+      space_used_by_buttons += layout->titlebar_spacing * scale * MAX (n_left - 1, 0);
 
       space_used_by_buttons += button_width * n_right;
       space_used_by_buttons += (button_width * 0.75) * n_right_spacers;
-      space_used_by_buttons += layout->titlebar_spacing * MAX (n_right - 1, 0);
+      space_used_by_buttons += layout->titlebar_spacing * scale * MAX (n_right - 1, 0);
 
       if (space_used_by_buttons <= content_width)
         break; /* Everything fits, bail out */
@@ -564,7 +591,7 @@ meta_frame_layout_calc_geometry (MetaFrameLayout        *layout,
 
       x = rect->visible.x + rect->visible.width;
       if (i < n_left - 1)
-        x += layout->titlebar_spacing;
+        x += layout->titlebar_spacing * scale;
       if (left_buttons_has_spacer[i])
         x += (button_width * 0.75);
     }
@@ -587,7 +614,7 @@ meta_frame_layout_calc_geometry (MetaFrameLayout        *layout,
   if (flags & META_FRAME_SHADED)
     min_size_for_rounding = 0;
   else
-    min_size_for_rounding = 5;
+    min_size_for_rounding = 5 * scale;
 
   fgeom->top_left_corner_rounded_radius = 0;
   fgeom->top_right_corner_rounded_radius = 0;
@@ -595,14 +622,14 @@ meta_frame_layout_calc_geometry (MetaFrameLayout        *layout,
   fgeom->bottom_right_corner_rounded_radius = 0;
 
   if (borders.visible.top + borders.visible.left >= min_size_for_rounding)
-    fgeom->top_left_corner_rounded_radius = layout->top_left_corner_rounded_radius;
+    fgeom->top_left_corner_rounded_radius = layout->top_left_corner_rounded_radius * scale;
   if (borders.visible.top + borders.visible.right >= min_size_for_rounding)
-    fgeom->top_right_corner_rounded_radius = layout->top_right_corner_rounded_radius;
+    fgeom->top_right_corner_rounded_radius = layout->top_right_corner_rounded_radius * scale;
 
   if (borders.visible.bottom + borders.visible.left >= min_size_for_rounding)
-    fgeom->bottom_left_corner_rounded_radius = layout->bottom_left_corner_rounded_radius;
+    fgeom->bottom_left_corner_rounded_radius = layout->bottom_left_corner_rounded_radius * scale;
   if (borders.visible.bottom + borders.visible.right >= min_size_for_rounding)
-    fgeom->bottom_right_corner_rounded_radius = layout->bottom_right_corner_rounded_radius;
+    fgeom->bottom_right_corner_rounded_radius = layout->bottom_right_corner_rounded_radius * scale;
 }
 
 static void
@@ -696,13 +723,27 @@ meta_frame_layout_draw_with_style (MetaFrameLayout         *layout,
   GdkRectangle titlebar_rect;
   GdkRectangle button_rect;
   const MetaFrameBorders *borders;
+  int scale = meta_theme_get_window_scaling_factor ();
+
+  /* We opt out of GTK+/Clutter's HiDPI handling, so we have to do the scaling
+   * ourselves; the nitty-gritty is a bit confusing, so here is an overview:
+   *  - the values in MetaFrameLayout are always as they appear in the theme,
+   *    i.e. unscaled
+   *  - calculated values (borders, MetaFrameGeometry) include the scale - as
+   *    the geometry is comprised of scaled decorations and the client size
+   *    which we must not scale, we don't have another option
+   *  - for drawing, we scale the canvas to have GTK+ render elements (borders,
+   *    radii, ...) at the correct scale - as a result, we have to "unscale"
+   *    the geometry again to not apply the scaling twice
+   */
+  cairo_scale (cr, scale, scale);
 
   borders = &fgeom->borders;
 
-  visible_rect.x = borders->invisible.left;
-  visible_rect.y = borders->invisible.top;
-  visible_rect.width = fgeom->width - borders->invisible.left - borders->invisible.right;
-  visible_rect.height = fgeom->height - borders->invisible.top - borders->invisible.bottom;
+  visible_rect.x = borders->invisible.left / scale;
+  visible_rect.y = borders->invisible.top / scale;
+  visible_rect.width = (fgeom->width - borders->invisible.left - borders->invisible.right) / scale;
+  visible_rect.height = (fgeom->height - borders->invisible.top - borders->invisible.bottom) / scale;
 
   meta_style_info_set_flags (style_info, flags);
 
@@ -717,7 +758,7 @@ meta_frame_layout_draw_with_style (MetaFrameLayout         *layout,
   titlebar_rect.x = visible_rect.x;
   titlebar_rect.y = visible_rect.y;
   titlebar_rect.width = visible_rect.width;
-  titlebar_rect.height = borders->visible.top;
+  titlebar_rect.height = borders->visible.top / scale;
 
   style = style_info->styles[META_STYLE_ELEMENT_TITLEBAR];
   gtk_render_background (style, cr,
@@ -735,7 +776,7 @@ meta_frame_layout_draw_with_style (MetaFrameLayout         *layout,
       pango_layout_set_width (title_layout, -1);
       pango_layout_get_pixel_extents (title_layout, NULL, &logical);
 
-      text_width = MIN(fgeom->title_rect.width, logical.width);
+      text_width = MIN(fgeom->title_rect.width / scale, logical.width);
 
       if (text_width < logical.width)
         pango_layout_set_width (title_layout, PANGO_SCALE * text_width);
@@ -744,10 +785,10 @@ meta_frame_layout_draw_with_style (MetaFrameLayout         *layout,
       x = titlebar_rect.x + (titlebar_rect.width - text_width) / 2;
       y = titlebar_rect.y + (titlebar_rect.height - logical.height) / 2;
 
-      if (x < fgeom->title_rect.x)
-        x = fgeom->title_rect.x;
-      else if (x + text_width > fgeom->title_rect.x + fgeom->title_rect.width)
-        x = fgeom->title_rect.x + fgeom->title_rect.width - text_width;
+      if (x < fgeom->title_rect.x / scale)
+        x = fgeom->title_rect.x / scale;
+      else if (x + text_width > (fgeom->title_rect.x + fgeom->title_rect.width) / scale)
+        x = (fgeom->title_rect.x + fgeom->title_rect.width) / scale - text_width;
 
       style = style_info->styles[META_STYLE_ELEMENT_TITLE];
       gtk_render_layout (style, cr, x, y, title_layout);
@@ -763,6 +804,11 @@ meta_frame_layout_draw_with_style (MetaFrameLayout         *layout,
 
       get_button_rect (button_type, fgeom, &button_rect);
 
+      button_rect.x /= scale;
+      button_rect.y /= scale;
+      button_rect.width /= scale;
+      button_rect.height /= scale;
+
       if (button_states[button_type] == META_BUTTON_STATE_PRELIGHT)
         gtk_style_context_set_state (style, state | GTK_STATE_PRELIGHT);
       else if (button_states[button_type] == META_BUTTON_STATE_PRESSED)
@@ -815,9 +861,10 @@ meta_frame_layout_draw_with_style (MetaFrameLayout         *layout,
               GtkIconInfo *info;
               GdkPixbuf *pixbuf;
 
-              info = gtk_icon_theme_lookup_icon (theme, icon_name, layout->icon_size, 0);
+              info = gtk_icon_theme_lookup_icon_for_scale (theme, icon_name,
+                                                           layout->icon_size, scale, 0);
               pixbuf = gtk_icon_info_load_symbolic_for_context (info, style, NULL, NULL);
-              surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, 1, NULL);
+              surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, NULL);
             }
 
           if (surface)
@@ -825,8 +872,8 @@ meta_frame_layout_draw_with_style (MetaFrameLayout         *layout,
               float width, height;
               int x, y;
 
-              width = cairo_image_surface_get_width (surface);
-              height = cairo_image_surface_get_height (surface);
+              width = cairo_image_surface_get_width (surface) / scale;
+              height = cairo_image_surface_get_height (surface) / scale;
               x = button_rect.x + (button_rect.width - width) / 2;
               y = button_rect.y + (button_rect.height - height) / 2;
 
@@ -939,6 +986,7 @@ create_style_context (GType            widget_type,
   va_list ap;
 
   style = gtk_style_context_new ();
+  gtk_style_context_set_scale (style, meta_theme_get_window_scaling_factor ());
   gtk_style_context_set_parent (style, parent_style);
 
   if (parent_style)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]