Bug 1283299 - Part 2: Add support for drawing decorated windows on GTK 3.20. r?karlt draft
authorAndrew Comminos <andrew@comminos.com>
Tue, 05 Jul 2016 13:49:42 -0400
changeset 384219 b0acd8753c8127cac4e126dd0c530c4910ca3ff0
parent 384218 bc9c6ccf6bc0a87bad8964ca6aba1b73b31dacbe
child 384220 1978e42078dc52777450bd81cb6df4dbb0f74ede
push id22212
push userbmo:andrew@comminos.com
push dateTue, 05 Jul 2016 20:52:16 +0000
reviewerskarlt
bugs1283299
milestone50.0a1
Bug 1283299 - Part 2: Add support for drawing decorated windows on GTK 3.20. r?karlt MozReview-Commit-ID: ymDFmAN0li
gfx/src/nsThemeConstants.h
layout/style/nsCSSKeywordList.h
layout/style/nsCSSProps.cpp
widget/gtk/WidgetStyleCache.cpp
widget/gtk/gtk3drawing.cpp
widget/gtk/gtkdrawing.h
widget/gtk/nsNativeThemeGTK.cpp
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
--- a/gfx/src/nsThemeConstants.h
+++ b/gfx/src/nsThemeConstants.h
@@ -287,8 +287,9 @@
 
 #define NS_THEME_MAC_VIBRANCY_LIGHT                        243
 #define NS_THEME_MAC_VIBRANCY_DARK                         244
 #define NS_THEME_MAC_DISCLOSURE_BUTTON_OPEN                245
 #define NS_THEME_MAC_DISCLOSURE_BUTTON_CLOSED              246
 
 #define NS_THEME_GTK_INFO_BAR                              247
 #define NS_THEME_MAC_SOURCE_LIST                           248
+#define NS_THEME_GTK_WINDOW_DECORATION                     249
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -68,16 +68,17 @@ CSS_KEY(-moz-fixed, _moz_fixed)
 CSS_KEY(-moz-grabbing, _moz_grabbing)
 CSS_KEY(-moz-grab, _moz_grab)
 CSS_KEY(-moz-grid-group, _moz_grid_group)
 CSS_KEY(-moz-grid-line, _moz_grid_line)
 CSS_KEY(-moz-grid, _moz_grid)
 CSS_KEY(-moz-groupbox, _moz_groupbox)
 CSS_KEY(-moz-gtk-info-bar, _moz_gtk_info_bar)
 CSS_KEY(-moz-gtk-info-bar-text, _moz_gtk_info_bar_text)
+CSS_KEY(-moz-gtk-window-decoration, _moz_gtk_window_decoration)
 CSS_KEY(-moz-hidden-unscrollable, _moz_hidden_unscrollable)
 CSS_KEY(-moz-hyperlinktext, _moz_hyperlinktext)
 CSS_KEY(-moz-html-cellhighlight, _moz_html_cellhighlight)
 CSS_KEY(-moz-html-cellhighlighttext, _moz_html_cellhighlighttext)
 CSS_KEY(-moz-image-rect, _moz_image_rect)
 CSS_KEY(-moz-info, _moz_info)
 CSS_KEY(-moz-inline-box, _moz_inline_box)
 CSS_KEY(-moz-inline-grid, _moz_inline_grid)
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -862,16 +862,17 @@ const KTableEntry nsCSSProps::kAppearanc
   { eCSSKeyword__moz_window_button_box,         NS_THEME_WINDOW_BUTTON_BOX },
   { eCSSKeyword__moz_window_button_box_maximized, NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED },
   { eCSSKeyword__moz_win_exclude_glass,         NS_THEME_WIN_EXCLUDE_GLASS },
   { eCSSKeyword__moz_mac_vibrancy_light,        NS_THEME_MAC_VIBRANCY_LIGHT },
   { eCSSKeyword__moz_mac_vibrancy_dark,         NS_THEME_MAC_VIBRANCY_DARK },
   { eCSSKeyword__moz_mac_disclosure_button_open,   NS_THEME_MAC_DISCLOSURE_BUTTON_OPEN },
   { eCSSKeyword__moz_mac_disclosure_button_closed, NS_THEME_MAC_DISCLOSURE_BUTTON_CLOSED },
   { eCSSKeyword__moz_gtk_info_bar,              NS_THEME_GTK_INFO_BAR },
