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}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
be0d7cec46
commit
8f45d25c2f
1
AUTHORS
1
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>
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ BASE_DECLARE_FEATURE(kUseDynamicCursorSize);
|
||||
bool IsWaylandSurfaceSubmissionInPixelCoordinatesEnabled();
|
||||
bool IsWaylandOverlayDelegationEnabled();
|
||||
bool IsWaylandFractionalScaleV1Enabled();
|
||||
bool IsWaylandXdgToplevelDragEnabled();
|
||||
bool IsPrettyPrintDrmModesetConfigLogsEnabled();
|
||||
bool IsUseDynamicCursorSizeEnabled();
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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_;
|
||||
|
@ -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);
|
||||
|
@ -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_;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
Reference in New Issue
Block a user