Bug 1605120 [Wayland] Calculate and set opaque region for all wayland surfaces, r=heftig
authorMartin Stransky <stransky@redhat.com>
Thu, 02 Jan 2020 13:35:26 +0000
changeset 570759 c96194fe9f07a39d4d681b392d0bb7bb778ecf41
parent 570758 5bd709eb6bd1b084779922c508df2ec9ad98eebc
child 570760 2955e3b6962e2386cce676514f01a9af9c954356
push id12493
push userffxbld-merge
push dateMon, 06 Jan 2020 15:38:57 +0000
treeherdermozilla-beta@63ae456b848d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheftig
bugs1605120
milestone73.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 1605120 [Wayland] Calculate and set opaque region for all wayland surfaces, r=heftig Calculate and set correct opaque regions for all wl_surfaces, which are: - mShell - toplevel window with CSD decorations, opaque mask needs to be shifted by titlebar/CSD size and subtracted transparent corners. - mContainer - child window with subtracted transparent corners. Differential Revision: https://phabricator.services.mozilla.com/D57797
widget/gtk/mozcontainer.cpp
widget/gtk/mozcontainer.h
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
--- a/widget/gtk/mozcontainer.cpp
+++ b/widget/gtk/mozcontainer.cpp
@@ -203,16 +203,17 @@ void moz_container_init(MozContainer* co
   gtk_widget_set_can_focus(GTK_WIDGET(container), TRUE);
   gtk_container_set_resize_mode(GTK_CONTAINER(container), GTK_RESIZE_IMMEDIATE);
   gtk_widget_set_redraw_on_allocate(GTK_WIDGET(container), FALSE);
 
 #if defined(MOZ_WAYLAND)
   container->surface = nullptr;
   container->subsurface = nullptr;
   container->eglwindow = nullptr;
+  container->opaque_region = nullptr;
   container->frame_callback_handler = nullptr;
   container->frame_callback_handler_surface_id = -1;
   // We can draw to x11 window any time.
   container->ready_to_draw = gfxPlatformGtk::GetPlatform()->IsX11Display();
   container->surface_needs_clear = true;
   container->subsurface_dx = 0;
   container->subsurface_dy = 0;
   container->surface_position_needs_update = 0;
@@ -324,16 +325,21 @@ static void moz_container_unmap_wayland(
   g_clear_pointer(&container->subsurface, wl_subsurface_destroy);
   g_clear_pointer(&container->surface, wl_surface_destroy);
   g_clear_pointer(&container->frame_callback_handler, wl_callback_destroy);
   container->frame_callback_handler_surface_id = -1;
 
   container->surface_needs_clear = true;
   container->ready_to_draw = false;
 
+  if (container->opaque_region) {
+    wl_region_destroy(container->opaque_region);
+    container->opaque_region = nullptr;
+  }
+
   LOGWAYLAND(("%s [%p]\n", __FUNCTION__, (void*)container));
 }
 #endif
 
 void moz_container_map(GtkWidget* widget) {
   MozContainer* container;
   GList* tmp_list;
   GtkWidget* tmp_child;
@@ -604,16 +610,18 @@ struct wl_surface* moz_container_get_wl_
                 (void*)container->surface));
   }
 
   if (container->surface_position_needs_update) {
     moz_container_move(container, container->subsurface_dx,
                        container->subsurface_dy);
   }
 
+  wl_surface_set_opaque_region(container->surface, container->opaque_region);
+
   return container->surface;
 }
 
 struct wl_egl_window* moz_container_get_wl_egl_window(MozContainer* container,
                                                       int scale) {
   LOGWAYLAND(("%s [%p] eglwindow %p\n", __FUNCTION__, (void*)container,
               (void*)container->eglwindow));
 
@@ -641,13 +649,21 @@ gboolean moz_container_has_wl_egl_window
   return container->eglwindow ? true : false;
 }
 
 gboolean moz_container_surface_needs_clear(MozContainer* container) {
   int ret = container->surface_needs_clear;
   container->surface_needs_clear = false;
   return ret;
 }