+  { eCSSKeyword__moz_gtk_window_decoration,     NS_THEME_GTK_WINDOW_DECORATION },
   { eCSSKeyword__moz_mac_source_list,           NS_THEME_MAC_SOURCE_LIST },
   { eCSSKeyword_UNKNOWN,                        -1 }
 };
 
 const KTableEntry nsCSSProps::kBackfaceVisibilityKTable[] = {
   { eCSSKeyword_visible, NS_STYLE_BACKFACE_VISIBILITY_VISIBLE },
   { eCSSKeyword_hidden, NS_STYLE_BACKFACE_VISIBILITY_HIDDEN },
   { eCSSKeyword_UNKNOWN, -1 }
--- a/widget/gtk/WidgetStyleCache.cpp
+++ b/widget/gtk/WidgetStyleCache.cpp
@@ -20,21 +20,32 @@ static GtkStyleContext* sStyleStorage[MO
 static bool sStyleContextNeedsRestore;
 #ifdef DEBUG
 static GtkStyleContext* sCurrentStyleContext;
 #endif
 static GtkStyleContext*
 GetCssNodeStyleInternal(WidgetNodeType aNodeType);
 
 static GtkWidget*
-CreateWindowWidget()
+CreateWindowWidget(WidgetNodeType type)
 {
   GtkWidget *widget = gtk_window_new(GTK_WINDOW_POPUP);
   gtk_widget_realize(widget);
   gtk_widget_set_name(widget, "MozillaGtkWidget");
+  GtkStyleContext* style = gtk_widget_get_style_context(widget);
+  switch (type) {
+  case MOZ_GTK_WINDOW_CSD:
+    gtk_style_context_add_class(style, "csd");
+    break;
+  case MOZ_GTK_WINDOW_SOLID_CSD:
+    gtk_style_context_add_class(style, "solid-csd");
+    break;
+  default:
+    break;
+  }
   return widget;
 }
 
 static GtkWidget*
 CreateWindowContainerWidget()
 {
   GtkWidget *widget = gtk_fixed_new();
   gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_WINDOW)), widget);
