Bug 1421974 - use gtk_window_set_titlebar() to hide titlebar on WM where GDK_DECOR_BORDER does not work, r=jhorak
authorMartin Stransky <stransky@redhat.com>
Fri, 01 Dec 2017 15:22:48 +0100
changeset 395179 ed817bc6cc3bc2a11e9607afb3c5b52702ceb12a
parent 395178 66315180168026ea6a2ce2861131e6b326ec6182
child 395180 a83339d160f144645852d80a4fda04c48f7119bb
push id56634
push userstransky@redhat.com
push dateWed, 06 Dec 2017 08:39:04 +0000
treeherderautoland@ed817bc6cc3b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjhorak
bugs1421974, 1419456, 791081
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1421974 - use gtk_window_set_titlebar() to hide titlebar on WM where GDK_DECOR_BORDER does not work, r=jhorak This patch is based on Karl Tomlinson's (:karlt) demo from Bug 1419456. We use gtk_window_set_titlebar() to set invisible widget. The widget takes place of GtkHeaderBar which leads Gtk+ to render CSD shadows and handle window resizing, it does not render any titlebar. gtk_window_set_titlebar() works on unrealized windows only and mShell is already realized at time of nsWindow::SetDrawsInTitlebar() call so we need to update recent GtkWidget setup. In that case we create GdkWindow for mContainer (instead of mShell), create a temporary GtkWindow, reparent mContainer (which owns mGdkWindow) to it, unrealize mShell and set up the titlebar for mShell toplevel window. As a workaround for Gtk+ Bug 791081 we also allocate some valid size for mShell before it's newly realized with the updated titlebar. MozReview-Commit-ID: A3KwRoOzoko
widget/gtk/mozgtk/mozgtk.c
widget/gtk/nsWindow.cpp
--- a/widget/gtk/mozgtk/mozgtk.c
+++ b/widget/gtk/mozgtk/mozgtk.c
@@ -433,16 +433,18 @@ STUB(gtk_widget_get_accessible)
 STUB(gtk_widget_get_allocation)
 STUB(gtk_widget_get_default_direction)
 STUB(gtk_widget_get_display)
 STUB(gtk_widget_get_events)
 STUB(gtk_widget_get_has_window)
 STUB(gtk_widget_get_mapped)
 STUB(gtk_widget_get_parent)
 STUB(gtk_widget_get_parent_window)
+STUB(gtk_widget_get_preferred_width)
+STUB(gtk_widget_get_preferred_height)
 STUB(gtk_widget_get_realized)
 STUB(gtk_widget_get_screen)
 STUB(gtk_widget_get_settings)
 STUB(gtk_widget_get_style)
 STUB(gtk_widget_get_toplevel)
 STUB(gtk_widget_get_type)
 STUB(gtk_widget_get_visible)
 STUB(gtk_widget_get_visual)
@@ -474,16 +476,17 @@ STUB(gtk_widget_set_realized)
 STUB(gtk_widget_set_redraw_on_allocate)
 STUB(gtk_widget_set_sensitive)
 STUB(gtk_widget_set_window)
 STUB(gtk_widget_show)
 STUB(gtk_widget_show_all)
 STUB(gtk_widget_size_allocate)
 STUB(gtk_widget_style_get)
 STUB(gtk_widget_unparent)
