File: wayland_window_drag_controller.h

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (240 lines) | stat: -rw-r--r-- 10,366 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_WINDOW_DRAG_CONTROLLER_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_WINDOW_DRAG_CONTROLLER_H_

#include <cstdint>
#include <iosfwd>
#include <memory>
#include <ostream>
#include <string>

#include "base/functional/callback_forward.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-forward.h"
#include "ui/events/event.h"
#include "ui/events/platform/platform_event_dispatcher.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
#include "ui/ozone/platform/wayland/gpu/wayland_surface_factory.h"
#include "ui/ozone/platform/wayland/host/wayland_data_device.h"
#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
#include "ui/ozone/platform/wayland/host/wayland_keyboard.h"
#include "ui/ozone/platform/wayland/host/wayland_pointer.h"
#include "ui/ozone/platform/wayland/host/wayland_serial_tracker.h"
#include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h"
#include "ui/ozone/platform/wayland/host/wayland_touch.h"
#include "ui/ozone/platform/wayland/host/wayland_window_observer.h"

namespace ui {

class ScopedEventDispatcher;
class WaylandConnection;
class WaylandDataDeviceManager;
class WaylandDataOffer;
class WaylandWindow;
class WaylandWindowManager;
class WaylandSurface;

// Drag controller implementation that drives window moving sessions (aka: tab
// dragging). Wayland Drag and Drop protocol is used, under the hood, to keep
// track of cursor location and surface focus.
//
// TODO(crbug.com/40598679): Use drag icon to emulate window moving.
class WaylandWindowDragController : public WaylandDataDevice::DragDelegate,
                                    public WaylandDataSource::Delegate,
                                    public PlatformEventDispatcher,
                                    public WaylandWindowObserver {
 public:
  // Constants used to keep track of the drag controller state.
  enum class State {
    kIdle,       // No DnD session nor drag loop running.
    kAttached,   // DnD session ongoing but no drag loop running.
    kDetached,   // Drag loop running. ie: blocked in a Drag() call.
    kDropped,    // Drop event was just received.
    kCancelled,  // Drag cancel event was just received.
    kAttaching,  // About to transition back to |kAttached|.
  };

  WaylandWindowDragController(WaylandConnection* connection,
                              WaylandDataDeviceManager* device_manager,
                              WaylandPointer::Delegate* pointer_delegate,
                              WaylandTouch::Delegate* touch_delegate,
                              WaylandKeyboard::Delegate* keyboard_delegate);
  WaylandWindowDragController(const WaylandWindowDragController&) = delete;
  WaylandWindowDragController& operator=(const WaylandWindowDragController&) =
      delete;
  ~WaylandWindowDragController() override;

  // Starts a new Wayland DND session for window dragging, if not done yet.
  // Whereas `origin` is used as the origin drag surface and `event_source` as
  // the event type that is triggering the drag session, ie: mouse or touch. See
  // https://wayland.app/protocols/wayland#wl_data_device:request:start_drag for
  // more protocol-related information.
  bool StartDragSession(WaylandToplevelWindow* origin,
                        mojom::DragEventSource event_source);

  bool Drag(WaylandToplevelWindow* window, const gfx::Vector2d& offset);
  void StopDragging();

  // Returns true if there is an in-progress drag session owned by the window
  // drag controller.
  bool IsDragInProgress() const;

  // Returns true iff there is an active drag session and `window` is the
  // current window being dragged.
  bool IsDraggingWindow(WaylandToplevelWindow* window) const;

  // Tells if any of the window drag protocol (ie: zcr-extended-drag-v1 or
  // xdg-toplevel-drag-v1) is available. May also return true in tests if
  // `window_drag_protocol_available_for_testing_` is set.
  bool IsWindowDragProtocolAvailable() 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
  // destroyed).
  bool IsActiveDragAndDropSession() const;

  std::optional<mojom::DragEventSource> drag_source() { return drag_source_; }

  void DumpState(std::ostream& out) const;

 private:
  class ExtendedDragSource;
  class XdgToplevelDrag;

  friend class WaylandWindowDragControllerTestApi;

  // WaylandDataDevice::DragDelegate:
  bool IsDragSource() const override;
  void DrawIcon() override;
  void OnDragOffer(std::unique_ptr<WaylandDataOffer> offer) override;
  void OnDragEnter(WaylandWindow* window,
                   const gfx::PointF& location,
                   base::TimeTicks timestamp,
                   uint32_t serial) override;
  void OnDragMotion(const gfx::PointF& location,
                    base::TimeTicks timestamp) override;
  void OnDragLeave(base::TimeTicks timestamp) override;
  void OnDragDrop(base::TimeTicks timestamp) override;

  // WaylandDataSource::Delegate
  void OnDataSourceDropPerformed(WaylandDataSource* source,
                                 base::TimeTicks timestamp) override;
  void OnDataSourceFinish(WaylandDataSource* source,
                          base::TimeTicks timestamp,
                          bool completed) override;
  void OnDataSourceSend(WaylandDataSource* source,
                        const std::string& mime_type,
                        std::string* contents) override;

  // PlatformEventDispatcher
  bool CanDispatchEvent(const PlatformEvent& event) override;
  uint32_t DispatchEvent(const PlatformEvent& event) override;

  // WaylandWindowObserver:
  void OnWindowRemoved(WaylandWindow* window) override;

  // Asks the Wayland compositor to cancel the drag session, if any.
  void CancelDragSession();
  // Handles drag/move mouse |event|, while in |kDetached| mode, forwarding it
  // as a bounds change event to the upper layer handlers.
  void HandleMotionEvent(LocatedEvent* event);
  // Handles drag session end, which might correspond to either a successful
  // drop, notified through wl_data_source.dnd_drop_performed or a source
  // finish/cancellation.
  void HandleDragEnd(bool completed, base::TimeTicks timestamp);
  // Handles the mouse button release (i.e: drop). Dispatches the required
  // events and resets the internal state.
  void HandleDropAndResetState(base::TimeTicks timestamp);
  // Registers as the top level PlatformEvent dispatcher and runs a nested
  // RunLoop, which blocks until the DnD session finishes.
  void RunLoop();
  // Unregisters the internal event dispatcher and asks to quit the nested loop.
  void QuitLoop();
  // Set |window| as the current dragged window and |offset| as the drag offset,
  // which makes |window| to appear anchored to the pointer cursor, if
  // extended-drag extension is available.
  void SetDraggedWindow(WaylandToplevelWindow* window,
                        const gfx::Vector2d& offset);

  // Returns the serial for the given |drag_source| if |origin| has the
  // corresponding focus, otherwise return null.
  std::optional<wl::Serial> GetSerial(mojom::DragEventSource drag_source,
                                      WaylandToplevelWindow* origin);

  const raw_ptr<WaylandConnection> connection_;
  const raw_ptr<WaylandDataDeviceManager> data_device_manager_;
  const raw_ptr<WaylandDataDevice> data_device_;
  const raw_ptr<WaylandWindowManager> window_manager_;
  const raw_ptr<WaylandPointer::Delegate> pointer_delegate_;
  const raw_ptr<WaylandTouch::Delegate> touch_delegate_;
  const raw_ptr<WaylandKeyboard::Delegate> keyboard_delegate_;

  State state_ = State::kIdle;
  std::optional<mojom::DragEventSource> drag_source_;

  gfx::Vector2d drag_offset_;

  // The last known pointer location in DIP.
  gfx::PointF pointer_location_;

  std::unique_ptr<WaylandDataSource> data_source_;
  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;

  // Keeps track of the window that holds the pointer grab. i.e: the owner of
  // the surface that must receive the mouse release event upon drop.
  raw_ptr<WaylandWindow> events_grabber_ = nullptr;

  // The window where the DND session originated from. i.e: which had the
  // pointer focus when the session was initiated.
  raw_ptr<WaylandWindow> origin_window_ = nullptr;

  raw_ptr<WaylandWindow, DanglingUntriaged> drag_target_window_ = nullptr;

  // The |origin_window_| can be destroyed during the DND session. If this
  // happens, |origin_surface_| takes ownership of its surface and ensure it
  // is kept alive until the end of the session.
  std::unique_ptr<WaylandSurface> origin_surface_;

  // In outgoing sessions, tracks if any drag enter has already been received.
  bool has_received_enter_ = false;

  std::unique_ptr<ScopedEventDispatcher> nested_dispatcher_;
  base::OnceClosure quit_loop_closure_;

  // Tells if the current drag motion events should be processed.
  //
  // The processing of drag motion events should be suspended for buggy
  // compositors send wl_pointer::motion events, for example, while a DND
  // session is still in progress, which leads to issues in window dragging
  // sessions.
  //
  // This flag is used to make window drag controller resistant to such
  // scenarios.
  bool should_process_drag_motion_events_ = false;

  bool window_drag_protocol_available_for_testing_ = false;

  base::WeakPtrFactory<WaylandWindowDragController> weak_factory_{this};
};

// Stream operator so WaylandWindowDragController::State can be used in
// log/assertion statements.
std::ostream& operator<<(std::ostream& out,
                         WaylandWindowDragController::State state);

}  // namespace ui

#endif  // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_WINDOW_DRAG_CONTROLLER_H_