@@ -105,28 +116,30 @@ CreateProgressWidget()
   return widget;
 }
 
 static GtkWidget*
 CreateTooltipWidget()
 {
   MOZ_ASSERT(gtk_check_version(3, 20, 0) != nullptr,
              "CreateTooltipWidget should be used for Gtk < 3.20 only.");
-  GtkWidget* widget = CreateWindowWidget();
+  GtkWidget* widget = CreateWindowWidget(MOZ_GTK_WINDOW);
   GtkStyleContext* style = gtk_widget_get_style_context(widget);
   gtk_style_context_add_class(style, GTK_STYLE_CLASS_TOOLTIP);
   return widget;
 }
 
 static GtkWidget*
 CreateWidget(WidgetNodeType aWidgetType)
 {
   switch (aWidgetType) {
     case MOZ_GTK_WINDOW:
-      return CreateWindowWidget();
+    case MOZ_GTK_WINDOW_CSD:
+    case MOZ_GTK_WINDOW_SOLID_CSD:
+      return CreateWindowWidget(aWidgetType);
     case MOZ_GTK_WINDOW_CONTAINER:
       return CreateWindowContainerWidget();
     case MOZ_GTK_CHECKBUTTON_CONTAINER:
       return CreateCheckboxWidget();
     case MOZ_GTK_PROGRESSBAR:
       return CreateProgressWidget();
     case MOZ_GTK_RADIOBUTTON_CONTAINER:
       return CreateRadiobuttonWidget();
@@ -259,16 +272,24 @@ GetCssNodeStyleInternal(WidgetNodeType a
       /* Progress bar background (trough) */
       style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
                                  MOZ_GTK_PROGRESSBAR);
       break;
     case MOZ_GTK_PROGRESS_CHUNK:
       style = CreateChildCSSNode("progress",
                                  MOZ_GTK_PROGRESS_TROUGH);
       break;
+    case MOZ_GTK_WINDOW_DECORATION:
+      style = CreateChildCSSNode("decoration",
+                                 MOZ_GTK_WINDOW_CSD);
+      break;
+    case MOZ_GTK_WINDOW_DECORATION_SOLID:
+      style = CreateChildCSSNode("decoration",
+                                 MOZ_GTK_WINDOW_SOLID_CSD);
+      break;
     case MOZ_GTK_TOOLTIP:
       // We create this from the path because GtkTooltipWindow is not public.
       style = CreateCSSNode("tooltip", nullptr, GTK_TYPE_TOOLTIP);
       gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND);
       break; 
     default:
       // TODO - create style from style path
       GtkWidget* widget = GetWidget(aNodeType);
--- a/widget/gtk/gtk3drawing.cpp
+++ b/widget/gtk/gtk3drawing.cpp
@@ -720,16 +720,40 @@ moz_gtk_splitter_get_metrics(gint orient
     } else {
         ensure_vpaned_widget();
         gtk_style_context_get_style(gtk_widget_get_style_context(gVPanedWidget),
                                     "handle_size", size, NULL);
     }
     return MOZ_GTK_SUCCESS;
 }
 
+void
+moz_gtk_get_window_decoration_extents(gint* top, gint* right, gint* bottom, gint* left)
+{
+  MOZ_ASSERT(gtk_check_version(3, 20, 0) == nullptr,
+             "Window decorations are only supported on GTK 3.20+.");
+
+  GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_WINDOW_DECORATION);
+
+  // Available on GTK 3.20+.
+  static auto sGtkRenderBackgroundGetClip =
+    (void (*)(GtkStyleContext*, gdouble, gdouble, gdouble, gdouble, GdkRectangle*))
+    dlsym(RTLD_DEFAULT, "gtk_render_background_get_clip");
+
+  GdkRectangle clip;
+  sGtkRenderBackgroundGetClip(style, 0, 0, 0, 0, &clip);
+
+  *top = -clip.y;
+  *right = clip.width + clip.x;
+  *bottom = clip.height + clip.y;
+  *left = -clip.x;
+
+  ReleaseStyleContext(style);
+}
+
 static gint
 moz_gtk_window_paint(cairo_t *cr, GdkRectangle* rect,
                      GtkTextDirection direction)
 {
     GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_WINDOW, direction);
 
     gtk_style_context_save(style);
     gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND);
