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,