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
--- 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()
{