@@ -737,16 +761,55 @@ moz_gtk_window_paint(cairo_t *cr, GdkRec
     gtk_style_context_restore(style);
 
     ReleaseStyleContext(style);
 
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
+moz_gtk_window_decoration_paint(cairo_t *cr, GdkRectangle* rect,
+                                GtkTextDirection direction)
+{
+    gint top, right, bottom, left;
+    moz_gtk_get_window_decoration_extents(&top, &right, &bottom, &left);
+
+    GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_WINDOW_DECORATION,
+                                               direction);
+
+    rect->x += left;
+    rect->y += top;
+    rect->width -= left + right;
+    rect->height -= top + bottom;
+
+    gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
+    gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
+
+    ReleaseStyleContext(style);
+
+    return MOZ_GTK_SUCCESS;
+}
+
+static gint
+moz_gtk_window_decoration_solid_paint(cairo_t *cr, GdkRectangle* rect,
+                                      GtkTextDirection direction)
+{
+    GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_WINDOW_DECORATION_SOLID,
+                                               direction);
+
+    gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
+    gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
+
+    ReleaseStyleContext(style);
+
+    return MOZ_GTK_SUCCESS;
+
+}
+
+static gint
 moz_gtk_button_paint(cairo_t *cr, GdkRectangle* rect,
                      GtkWidgetState* state,
                      GtkReliefStyle relief, GtkWidget* widget,
                      GtkTextDirection direction)
 {
     GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
     GtkStyleContext* style = gtk_widget_get_style_context(widget);    
     gint x = rect->x, y=rect->y, width=rect->width, height=rect->height;
@@ -2544,16 +2607,31 @@ moz_gtk_add_style_padding(GtkStyleContex
     gtk_style_context_get_padding(style, GTK_STATE_FLAG_NORMAL, &padding);
 
     *left += padding.left;
     *right += padding.right;
     *top += padding.top;
     *bottom += padding.bottom;
 }
 
+
+static void
+moz_gtk_add_style_margin(GtkStyleContext* style,
+                         gint* left, gint* top, gint* right, gint* bottom)
+{
+    GtkBorder margin;
+
+    gtk_style_context_get_margin(style, GTK_STATE_FLAG_NORMAL, &margin);
+
+    *left += margin.left;
+    *right += margin.right;
+    *top += margin.top;
+    *bottom += margin.bottom;
+}
+
 gint
 moz_gtk_get_widget_border(WidgetNodeType widget, gint* left, gint* top,
                           gint* right, gint* bottom, GtkTextDirection direction,
                           gboolean inhtml)
 {
     GtkWidget* w;
     GtkStyleContext* style;
     *left = *top = *right = *bottom = 0;
@@ -2745,16 +2823,36 @@ moz_gtk_get_widget_border(WidgetNodeType
     case MOZ_GTK_TOOLTIP:
         {
             style = ClaimStyleContext(MOZ_GTK_TOOLTIP);
             moz_gtk_add_style_border(style, left, top, right, bottom);
             moz_gtk_add_style_padding(style, left, top, right, bottom);
             ReleaseStyleContext(style);
             return MOZ_GTK_SUCCESS;
         }
+    case MOZ_GTK_WINDOW_DECORATION:
+        {
+            moz_gtk_get_window_decoration_extents(top, right, bottom, left);
+
+            style = ClaimStyleContext(MOZ_GTK_WINDOW_DECORATION);
+            moz_gtk_add_style_border(style, left, top, right, bottom);
+            moz_gtk_add_style_padding(style, left, top, right, bottom);
+            ReleaseStyleContext(style);
+            return MOZ_GTK_SUCCESS;
+        }
+    case MOZ_GTK_WINDOW_DECORATION_SOLID:
+        {
+            style = ClaimStyleContext(MOZ_GTK_WINDOW_DECORATION_SOLID);
+            moz_gtk_add_style_border(style, left, top, right, bottom);
+            moz_gtk_add_style_padding(style, left, top, right, bottom);
+            moz_gtk_add_style_margin(style, left, top, right, bottom);
+            ReleaseStyleContext(style);
+            return MOZ_GTK_SUCCESS;
+        }
+
     /* These widgets have no borders, since they are not containers. */
     case MOZ_GTK_CHECKBUTTON_LABEL:
     case MOZ_GTK_RADIOBUTTON_LABEL:
     case MOZ_GTK_SPLITTER_HORIZONTAL:
     case MOZ_GTK_SPLITTER_VERTICAL:
     case MOZ_GTK_CHECKBUTTON:
     case MOZ_GTK_RADIOBUTTON:
     case MOZ_GTK_SCROLLBAR_BUTTON:
@@ -3262,16 +3360,22 @@ moz_gtk_widget_paint(WidgetNodeType widg
         return moz_gtk_hpaned_paint(cr, rect, state);
         break;
     case MOZ_GTK_WINDOW:
         return moz_gtk_window_paint(cr, rect, direction);
         break;
     case MOZ_GTK_INFO_BAR:
         return moz_gtk_info_bar_paint(cr, rect, state);
         break;
+    case MOZ_GTK_WINDOW_DECORATION:
+        return moz_gtk_window_decoration_paint(cr, rect, direction);
+        break;
+    case MOZ_GTK_WINDOW_DECORATION_SOLID:
+        return moz_gtk_window_decoration_solid_paint(cr, rect, direction);
+        break;
     default:
         g_warning("Unknown widget type: %d", widget);
     }
 
     return MOZ_GTK_UNKNOWN_WIDGET;
 }
 
 GtkWidget* moz_gtk_get_scrollbar_widget(void)
--- a/widget/gtk/gtkdrawing.h
+++ b/widget/gtk/gtkdrawing.h
@@ -197,16 +197,24 @@ typedef enum {
   /* Paints a GtkVPaned separator */
   MOZ_GTK_SPLITTER_HORIZONTAL,
   /* Paints a GtkHPaned separator */
   MOZ_GTK_SPLITTER_VERTICAL,
   /* Paints the background of a window, dialog or page. */
   MOZ_GTK_WINDOW,
   /* Window container for all widgets */
   MOZ_GTK_WINDOW_CONTAINER,
+  /* Window with the 'csd' style class. */
+  MOZ_GTK_WINDOW_CSD,
+  /* Window with the 'csd' style class. */
+  MOZ_GTK_WINDOW_SOLID_CSD,
+  /* Client-side window decoration node. Available on GTK 3.20+. */
+  MOZ_GTK_WINDOW_DECORATION,
+  /* Solid client-side window decoration node. Available on GTK 3.20+. */
+  MOZ_GTK_WINDOW_DECORATION_SOLID,
   /* Paints a GtkInfoBar, for notifications. */
   MOZ_GTK_INFO_BAR,
 
   MOZ_GTK_WIDGET_NODE_COUNT
 } WidgetNodeType;
 
 /*** General library functions ***/
 /**
@@ -458,16 +466,24 @@ gint moz_gtk_get_menu_separator_height(g
  * orientation:   [IN]  GTK_ORIENTATION_HORIZONTAL or GTK_ORIENTATION_VERTICAL
  * size:          [OUT] width or height of the splitter handle
  *
  * returns:    MOZ_GTK_SUCCESS if there was no error, an error code otherwise
  */
 gint moz_gtk_splitter_get_metrics(gint orientation, gint* size);
 
 /**
+ * Gets the margins to be used for window decorations, typically the extra space
+ * required to draw a drop shadow (obtained from gtk_render_background_get_clip).
+ * Only available on GTK 3.20+.
+ */
+void moz_gtk_get_window_decoration_extents(gint* top, gint* right, gint* bottom,
+                                           gint* left);
+
+/**
  * Retrieve an actual GTK scrollbar widget for style analysis. It will not
  * be modified.
  */
 GtkWidget* moz_gtk_get_scrollbar_widget(void);
 
 /**
  * Get the YTHICKNESS of a tab (notebook extension).
  */
--- a/widget/gtk/nsNativeThemeGTK.cpp
+++ b/widget/gtk/nsNativeThemeGTK.cpp
@@ -19,16 +19,17 @@
 #include "nsGfxCIID.h"
 #include "nsTransform2D.h"
 #include "nsMenuFrame.h"
 #include "prlink.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsRenderingContext.h"
 #include "nsGkAtoms.h"
 #include "nsAttrValueInlines.h"
+#include "nsWindow.h"
 
 #include "mozilla/EventStates.h"
 #include "mozilla/Services.h"
 
 #include <gdk/gdkprivate.h>
 #include <gtk/gtk.h>
 
 #include "gfxContext.h"
@@ -700,16 +701,26 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u
     break;
   case NS_THEME_WINDOW:
   case NS_THEME_DIALOG:
     aGtkWidgetType = MOZ_GTK_WINDOW;
     break;
   case NS_THEME_GTK_INFO_BAR:
     aGtkWidgetType = MOZ_GTK_INFO_BAR;
     break;
+  case NS_THEME_GTK_WINDOW_DECORATION:
+  {
+    nsWindow* window = static_cast<nsWindow*>(aFrame->GetNearestWidget());
+    if (window && !window->UseSolidCSD()) {
+      aGtkWidgetType = MOZ_GTK_WINDOW_DECORATION;
+    } else {
+      aGtkWidgetType = MOZ_GTK_WINDOW_DECORATION_SOLID;
+    }
+    break;
+  }
   default:
     return false;
   }
 
   return true;
 }
 
 #if (MOZ_WIDGET_GTK == 2)