+
+void moz_container_set_opaque_region(MozContainer* container,
+                                     wl_region* opaque_region) {
+  if (container->opaque_region) {
+    wl_region_destroy(container->opaque_region);
+  }
+  container->opaque_region = opaque_region;
+}
 #endif
 
 void moz_container_force_default_visual(MozContainer* container) {
   container->force_default_visual = true;
 }
--- a/widget/gtk/mozcontainer.h
+++ b/widget/gtk/mozcontainer.h
@@ -73,16 +73,17 @@ struct _MozContainer {
   GtkContainer container;
   GList* children;
 
 #ifdef MOZ_WAYLAND
   struct wl_surface* surface;
   struct wl_subsurface* subsurface;
   int subsurface_dx, subsurface_dy;
   struct wl_egl_window* eglwindow;
+  struct wl_region* opaque_region;
   struct wl_callback* frame_callback_handler;
   int frame_callback_handler_surface_id;
   gboolean surface_position_needs_update;
   gboolean surface_needs_clear;
   gboolean ready_to_draw;
   std::vector<std::function<void(void)>> initial_draw_cbs;
 #endif
   gboolean force_default_visual;
@@ -109,11 +110,13 @@ void moz_container_move_resize(MozContai
                                int width, int height);
 void moz_container_egl_window_set_size(MozContainer* container, int width,
                                        int height);
 void moz_container_scale_changed(MozContainer* container,
                                  GtkAllocation* aAllocation);
 void moz_container_add_initial_draw_callback(
     MozContainer* container, const std::function<void(void)>& initial_draw_cb);
 wl_surface* moz_gtk_widget_get_wl_surface(GtkWidget* aWidget);
+void moz_container_set_opaque_region(MozContainer* container,
+                                     wl_region* opaque_region);
 #endif
 
 #endif /* __MOZ_CONTAINER_H__ */
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -4789,132 +4789,165 @@ void nsWindow::SetWindowMouseTransparent
 // with -moz-window-dragging: drag.
 void nsWindow::UpdateWindowDraggingRegion(
     const LayoutDeviceIntRegion& aRegion) {
   if (mDraggableRegion != aRegion) {
     mDraggableRegion = aRegion;
   }
 }
 
+// See subtract_corners_from_region() at gtk/gtkwindow.c
+// We need to subtract corners from toplevel window opaque region
+// to draw transparent corners of default Gtk titlebar.
+// Both implementations (cairo_region_t and wl_region) needs to be synced.
+static void SubtractTitlebarCorners(cairo_region_t* aRegion, int aX, int aY,
+                                    int aWindowWidth) {
+  cairo_rectangle_int_t rect = {aX, aY, TITLEBAR_SHAPE_MASK_HEIGHT,
+                                TITLEBAR_SHAPE_MASK_HEIGHT};
+  cairo_region_subtract_rectangle(aRegion, &rect);
+  rect = {
+      aX + aWindowWidth - TITLEBAR_SHAPE_MASK_HEIGHT,
+      aY,
+      TITLEBAR_SHAPE_MASK_HEIGHT,
+      TITLEBAR_SHAPE_MASK_HEIGHT,
+  };
+  cairo_region_subtract_rectangle(aRegion, &rect);
+}
+
 #ifdef MOZ_WAYLAND
-void nsWindow::UpdateOpaqueRegionWayland(cairo_region_t* aRegion) {
+static void SubtractTitlebarCorners(wl_region* aRegion, int aX, int aY,
+                                    int aWindowWidth) {
+  wl_region_subtract(aRegion, aX, aY, TITLEBAR_SHAPE_MASK_HEIGHT,
+                     TITLEBAR_SHAPE_MASK_HEIGHT);
+  wl_region_subtract(aRegion, aX + aWindowWidth - TITLEBAR_SHAPE_MASK_HEIGHT,
+                     aY, TITLEBAR_SHAPE_MASK_HEIGHT,
+                     TITLEBAR_SHAPE_MASK_HEIGHT);
+}
+
+void nsWindow::UpdateTopLevelOpaqueRegionWayland(bool aSubtractCorners) {
   wl_surface* surface = moz_gtk_widget_get_wl_surface(GTK_WIDGET(mShell));
   if (!surface) {
     return;
   }
 
   GdkDisplay* display = gtk_widget_get_display(GTK_WIDGET(mShell));
   nsWaylandDisplay* waylandDisplay = WaylandDisplayGet(display);
-
-  wl_region* wl_region = nullptr;
-  if (aRegion) {
-    struct wl_compositor* compositor = waylandDisplay->GetCompositor();
-    wl_region = wl_compositor_create_region(compositor);
-    int n_rects = cairo_region_num_rectangles(aRegion);
-    for (int i = 0; i < n_rects; i++) {
-      cairo_rectangle_int_t rect;
-      cairo_region_get_rectangle(aRegion, i, &rect);
-      wl_region_add(wl_region, rect.x, rect.y, rect.width, rect.height);
-    }
-  }
-
-  wl_surface_set_opaque_region(surface, wl_region);
-  if (wl_region) {
-    wl_region_destroy(wl_region);
-  }
+  struct wl_compositor* compositor = waylandDisplay->GetCompositor();
+
+  // Set opaque region to mShell. It's moved to mClientOffset.x/mClientOffset.y
+  // from origin as we need transparent shadows around a window.
+  wl_region* region = wl_compositor_create_region(compositor);
+  int x = DevicePixelsToGdkCoordRoundDown(mClientOffset.x);
+  int y = DevicePixelsToGdkCoordRoundDown(mClientOffset.y);
+  int width = DevicePixelsToGdkCoordRoundDown(mBounds.width);
+  int height = DevicePixelsToGdkCoordRoundDown(mBounds.height);
+  wl_region_add(region, x, y, width, height);
+  if (aSubtractCorners) {
+    SubtractTitlebarCorners(region, x, y, width);
+  }
+  wl_surface_set_opaque_region(surface, region);
+  wl_region_destroy(region);
+
+  // Set region to mozcontainer which does not have any offset
+  region = wl_compositor_create_region(compositor);
+  wl_region_add(region, 0, 0, width, height);
+  if (aSubtractCorners) {
+    SubtractTitlebarCorners(region, 0, 0, width);
+  }
+  moz_container_set_opaque_region(mContainer, region);
 }
 #endif
 
-void nsWindow::UpdateOpaqueRegionGtk(cairo_region_t* aRegion) {
+static void GdkWindowSetOpaqueRegion(GdkWindow* aGdkWindow,
+                                     cairo_region_t* aRegion) {
   // Available as of GTK 3.10+
   static auto sGdkWindowSetOpaqueRegion =
       (void (*)(GdkWindow*, cairo_region_t*))dlsym(
           RTLD_DEFAULT, "gdk_window_set_opaque_region");
 
   if (MOZ_UNLIKELY(!sGdkWindowSetOpaqueRegion)) {
     LOG(("    gdk_window_set_opaque_region is not available!\n"));
     return;
   }
 
+  (*sGdkWindowSetOpaqueRegion)(aGdkWindow, aRegion);
+}
+
+void nsWindow::UpdateTopLevelOpaqueRegionGtk(bool aSubtractCorners) {
+  cairo_region_t* region = cairo_region_create();
+  int x = DevicePixelsToGdkCoordRoundDown(mClientOffset.x);
+  int y = DevicePixelsToGdkCoordRoundDown(mClientOffset.y);
+  int width = DevicePixelsToGdkCoordRoundDown(mBounds.width);
+  int height = DevicePixelsToGdkCoordRoundDown(mBounds.height);
+
+  cairo_rectangle_int_t rect = {x, y, width, height};
+  cairo_region_union_rectangle(region, &rect);
+
+  if (aSubtractCorners) {
+    SubtractTitlebarCorners(region, x, y, width);
+  }
+
   GdkWindow* window =
       (mDrawToContainer) ? gtk_widget_get_window(mShell) : mGdkWindow;
-  if (gdk_window_get_window_type(window) == GDK_WINDOW_TOPLEVEL) {
-    (*sGdkWindowSetOpaqueRegion)(window, aRegion);
+  MOZ_ASSERT(gdk_window_get_window_type(window) == GDK_WINDOW_TOPLEVEL);
+  GdkWindowSetOpaqueRegion(window, region);
+
+  cairo_region_destroy(region);
+}
+
+void nsWindow::UpdatePopupOpaqueRegion(
+    const LayoutDeviceIntRegion& aOpaqueRegion) {
+  cairo_region_t* region = nullptr;
+
+  if (!aOpaqueRegion.IsEmpty()) {
+    region = cairo_region_create();
+    for (auto iter = aOpaqueRegion.RectIter(); !iter.Done(); iter.Next()) {
+      const LayoutDeviceIntRect& r = iter.Get();
+      cairo_rectangle_int_t rect = {r.x, r.y, r.width, r.height};
+      cairo_region_union_rectangle(region, &rect);
+    }
+  }
+
+  GdkWindow* window =
+      (mDrawToContainer) ? gtk_widget_get_window(mShell) : mGdkWindow;
+  GdkWindowSetOpaqueRegion(window, region);
+
+  if (region) {
+    cairo_region_destroy(region);
   }
 }
 
 void nsWindow::UpdateOpaqueRegion(const LayoutDeviceIntRegion& aOpaqueRegion) {
-  // Also don't set shape mask if we use transparency bitmap.
+  // Don't set shape mask if we use transparency bitmap.
   if (mTransparencyBitmapForTitlebar) {
     return;
   }
 
-  cairo_region_t* region = nullptr;
-
-  // We don't tweak opaque regions for non-toplevel windows (popup, panels etc.)
-  // as they can be transparent by gecko.
   if (mWindowType != eWindowType_toplevel) {
-    if (!aOpaqueRegion.IsEmpty()) {
-      region = cairo_region_create();
-      for (auto iter = aOpaqueRegion.RectIter(); !iter.Done(); iter.Next()) {
-        const LayoutDeviceIntRect& r = iter.Get();
-        cairo_rectangle_int_t rect = {r.x, r.y, r.width, r.height};
-        cairo_region_union_rectangle(region, &rect);
-      }
-    }
+    // We don't tweak opaque regions for non-toplevel windows
+    // (popup, panels etc.) as they can be transparent by gecko.
+    UpdatePopupOpaqueRegion(aOpaqueRegion);
   } else {
     // Gecko does not use transparent toplevel windows (see Bug 1469716),
     // however we need to make it transparent to draw round corners of
     // Gtk titlebar.
-    region = cairo_region_create();
-
-    GtkBorder decorationSize = {0, 0, 0, 0};
-    if (mCSDSupportLevel == CSD_SUPPORT_CLIENT &&
-        mSizeState == nsSizeMode_Normal) {
-      decorationSize = GetCSDDecorationSize(
-          gtk_window_get_window_type(GTK_WINDOW(mShell)) == GTK_WINDOW_POPUP);
-    }
-
-    int width = DevicePixelsToGdkCoordRoundDown(mBounds.width);
-    int height = DevicePixelsToGdkCoordRoundDown(mBounds.height);
-
-    cairo_rectangle_int_t rect = {decorationSize.left, decorationSize.top,
-                                  width, height};
-    cairo_region_union_rectangle(region, &rect);
 
     // Subtract transparent corners which are used by
     // various Gtk themes for toplevel windows when titlebar
     // is rendered by gecko.
-    if (mDrawInTitlebar && !mIsPIPWindow && mSizeState == nsSizeMode_Normal &&
-        !mIsTiled) {
-      cairo_rectangle_int_t rect = {decorationSize.left, decorationSize.top,
-                                    TITLEBAR_SHAPE_MASK_HEIGHT,
-                                    TITLEBAR_SHAPE_MASK_HEIGHT};
-      cairo_region_subtract_rectangle(region, &rect);
-      rect = {
-          decorationSize.left + width - TITLEBAR_SHAPE_MASK_HEIGHT,
-          decorationSize.top,
-          TITLEBAR_SHAPE_MASK_HEIGHT,
-          TITLEBAR_SHAPE_MASK_HEIGHT,
-      };
-      cairo_region_subtract_rectangle(region, &rect);
+    bool drawTilebarCorners = (mDrawInTitlebar && !mIsPIPWindow) &&
+                              (mSizeState == nsSizeMode_Normal && !mIsTiled);
+    if (mIsX11Display) {
+      UpdateTopLevelOpaqueRegionGtk(drawTilebarCorners);
     }
-  }
-
-  if (mIsX11Display) {
-    UpdateOpaqueRegionGtk(region);
-  }
 #ifdef MOZ_WAYLAND
-  else {
-    UpdateOpaqueRegionWayland(region);
-  }
+    else {
+      UpdateTopLevelOpaqueRegionWayland(drawTilebarCorners);
+    }
 #endif
-
-  if (region) {
-    cairo_region_destroy(region);
   }
 }
 
 nsresult nsWindow::ConfigureChildren(
     const nsTArray<Configuration>& aConfigurations) {
   // If this is a remotely updated widget we receive clipping, position, and
   // size information from a source other than our owner. Don't let our parent
   // update this information.
@@ -7678,16 +7711,18 @@ GtkTextDirection nsWindow::GetTextDirect
   return wm.IsPhysicalLTR() ? GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
 }
 
 void nsWindow::LockAspectRatio(bool aShouldLock) {
   if (aShouldLock) {
     float width = (float)DevicePixelsToGdkCoordRoundDown(mBounds.width);
     float height = (float)DevicePixelsToGdkCoordRoundDown(mBounds.height);
 
+    // TODO - compare mShell GdkWindow size and mContainer GdkWindow size
+    // instead of GetCSDDecorationSize() call.
     if (mCSDSupportLevel == CSD_SUPPORT_CLIENT) {
       GtkBorder decorationSize = GetCSDDecorationSize(
           gtk_window_get_window_type(GTK_WINDOW(mShell)) == GTK_WINDOW_POPUP);
       width += decorationSize.left + decorationSize.right;
       height += decorationSize.top + decorationSize.bottom;
     }
 
     mAspectRatio = width / height;
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -480,19 +480,20 @@ class nsWindow final : public nsBaseWidg
   void CheckForRollupDuringGrab() { CheckForRollup(0, 0, false, true); }
 
   bool GetDragInfo(mozilla::WidgetMouseEvent* aMouseEvent, GdkWindow** aWindow,
                    gint* aButton, gint* aRootX, gint* aRootY);
   void ClearCachedResources();
   nsIWidgetListener* GetListener();
 
 #ifdef MOZ_WAYLAND
-  void UpdateOpaqueRegionWayland(cairo_region_t* aRegion);
+  void UpdateTopLevelOpaqueRegionWayland(bool aSubtractCorners);
 #endif
-  void UpdateOpaqueRegionGtk(cairo_region_t* aRegion);
+  void UpdateTopLevelOpaqueRegionGtk(bool aSubtractCorners);
+  void UpdatePopupOpaqueRegion(const LayoutDeviceIntRegion& aOpaqueRegion);
 
   nsWindow* GetTransientForWindowIfPopup();
   bool IsHandlingTouchSequence(GdkEventSequence* aSequence);
 
   void ResizeInt(int aX, int aY, int aWidth, int aHeight, bool aMove,
                  bool aRepaint);
   void NativeMoveResizeWaylandPopup(GdkPoint* aPosition, GdkRectangle* aSize);