File wayland-protocol-toplevel-drag.patch of Package nodejs-electron-cdm

From 8f45d25c2f8f4849b66136b14734b79e2b4fe9c1 Mon Sep 17 00:00:00 2001
From: Robert Mader <robert.mader@collabora.com>
Date: Thu, 12 Sep 2024 19:11:15 +0000
Subject: [PATCH] ozone/wayland: Add support for xdg-toplevel-drag

Rebased and slighty updated version of https://chromium-review.googlesource.com/c/chromium/src/+/4400967

This implements a new protocol landed in wayland-protocols 1.34 that can
be found at
https://gitlab.freedesktop.org/wayland/wayland-protocols/-/blob/main/staging/xdg-toplevel-drag/xdg-toplevel-drag-v1.xml

It is intended to be a drop-in replacement for extended-drag-unstable-v1
with some features removed that were never implemented in Exo/Chromium.

If both the `extended-drag` and `xdg-toplevel-drag` protocols are
supported by the compositor - i.e. in Exo - the former is kept being
used by default. The later can be enabled via the
`WaylandXdgToplevelDrag` feature.

Original author: David Redondo <kde@david-redondo.de>

Bug: b:315000518
Bug: b:324170129
Change-Id: If251ac9944ec4395f2d8630b7c4ac74ca56a662d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5088752
Reviewed-by: Thomas Anderson <thomasanderson@chromium.org>
Reviewed-by: Antonio Gomes <tonikitoo@igalia.com>
Commit-Queue: Nick Yamane <nickdiego@igalia.com>
Reviewed-by: Nick Yamane <nickdiego@igalia.com>
Cr-Commit-Position: refs/heads/main@{#1354718}
---
 AUTHORS                                       |  1 +
 ui/ozone/common/features.cc                   | 16 ++++
 ui/ozone/common/features.h                    |  1 +
 ui/ozone/platform/wayland/BUILD.gn            |  1 +
 .../platform/wayland/common/wayland_object.cc |  3 +
 .../platform/wayland/common/wayland_object.h  |  2 +
 .../wayland/host/wayland_connection.cc        | 10 +++
 .../wayland/host/wayland_connection.h         |  4 +
 .../wayland/host/wayland_toplevel_window.cc   | 13 +--
 .../host/wayland_window_drag_controller.cc    | 85 ++++++++++++++++---
 .../host/wayland_window_drag_controller.h     |  4 +
 .../wayland/host/xdg_toplevel_wrapper_impl.h  |  1 +
 12 files changed, 121 insertions(+), 20 deletions(-)

diff --git a/AUTHORS b/AUTHORS
index 52d075125b8126..bd9327b5b477f2 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -337,6 +337,7 @@ David Leen <davileen@amazon.com>
 David Manouchehri <david@davidmanouchehri.com>
 David McAllister <mcdavid@amazon.com>
 David Michael Barr <david.barr@samsung.com>
+David Redondo <kde@david-redondo.de>
 David Sanders <dsanders11@ucsbalum.com>
 David Spellman <dspell@amazon.com>
 David Valachovic <adenflorian@gmail.com>
diff --git a/ui/ozone/common/features.cc b/ui/ozone/common/features.cc
index bd13d0e17f39a3..74ffde0cf5b85c 100644
--- a/ui/ozone/common/features.cc
+++ b/ui/ozone/common/features.cc
@@ -37,6 +37,18 @@ BASE_FEATURE(kWaylandFractionalScaleV1,
 #endif
 );
 
+// Controls whether support for the xdg-toplevel-drag protocol should be
+// enabled. On Lacros it will then be used even if the Exo-only extended-drag
+// protocol is supported.
+BASE_FEATURE(kWaylandXdgToplevelDrag,
+             "WaylandXdgToplevelDrag",
+#if BUILDFLAG(IS_LINUX)
+             base::FEATURE_ENABLED_BY_DEFAULT
+#else
+             base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+);
+
 // This debug/dev flag pretty-prints DRM modeset configuration logs for ease
 // of reading. For more information, see: http://b/233006802
 BASE_FEATURE(kPrettyPrintDrmModesetConfigLogs,
@@ -62,6 +74,10 @@ bool IsWaylandFractionalScaleV1Enabled() {
   return base::FeatureList::IsEnabled(kWaylandFractionalScaleV1);
 }
 
+bool IsWaylandXdgToplevelDragEnabled() {
+  return base::FeatureList::IsEnabled(kWaylandXdgToplevelDrag);
+}
+
 bool IsPrettyPrintDrmModesetConfigLogsEnabled() {
   return base::FeatureList::IsEnabled(kPrettyPrintDrmModesetConfigLogs);
 }
diff --git a/ui/ozone/common/features.h b/ui/ozone/common/features.h
index 5d4c3c42af9ca7..1f88055f8975bc 100644
--- a/ui/ozone/common/features.h
+++ b/ui/ozone/common/features.h
@@ -18,6 +18,7 @@ BASE_DECLARE_FEATURE(kUseDynamicCursorSize);
 bool IsWaylandSurfaceSubmissionInPixelCoordinatesEnabled();
 bool IsWaylandOverlayDelegationEnabled();
 bool IsWaylandFractionalScaleV1Enabled();
+bool IsWaylandXdgToplevelDragEnabled();
 bool IsPrettyPrintDrmModesetConfigLogsEnabled();
 bool IsUseDynamicCursorSizeEnabled();
 
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn
index 6d3ec34101498d..b87a7b5d2903f0 100644
--- a/ui/ozone/platform/wayland/BUILD.gn
+++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -294,6 +294,7 @@ source_set("wayland") {
     "//third_party/wayland-protocols:xdg_foreign",
     "//third_party/wayland-protocols:xdg_output_protocol",
     "//third_party/wayland-protocols:xdg_shell_protocol",
+    "//third_party/wayland-protocols:xdg_toplevel_drag_protocol",
     "//third_party/wayland-protocols:xdg_toplevel_icon_protocol",
     "//ui/base",
     "//ui/base:buildflags",
diff --git a/ui/ozone/platform/wayland/common/wayland_object.cc b/ui/ozone/platform/wayland/common/wayland_object.cc
index e30367a19d7774..1534a735855dd3 100644
--- a/ui/ozone/platform/wayland/common/wayland_object.cc
+++ b/ui/ozone/platform/wayland/common/wayland_object.cc
@@ -44,6 +44,7 @@
 #include <xdg-foreign-unstable-v2-client-protocol.h>
 #include <xdg-output-unstable-v1-client-protocol.h>
 #include <xdg-shell-client-protocol.h>
+#include <xdg-toplevel-drag-v1-client-protocol.h>
 #include <xdg-toplevel-icon-v1-client-protocol.h>
 
 #include "base/logging.h"
@@ -257,6 +258,8 @@ IMPLEMENT_WAYLAND_OBJECT_TRAITS(xdg_popup)
 IMPLEMENT_WAYLAND_OBJECT_TRAITS(xdg_positioner)
 IMPLEMENT_WAYLAND_OBJECT_TRAITS(xdg_surface)
 IMPLEMENT_WAYLAND_OBJECT_TRAITS(xdg_toplevel)
+IMPLEMENT_WAYLAND_OBJECT_TRAITS(xdg_toplevel_drag_v1)
+IMPLEMENT_WAYLAND_OBJECT_TRAITS(xdg_toplevel_drag_manager_v1)
 IMPLEMENT_WAYLAND_OBJECT_TRAITS(xdg_toplevel_icon_manager_v1)
 IMPLEMENT_WAYLAND_OBJECT_TRAITS(xdg_toplevel_icon_v1)
 IMPLEMENT_WAYLAND_OBJECT_TRAITS(xdg_wm_base)
diff --git a/ui/ozone/platform/wayland/common/wayland_object.h b/ui/ozone/platform/wayland/common/wayland_object.h
index 38213eff64de40..b279424d14784d 100644
--- a/ui/ozone/platform/wayland/common/wayland_object.h
+++ b/ui/ozone/platform/wayland/common/wayland_object.h
@@ -156,6 +156,8 @@ DECLARE_WAYLAND_OBJECT_TRAITS(xdg_popup)
 DECLARE_WAYLAND_OBJECT_TRAITS(xdg_positioner)
 DECLARE_WAYLAND_OBJECT_TRAITS(xdg_surface)
 DECLARE_WAYLAND_OBJECT_TRAITS(xdg_toplevel)
+DECLARE_WAYLAND_OBJECT_TRAITS(xdg_toplevel_drag_v1)
+DECLARE_WAYLAND_OBJECT_TRAITS(xdg_toplevel_drag_manager_v1)
 DECLARE_WAYLAND_OBJECT_TRAITS(xdg_toplevel_icon_manager_v1)
 DECLARE_WAYLAND_OBJECT_TRAITS(xdg_toplevel_icon_v1)
 DECLARE_WAYLAND_OBJECT_TRAITS(xdg_wm_base)
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.cc b/ui/ozone/platform/wayland/host/wayland_connection.cc
index a1584eb682adf3..e595f500758492 100644
--- a/ui/ozone/platform/wayland/host/wayland_connection.cc
+++ b/ui/ozone/platform/wayland/host/wayland_connection.cc
@@ -91,6 +91,7 @@ constexpr uint32_t kMaxExplicitSyncVersion = 2;
 constexpr uint32_t kMaxAlphaCompositingVersion = 1;
 constexpr uint32_t kMaxXdgDecorationVersion = 1;
 constexpr uint32_t kMaxExtendedDragVersion = 1;
+constexpr uint32_t kMaxXdgToplevelDragVersion = 1;
 constexpr uint32_t kMaxXdgOutputManagerVersion = 3;
 constexpr uint32_t kMaxKeyboardShortcutsInhibitManagerVersion = 1;
 constexpr uint32_t kMaxStylusVersion = 2;
@@ -745,6 +746,15 @@ void WaylandConnection::HandleGlobal(wl_registry* registry,
       LOG(ERROR) << "Failed to bind to zcr_extended_drag_v1 global";
       return;
     }
+  } else if (!xdg_toplevel_drag_manager_v1_ &&
+             strcmp(interface, "xdg_toplevel_drag_manager_v1") == 0 &&
+             IsWaylandXdgToplevelDragEnabled()) {
+    xdg_toplevel_drag_manager_v1_ = wl::Bind<::xdg_toplevel_drag_manager_v1>(
+        registry, name, std::min(version, kMaxXdgToplevelDragVersion));
+    if (!xdg_toplevel_drag_manager_v1_) {
+      LOG(ERROR) << "Failed to bind to xdg_toplevel_drag_manager_v1 global";
+      return;
+    }
   } else if (!xdg_output_manager_ &&
              strcmp(interface, "zxdg_output_manager_v1") == 0) {
     // Responsibilities of zxdg_output_manager_v1 have been subsumed into the
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.h b/ui/ozone/platform/wayland/host/wayland_connection.h
index b4633ad03c4508..79680db7189852 100644
--- a/ui/ozone/platform/wayland/host/wayland_connection.h
+++ b/ui/ozone/platform/wayland/host/wayland_connection.h
@@ -160,6 +160,9 @@ class WaylandConnection {
   zcr_extended_drag_v1* extended_drag_v1() const {
     return extended_drag_v1_.get();
   }
+  xdg_toplevel_drag_manager_v1* xdg_toplevel_drag_manager_v1() const {
+    return xdg_toplevel_drag_manager_v1_.get();
+  }
 
   zxdg_output_manager_v1* xdg_output_manager_v1() const {
     return xdg_output_manager_.get();
@@ -500,6 +503,7 @@ class WaylandConnection {
       linux_explicit_synchronization_;
   wl::Object<zxdg_decoration_manager_v1> xdg_decoration_manager_;
   wl::Object<zcr_extended_drag_v1> extended_drag_v1_;
+  wl::Object<::xdg_toplevel_drag_manager_v1> xdg_toplevel_drag_manager_v1_;
   wl::Object<zxdg_output_manager_v1> xdg_output_manager_;
   wl::Object<wp_fractional_scale_manager_v1> fractional_scale_manager_v1_;
   wl::Object<xdg_toplevel_icon_manager_v1> toplevel_icon_manager_v1_;
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
index 204e65c90ec374..5af9efc0d8acbf 100644
--- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
+++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
@@ -858,7 +858,8 @@ void WaylandToplevelWindow::HideTooltip() {
 bool WaylandToplevelWindow::IsClientControlledWindowMovementSupported() const {
   auto* window_drag_controller = connection()->window_drag_controller();
   DCHECK(window_drag_controller);
-  return window_drag_controller->IsExtendedDragAvailable();
+  return window_drag_controller->IsExtendedDragAvailable() ||
+         window_drag_controller->IsXdgToplevelDragAvailable();
 }
 
 bool WaylandToplevelWindow::ShouldReleaseCaptureForDrag(
@@ -882,11 +883,11 @@ void WaylandToplevelWindow::StartWindowDraggingSessionIfNeeded(
     ui::mojom::DragEventSource event_source,
     bool allow_system_drag) {
   DCHECK(connection()->window_drag_controller());
-  // If extended drag is not available and |allow_system_drag| is set, this is
-  // no-op and WaylandDataDragController is assumed to be used instead. i.e:
-  // Fallback to a simpler window drag UX based on regular system drag-and-drop.
-  if (!connection()->window_drag_controller()->IsExtendedDragAvailable() &&
-      allow_system_drag) {
+  // If extended-drag and xdg-toplevel-drag are not available and
+  // |allow_system_drag| is set, this is no-op and WaylandDataDragController is
+  // assumed to be used instead. i.e: Fallback to a simpler window drag UX based
+  // on regular system drag-and-drop.
+  if (!IsClientControlledWindowMovementSupported() && allow_system_drag) {
     return;
   }
   connection()->window_drag_controller()->StartDragSession(this, event_source);
diff --git a/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc b/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc
index 2e95a32417a4c1..6184684e308154 100644
--- a/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc
+++ b/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc
@@ -6,6 +6,7 @@
 
 #include <extended-drag-unstable-v1-client-protocol.h>
 #include <wayland-client-protocol.h>
+#include <xdg-toplevel-drag-v1-client-protocol.h>
 
 #include <cstdint>
 #include <memory>
@@ -36,8 +37,10 @@
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/vector2d.h"
+#include "ui/ozone/common/features.h"
 #include "ui/ozone/platform/wayland/common/wayland_object.h"
 #include "ui/ozone/platform/wayland/host/dump_util.h"
+#include "ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h"
 #include "ui/ozone/platform/wayland/host/wayland_connection.h"
 #include "ui/ozone/platform/wayland/host/wayland_cursor_position.h"
 #include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h"
@@ -51,6 +54,7 @@
 #include "ui/ozone/platform/wayland/host/wayland_surface.h"
 #include "ui/ozone/platform/wayland/host/wayland_window.h"
 #include "ui/ozone/platform/wayland/host/wayland_window_manager.h"
+#include "ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h"
 #include "ui/platform_window/platform_window_init_properties.h"
 
 namespace ui {
@@ -101,6 +105,39 @@ class WaylandWindowDragController::ExtendedDragSource {
   const raw_ref<WaylandConnection> connection_;
 };
 
+class WaylandWindowDragController::XdgToplevelDrag {
+ public:
+  XdgToplevelDrag(WaylandConnection& connection, wl_data_source* source)
+      : connection_(connection) {
+    DCHECK(connection.xdg_toplevel_drag_manager_v1());
+    drag_.reset(xdg_toplevel_drag_manager_v1_get_xdg_toplevel_drag(
+        connection.xdg_toplevel_drag_manager_v1(), source));
+    DCHECK(drag_);
+  }
+
+  void SetDraggedWindow(WaylandToplevelWindow* window,
+                        const gfx::Vector2d& offset) {
+    if (!window) {
+      // Detaching happens implicitly via wl_data_source.dnd_drop_performed (see
+      // OnDataSourceDropPerformed()) or when the toplevel gets unmapped.
+      return;
+    }
+    DCHECK(window->shell_toplevel() &&
+           window->shell_toplevel()->AsXDGToplevelWrapper());
+
+    auto* toplevel =
+        window->shell_toplevel()->AsXDGToplevelWrapper()->xdg_toplevel_.get();
+    DCHECK(toplevel);
+
+    xdg_toplevel_drag_v1_attach(drag_.get(), toplevel, offset.x(), offset.y());
+    connection_->Flush();
+  }
+
+ private:
+  wl::Object<xdg_toplevel_drag_v1> drag_;
+  const raw_ref<WaylandConnection> connection_;
+};
+
 WaylandWindowDragController::WaylandWindowDragController(
     WaylandConnection* connection,
     WaylandDataDeviceManager* device_manager,
@@ -165,12 +202,16 @@ bool WaylandWindowDragController::StartDragSession(
   data_source_->Offer({kMimeTypeChromiumWindow});
   data_source_->SetDndActions(kDndActionWindowDrag);
 
-  if (IsExtendedDragAvailableInternal()) {
+  if (IsXdgToplevelDragAvailable()) {
+    xdg_toplevel_drag_ = std::make_unique<XdgToplevelDrag>(
+        *connection_, data_source_->data_source());
+  } else if (IsExtendedDragAvailableInternal()) {
     extended_drag_source_ = std::make_unique<ExtendedDragSource>(
         *connection_, data_source_->data_source());
   } else {
-    LOG(ERROR) << "zcr_extended_drag_v1 extension not available! "
-               << "Window/Tab dragging won't be fully functional.";
+    LOG(ERROR)
+        << "zcr_extended_drag_v1 and xdg_toplevel_drag_v1 extensions "
+           "not available! Window/Tab dragging won't be fully functional.";
   }
 
   data_device_->StartDrag(*data_source_, *origin_window_, serial->value,
@@ -441,17 +482,24 @@ void WaylandWindowDragController::OnDataSourceFinish(WaylandDataSource* source,
   data_offer_.reset();
   data_source_.reset();
   extended_drag_source_.reset();
+  xdg_toplevel_drag_.reset();
   origin_surface_.reset();
   origin_window_ = nullptr;
   has_received_enter_ = false;
 
-  // When extended-drag is available and the drop happens while a non-null
-  // surface was being dragged (i.e: detached mode) which had pointer focus
-  // before the drag session, we must reset focus to it, otherwise it would be
-  // wrongly kept to the latest surface received through wl_data_device::enter
-  // (see OnDragEnter function).
-  // In case of touch, though, we simply reset the focus altogether.
-  if (IsExtendedDragAvailableInternal() && dragged_window_) {
+  // When extended-drag or xdg-toplevel-drag is available and the drop happens
+  // while a non-null surface was being dragged (i.e: detached mode) which had
+  // pointer focus before the drag session, we must reset focus to it, otherwise
+  // it would be wrongly kept to the latest surface received through
+  // wl_data_device::enter (see OnDragEnter function). In case of touch, though,
+  // we simply reset the focus altogether.
+  //
+  // TODO(crbug.com/324170129): Move drop handling logic below into
+  // OnDataSourceDropPerformed instead, otherwise dropping outside target
+  // surfaces will results in drag cancellation when xdg-toplevel-drag is used.
+  bool is_protocol_available =
+      IsExtendedDragAvailableInternal() || IsXdgToplevelDragAvailable();
+  if (is_protocol_available && dragged_window_) {
     if (*drag_source_ == DragEventSource::kMouse) {
       // TODO: check if this usage is correct.
 
@@ -467,9 +515,11 @@ void WaylandWindowDragController::OnDataSourceFinish(WaylandDataSource* source,
   // Transition to |kDropped| state and determine the next action to take. If
   // drop happened while the move loop was running (i.e: kDetached), ask to quit
   // the loop, otherwise notify session end and reset state right away.
+  is_protocol_available =
+      IsExtendedDragAvailable() || IsXdgToplevelDragAvailable();
   State state_when_dropped = std::exchange(
-      state_, completed || !IsExtendedDragAvailable() ? State::kDropped
-                                                      : State::kCancelled);
+      state_, completed || !is_protocol_available ? State::kDropped
+                                                  : State::kCancelled);
   if (state_when_dropped == State::kDetached) {
     VLOG(1) << "Quiting Loop : Detached";
     QuitLoop();
@@ -673,9 +723,12 @@ void WaylandWindowDragController::SetDraggedWindow(
   dragged_window_ = window;
   drag_offset_ = offset;
 
-  // TODO(crbug.com/40598679): Fallback when extended-drag is not available.
-  if (extended_drag_source_)
+  // TODO(crbug.com/40598679): Fallback when no window drag protocol available.
+  if (extended_drag_source_) {
     extended_drag_source_->SetDraggedWindow(dragged_window_, drag_offset_);
+  } else if (xdg_toplevel_drag_) {
+    xdg_toplevel_drag_->SetDraggedWindow(dragged_window_, drag_offset_);
+  }
 }
 
 bool WaylandWindowDragController::IsExtendedDragAvailable() const {
@@ -683,6 +736,10 @@ bool WaylandWindowDragController::IsExtendedDragAvailable() const {
          IsExtendedDragAvailableInternal();
 }
 
+bool WaylandWindowDragController::IsXdgToplevelDragAvailable() const {
+  return !!connection_->xdg_toplevel_drag_manager_v1();
+}
+
 bool WaylandWindowDragController::IsActiveDragAndDropSession() const {
   return !!data_source_;
 }
diff --git a/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h b/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h
index de94ea22590990..fe523b85a2b340 100644
--- a/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h
+++ b/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h
@@ -88,6 +88,8 @@ class WaylandWindowDragController : public WaylandDataDevice::DragDelegate,
 
   // Tells if "extended drag" extension is available.
   bool IsExtendedDragAvailable() const;
+  // Tells if "xdg toplevel drag" extension is available.
+  bool IsXdgToplevelDragAvailable() const;
 
   // Returns true if there there is currently an active drag-and-drop session.
   // This is true if the `data_source_` exists (the session ends when this is
@@ -118,6 +120,7 @@ class WaylandWindowDragController : public WaylandDataDevice::DragDelegate,
 
  private:
   class ExtendedDragSource;
+  class XdgToplevelDrag;
 
   friend class WaylandWindowDragControllerTest;
   FRIEND_TEST_ALL_PREFIXES(WaylandWindowDragControllerTest,
@@ -204,6 +207,7 @@ class WaylandWindowDragController : public WaylandDataDevice::DragDelegate,
   std::unique_ptr<WaylandDataOffer> data_offer_;
 
   std::unique_ptr<ExtendedDragSource> extended_drag_source_;
+  std::unique_ptr<XdgToplevelDrag> xdg_toplevel_drag_;
 
   // The current toplevel window being dragged, when in detached mode.
   raw_ptr<WaylandToplevelWindow> dragged_window_ = nullptr;
diff --git a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h
index f34cc782ffc82e..280efa771c3fe3 100644
--- a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h
+++ b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h
@@ -88,6 +88,7 @@ class XDGToplevelWrapperImpl : public ShellToplevelWrapper {
   XDGSurfaceWrapperImpl* xdg_surface_wrapper() const;
 
  private:
+  friend class WaylandWindowDragController;
   // xdg_toplevel_listener callbacks:
   static void OnToplevelConfigure(void* data,
                                   xdg_toplevel* toplevel,
openSUSE Build Service is sponsored by