@@ -1857,16 +1868,17 @@ nsNativeThemeGTK::ThemeSupportsWidget(ns
   case NS_THEME_MENUSEPARATOR:
   case NS_THEME_CHECKMENUITEM:
   case NS_THEME_RADIOMENUITEM:
   case NS_THEME_SPLITTER:
   case NS_THEME_WINDOW:
   case NS_THEME_DIALOG:
 #if (MOZ_WIDGET_GTK == 3)
   case NS_THEME_GTK_INFO_BAR:
+  case NS_THEME_GTK_WINDOW_DECORATION:
 #endif
     return !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
 
   case NS_THEME_MENULIST_BUTTON:
     if (aFrame && aFrame->GetWritingMode().IsVertical()) {
       return false;
     }
     // "Native" dropdown buttons cause padding and margin problems, but only
@@ -1943,12 +1955,19 @@ nsNativeThemeGTK::GetWidgetTransparency(
   // Tooltips use gtk_paint_flat_box() on Gtk2
   // but are shaped on Gtk3
   case NS_THEME_TOOLTIP:
 #if (MOZ_WIDGET_GTK == 2)
     return eOpaque;
 #else
     return eTransparent;
 #endif
+  case NS_THEME_GTK_WINDOW_DECORATION:
+  {
+    nsWindow* window = static_cast<nsWindow*>(aFrame->GetNearestWidget());
+    if (window)
+      return window->UseSolidCSD() ? eOpaque : eTransparent;
+    return eOpaque;
+  }
   }
 
   return eUnknownTransparency;
 }
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -6839,16 +6839,39 @@ 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();
         }
     }
 }
 
