1 /*
2 SPDX-FileCopyrightText: 2020 Roman Gilg <subdiff@gmail.com>
3 SPDX-FileCopyrightText: 2021 Francesco Sorrentino <francesco.sorr@gmail.com>
4
5 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only
6 */
7 #include "drag_pool.h"
8 #include "data_device.h"
9 #include "data_source.h"
10 #include "display.h"
11 #include "seat.h"
12 #include "seat_p.h"
13 #include "touch.h"
14 #include "utils.h"
15
16 namespace Wrapland::Server
17 {
18
drag_pool(Seat * seat)19 drag_pool::drag_pool(Seat* seat)
20 : seat{seat}
21 {
22 }
23
get_source() const24 drag_source const& drag_pool::get_source() const
25 {
26 return source;
27 }
28
get_target() const29 drag_target const& drag_pool::get_target() const
30 {
31 return target;
32 }
33
cancel()34 void drag_pool::cancel()
35 {
36 if (target.dev) {
37 target.dev->updateDragTarget(nullptr, 0);
38 target.dev = nullptr;
39 }
40 end(0);
41 }
42
end(uint32_t serial)43 void drag_pool::end(uint32_t serial)
44 {
45 auto trgt = target.dev;
46
47 QObject::disconnect(source.device_destroy_notifier);
48 QObject::disconnect(source.destroy_notifier);
49
50 if (source.dev && source.dev->dragSource()) {
51 source.dev->dragSource()->dropPerformed();
52 }
53
54 if (trgt) {
55 trgt->drop();
56 trgt->updateDragTarget(nullptr, serial);
57 }
58
59 source = {};
60 target = {};
61
62 Q_EMIT seat->dragSurfaceChanged();
63 Q_EMIT seat->dragEnded();
64 }
65
set_target(Surface * new_surface,const QMatrix4x4 & inputTransformation)66 void drag_pool::set_target(Surface* new_surface, const QMatrix4x4& inputTransformation)
67 {
68 if (source.mode == drag_mode::pointer) {
69 set_target(new_surface, seat->pointers().get_position(), inputTransformation);
70 } else {
71 assert(source.mode == drag_mode::touch);
72 set_target(
73 new_surface, seat->touches().get_focus().first_touch_position, inputTransformation);
74 }
75 }
76
set_target(Surface * new_surface,const QPointF & globalPosition,const QMatrix4x4 & inputTransformation)77 void drag_pool::set_target(Surface* new_surface,
78 const QPointF& globalPosition,
79 const QMatrix4x4& inputTransformation)
80 {
81 if (new_surface == target.surface) {
82 // no change
83 return;
84 }
85 auto const serial = seat->d_ptr->display()->handle()->nextSerial();
86 if (target.dev) {
87 target.dev->updateDragTarget(nullptr, serial);
88 QObject::disconnect(target.destroy_notifier);
89 target.destroy_notifier = QMetaObject::Connection();
90 }
91
92 // In theory we can have multiple data devices and we should send the drag to all of them, but
93 // that seems overly complicated. In practice so far the only case for multiple data devices is
94 // for clipboard overriding.
95 target.dev = interfaceForSurface(new_surface, seat->d_ptr->data_devices.devices);
96
97 if (source.mode == drag_mode::pointer) {
98 seat->pointers().set_position(globalPosition);
99 } else if (source.mode == drag_mode::touch
100 && seat->touches().get_focus().first_touch_position != globalPosition) {
101 // TODO(romangg): instead of moving any touch point could we move with id 0? Probably yes
102 // if we always end a drag once the id 0 touch point has been lifted.
103 seat->touches().touch_move_any(globalPosition);
104 }
105 if (target.dev) {
106 target.surface = new_surface;
107 target.transformation = inputTransformation;
108 target.dev->updateDragTarget(target.surface, serial);
109 target.destroy_notifier
110 = QObject::connect(target.dev, &DataDevice::resourceDestroyed, seat, [this] {
111 QObject::disconnect(target.destroy_notifier);
112 target.destroy_notifier = QMetaObject::Connection();
113 target.dev = nullptr;
114 });
115
116 } else {
117 target.surface = nullptr;
118 }
119 Q_EMIT seat->dragSurfaceChanged();
120 }
121
is_in_progress() const122 bool drag_pool::is_in_progress() const
123 {
124 return source.mode != drag_mode::none;
125 }
126
is_pointer_drag() const127 bool drag_pool::is_pointer_drag() const
128 {
129 return source.mode == drag_mode::pointer;
130 }
131
is_touch_drag() const132 bool drag_pool::is_touch_drag() const
133 {
134 return source.mode == drag_mode::touch;
135 }
136
perform_drag(DataDevice * dataDevice)137 void drag_pool::perform_drag(DataDevice* dataDevice)
138 {
139 const auto dragSerial = dataDevice->dragImplicitGrabSerial();
140 auto* dragSurface = dataDevice->origin();
141 auto& pointers = seat->pointers();
142
143 if (pointers.has_implicit_grab(dragSerial)) {
144 source.mode = drag_mode::pointer;
145 source.pointer = interfaceForSurface(dragSurface, seat->pointers().get_devices());
146 target.transformation = pointers.get_focus().transformation;
147 } else if (seat->touches().has_implicit_grab(dragSerial)) {
148 source.mode = drag_mode::touch;
149 source.touch = interfaceForSurface(dragSurface, seat->touches().get_devices());
150 // TODO(unknown author): touch transformation
151 } else {
152 // no implicit grab, abort drag
153 return;
154 }
155 auto* originSurface = dataDevice->origin();
156 const bool proxied = originSurface->dataProxy();
157 if (!proxied) {
158 // origin surface
159 target.dev = dataDevice;
160 target.surface = originSurface;
161 // TODO(unknown author): transformation needs to be either pointer or touch
162 target.transformation = pointers.get_focus().transformation;
163 }
164
165 source.dev = dataDevice;
166 source.device_destroy_notifier
167 = QObject::connect(dataDevice, &DataDevice::resourceDestroyed, seat, [this] {
168 end(seat->d_ptr->display()->handle()->nextSerial());
169 });
170
171 if (dataDevice->dragSource()) {
172 source.destroy_notifier = QObject::connect(
173 dataDevice->dragSource(), &DataSource::resourceDestroyed, seat, [this] {
174 const auto serial = seat->d_ptr->display()->handle()->nextSerial();
175 if (target.dev) {
176 target.dev->updateDragTarget(nullptr, serial);
177 target.dev = nullptr;
178 }
179 end(serial);
180 });
181 } else {
182 source.destroy_notifier = QMetaObject::Connection();
183 }
184 dataDevice->updateDragTarget(proxied ? nullptr : originSurface,
185 dataDevice->dragImplicitGrabSerial());
186 Q_EMIT seat->dragStarted();
187 Q_EMIT seat->dragSurfaceChanged();
188 }
189
190 }
191