+STUB(gtk_widget_unrealize)
 STUB(gtk_window_deiconify)
 STUB(gtk_window_fullscreen)
 STUB(gtk_window_get_group)
 STUB(gtk_window_get_transient_for)
 STUB(gtk_window_get_type)
 STUB(gtk_window_get_type_hint)
 STUB(gtk_window_get_window_type)
 STUB(gtk_window_group_add_window)
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -3785,22 +3785,23 @@ nsWindow::Create(nsIWidget* aParent,
         // it explicitly now.
         gtk_widget_realize(mShell);
 
         /* There are two cases here:
          *
          * 1) We're running on Gtk+ without client side decorations.
          *    Content is rendered to mShell window and we listen
          *    to the Gtk+ events on mShell
-         * 2) We're running on Gtk+ > 3.20 and client side decorations
+         * 2) We're running on Gtk+ and client side decorations
          *    are drawn by Gtk+ to mShell. Content is rendered to mContainer
          *    and we listen to the Gtk+ events on mContainer.
          */
         GtkStyleContext* style = gtk_widget_get_style_context(mShell);
-        drawToContainer = gtk_style_context_has_class(style, "csd");
+        drawToContainer = mIsCSDAvailable ||
+                          gtk_style_context_has_class(style, "csd");
 #endif
         eventWidget = (drawToContainer) ? container : mShell;
 
         gtk_widget_add_events(eventWidget, kEvents);
 
         // Prevent GtkWindow from painting a background to avoid flickering.
         gtk_widget_set_app_paintable(eventWidget, TRUE);
 
@@ -6645,16 +6646,73 @@ nsWindow::SetDrawsInTitlebar(bool aState
 {
   if (!mIsCSDAvailable || aState == mIsCSDEnabled)
       return;
 
   if (mShell) {
       if (GetCSDSupportLevel() == CSD_SUPPORT_FULL) {
           SetWindowDecoration(aState ? eBorderStyle_border : mBorderStyle);
       }
+      else {
+          /* Window manager does not support GDK_DECOR_BORDER,
+           * emulate it by CSD.
+           *
+           * gtk_window_set_titlebar() works on unrealized widgets only,
+           * we need to handle mShell carefully here.
+           * When CSD is enabled mGdkWindow is owned by mContainer which is good
+           * as we can't delete our mGdkWindow. To make mShell unrealized while
+           * mContainer is preserved we temporary reparent mContainer to an
+           * invisible GtkWindow.
+           */
+          NativeShow(false);
+
+          // Using GTK_WINDOW_POPUP rather than
+          // GTK_WINDOW_TOPLEVEL in the hope that POPUP results in less
+          // initialization and window manager interaction.
+          GtkWidget* tmpWindow = gtk_window_new(GTK_WINDOW_POPUP);
+          gtk_widget_realize(tmpWindow);
+
+          gtk_widget_reparent(GTK_WIDGET(mContainer), tmpWindow);
+          gtk_widget_unrealize(GTK_WIDGET(mShell));
+
+          // Available as of GTK 3.10+
+          static auto sGtkWindowSetTitlebar = (void (*)(GtkWindow*, GtkWidget*))
+              dlsym(RTLD_DEFAULT, "gtk_window_set_titlebar");
+          MOZ_ASSERT(sGtkWindowSetTitlebar,
+              "Missing gtk_window_set_titlebar(), old Gtk+ library?");
+
+          if (aState) {
+              // Add a hidden titlebar widget to trigger CSD, but disable the default
+              // titlebar.  GtkFixed is a somewhat random choice for a simple unused
+              // widget. gtk_window_set_titlebar() takes ownership of the titlebar
+              // widget.
+              sGtkWindowSetTitlebar(GTK_WINDOW(mShell), gtk_fixed_new());
+          } else {
+              sGtkWindowSetTitlebar(GTK_WINDOW(mShell), nullptr);
+          }
+
+          /* A workaround for https://bugzilla.gnome.org/show_bug.cgi?id=791081
+           * gtk_widget_realize() throws:
+           * "In pixman_region32_init_rect: Invalid rectangle passed"
+           * when mShell has default 1x1 size.
+           */
+          GtkAllocation allocation = {0, 0, 0, 0};
+          gtk_widget_get_preferred_width(GTK_WIDGET(mShell), nullptr,
+                                         &allocation.width);
+          gtk_widget_get_preferred_height(GTK_WIDGET(mShell), nullptr,
+                                          &allocation.height);
+          gtk_widget_size_allocate(GTK_WIDGET(mShell), &allocation);
+
+          gtk_widget_realize(GTK_WIDGET(mShell));
+          gtk_widget_reparent(GTK_WIDGET(mContainer), GTK_WIDGET(mShell));
+          mNeedsShow = true;
+          NativeResize();
+
+          gtk_widget_destroy(tmpWindow);
+      }
   }
 
   mIsCSDEnabled = aState;
 }
 
 gint
 nsWindow::GdkScaleFactor()
 {