+bool
+nsWindow::HasARGBVisual() const
+{
+  if (!mGdkWindow) {
+    NS_WARNING("nsWindow::HasARGBVisual called before realization!");
+    return false;
+  }
+  return gdk_window_get_visual(mGdkWindow)
+      == gdk_screen_get_rgba_visual(gdk_screen_get_default());
+}
+
+bool
+nsWindow::IsComposited() const
+{
+  return gdk_screen_is_composited(gdk_screen_get_default());
+}
+
+bool
+nsWindow::UseSolidCSD() const
+{
+  return !HasARGBVisual() || !IsComposited();
+}
+
 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 && mGdkWindow)
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -348,16 +348,23 @@ public:
     virtual nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId,
                                                 TouchPointerState aPointerState,
                                                 LayoutDeviceIntPoint aPoint,
                                                 double aPointerPressure,
                                                 uint32_t aPointerOrientation,
                                                 nsIObserver* aObserver) override;
 #endif
 
+    bool HasARGBVisual() const;
+    bool IsComposited() const;
+
+    // If true, 'solid' client side decorations (without an alpha channel)
+    // should be used if available.
+    bool UseSolidCSD() const;
+
     // HiDPI scale conversion
     gint GdkScaleFactor();
 
     // To GDK
     gint DevicePixelsToGdkCoordRoundUp(int pixels);
     gint DevicePixelsToGdkCoordRoundDown(int pixels);
     GdkPoint DevicePixelsToGdkPointRoundDown(LayoutDeviceIntPoint point);
     GdkRectangle DevicePixelsToGdkSizeRoundUp(LayoutDeviceIntSize pixelSize);