KWin
Loading...
Searching...
No Matches
pointer.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
3 SPDX-FileCopyrightText: 2020 Adrien Faveraux <ad1rie3@hotmail.fr>
4 SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7*/
8
9#include "pointer.h"
10#include "clientconnection.h"
11#include "display.h"
12#include "pointer_p.h"
15#include "seat.h"
16#include "surface.h"
17#include "utils/common.h"
18#include "utils/resource.h"
19
20namespace KWin
21{
23{
24public:
25 QPointF hotspot;
26 QPointer<SurfaceInterface> surface;
27};
28
30{
31 return pointer->d.get();
32}
33
35 : q(q)
36 , seat(seat)
37 , relativePointersV1(new RelativePointerV1Interface(q))
38 , swipeGesturesV1(new PointerSwipeGestureV1Interface(q))
39 , pinchGesturesV1(new PointerPinchGestureV1Interface(q))
40 , holdGesturesV1(new PointerHoldGestureV1Interface(q))
41{
42}
43
47
48QList<PointerInterfacePrivate::Resource *> PointerInterfacePrivate::pointersForClient(ClientConnection *client) const
49{
50 return resourceMap().values(client->client());
51}
52
53void PointerInterfacePrivate::pointer_set_cursor(Resource *resource, uint32_t serial, ::wl_resource *surface_resource, int32_t hotspot_x, int32_t hotspot_y)
54{
55 SurfaceInterface *surface = nullptr;
56
57 if (!focusedSurface) {
58 return;
59 }
60 if (focusedSurface->client()->client() != resource->client()) {
61 qCDebug(KWIN_CORE, "Denied set_cursor request from unfocused client");
62 return;
63 }
64 if (focusedSerial != serial) {
65 return;
66 }
67
68 if (surface_resource) {
69 surface = SurfaceInterface::get(surface_resource);
70 if (!surface) {
71 wl_resource_post_error(resource->handle, WL_DISPLAY_ERROR_INVALID_OBJECT, "invalid surface");
72 return;
73 }
74
75 static SurfaceRole cursorRole(QByteArrayLiteral("cursor"));
76 if (const SurfaceRole *role = surface->role()) {
77 if (role != &cursorRole) {
78 wl_resource_post_error(resource->handle, error_role, "the wl_surface already has a role assigned %s", role->name().constData());
79 return;
80 }
81 } else {
82 surface->setRole(&cursorRole);
83 }
84 }
85
86 if (!cursor) {
87 cursor = std::make_unique<PointerSurfaceCursor>();
88 }
89 cursor->d->hotspot = QPointF(hotspot_x, hotspot_y) / focusedSurface->client()->scaleOverride();
90 cursor->d->surface = surface;
91
92 Q_EMIT q->cursorChanged(cursor.get());
93}
94
96{
97 wl_resource_destroy(resource->handle);
98}
99
101{
102 const ClientConnection *focusedClient = focusedSurface ? focusedSurface->client() : nullptr;
103
104 if (focusedClient && focusedClient->client() == resource->client()) {
105 const quint32 serial = seat->display()->nextSerial();
106 send_enter(resource->handle, serial, focusedSurface->resource(), wl_fixed_from_double(lastPosition.x()), wl_fixed_from_double(lastPosition.y()));
107 if (resource->version() >= WL_POINTER_FRAME_SINCE_VERSION) {
108 send_frame(resource->handle);
109 }
110 }
111}
112
114{
115 const QList<Resource *> pointerResources = pointersForClient(focusedSurface->client());
116 for (Resource *resource : pointerResources) {
117 send_leave(resource->handle, serial, focusedSurface->resource());
118 }
119}
120
121void PointerInterfacePrivate::sendEnter(const QPointF &position, quint32 serial)
122{
123 const QList<Resource *> pointerResources = pointersForClient(focusedSurface->client());
124 for (Resource *resource : pointerResources) {
125 send_enter(resource->handle, serial, focusedSurface->resource(), wl_fixed_from_double(position.x()), wl_fixed_from_double(position.y()));
126 }
127}
128
130{
131 const QList<Resource *> pointerResources = pointersForClient(focusedSurface->client());
132 for (Resource *resource : pointerResources) {
133 if (resource->version() >= WL_POINTER_FRAME_SINCE_VERSION) {
134 send_frame(resource->handle);
135 }
136 }
137}
138
139bool PointerInterfacePrivate::AxisAccumulator::Axis::shouldReset(int newDirection, std::chrono::milliseconds newTimestamp) const
140{
141 if (newTimestamp.count() - timestamp.count() >= 1000) {
142 return true;
143 }
144
145 // Reset the accumulator if the delta has opposite sign.
146 return direction && ((direction < 0) != (newDirection < 0));
147}
148
149PointerInterface::PointerInterface(SeatInterface *seat)
150 : d(new PointerInterfacePrivate(this, seat))
151{
152}
153
157
159{
160 return d->focusedSurface;
161}
162
164{
165 return d->focusedSerial;
166}
167
168void PointerInterface::sendEnter(SurfaceInterface *surface, const QPointF &position, quint32 serial)
169{
170 if (d->focusedSurface == surface) {
171 return;
172 }
173
174 if (d->focusedSurface) {
175 d->sendLeave(serial);
176 if (d->focusedSurface->client() != surface->client()) {
177 d->sendFrame();
178 }
179 disconnect(d->destroyConnection);
180 }
181
182 d->focusedSurface = surface;
183 d->focusedSerial = serial;
184 d->destroyConnection = connect(d->focusedSurface, &SurfaceInterface::aboutToBeDestroyed, this, [this]() {
185 d->sendLeave(d->seat->display()->nextSerial());
186 d->sendFrame();
187 d->focusedSurface = nullptr;
188 Q_EMIT focusedSurfaceChanged();
189 });
190
191 d->sendEnter(d->focusedSurface->toSurfaceLocal(position), serial);
192 d->sendFrame();
193 d->lastPosition = position;
194
195 Q_EMIT focusedSurfaceChanged();
196}
197
198void PointerInterface::sendLeave(quint32 serial)
199{
200 if (!d->focusedSurface) {
201 return;
202 }
203
204 d->sendLeave(serial);
205 d->sendFrame();
206
207 d->focusedSurface = nullptr;
208 disconnect(d->destroyConnection);
209
210 Q_EMIT focusedSurfaceChanged();
211}
212
213void PointerInterface::sendButton(quint32 button, PointerButtonState state, quint32 serial)
214{
215 if (!d->focusedSurface) {
216 return;
217 }
218
219 const auto pointerResources = d->pointersForClient(d->focusedSurface->client());
220 for (PointerInterfacePrivate::Resource *resource : pointerResources) {
221 d->send_button(resource->handle, serial, d->seat->timestamp().count(), button, quint32(state));
222 }
223}
224
225static void updateAccumulators(Qt::Orientation orientation, qreal delta, qint32 deltaV120, PointerInterfacePrivate *d, qint32 &valueAxisLowRes, qint32 &valueDiscrete)
226{
227 const int newDirection = deltaV120 > 0 ? 1 : -1;
228 auto &accum = d->axisAccumulator.axis(orientation);
229
230 if (accum.shouldReset(newDirection, d->seat->timestamp())) {
231 accum.reset();
232 }
233
234 accum.timestamp = d->seat->timestamp();
235 accum.direction = newDirection;
236
237 accum.axis += delta;
238 accum.axis120 += deltaV120;
239
240 // ±120 is a "wheel click"
241 if (std::abs(accum.axis120) >= 60) {
242 const int steps = accum.axis120 / 120;
243 valueDiscrete += steps;
244 if (steps == 0) {
245 valueDiscrete += accum.direction;
246 }
247
248 accum.axis120 -= valueDiscrete * 120;
249 }
250
251 if (valueDiscrete) {
252 // Accumulate the axis values to send to low-res clients
253 valueAxisLowRes = accum.axis;
254 accum.axis = 0;
255 }
256}
257
258void PointerInterface::sendAxis(Qt::Orientation orientation, qreal delta, qint32 deltaV120, PointerAxisSource source, PointerAxisRelativeDirection direction)
259{
260 if (!d->focusedSurface) {
261 return;
262 }
263
264 qint32 valueAxisLowRes = 0;
265 qint32 valueDiscrete = 0;
266
267 if (deltaV120) {
268 updateAccumulators(orientation, delta, deltaV120, d.get(),
269 valueAxisLowRes, valueDiscrete);
270 } else {
271 valueAxisLowRes = delta;
272 }
273
274 const auto pointerResources = d->pointersForClient(d->focusedSurface->client());
275 for (PointerInterfacePrivate::Resource *resource : pointerResources) {
276 const quint32 version = resource->version();
277
278 // Don't send anything if the client doesn't support high-res scrolling and
279 // we haven't accumulated a wheel click's worth of events.
280 if (version < WL_POINTER_AXIS_VALUE120_SINCE_VERSION && deltaV120 && !valueDiscrete) {
281 continue;
282 }
283
284 if (source != PointerAxisSource::Unknown && version >= WL_POINTER_AXIS_SOURCE_SINCE_VERSION) {
285 PointerInterfacePrivate::axis_source wlSource;
286 switch (source) {
288 wlSource = PointerInterfacePrivate::axis_source_wheel;
289 break;
291 wlSource = PointerInterfacePrivate::axis_source_finger;
292 break;
294 wlSource = PointerInterfacePrivate::axis_source_continuous;
295 break;
297 wlSource = PointerInterfacePrivate::axis_source_wheel_tilt;
298 break;
299 default:
300 Q_UNREACHABLE();
301 break;
302 }
303 d->send_axis_source(resource->handle, wlSource);
304 }
305
306 const auto wlOrientation =
307 (orientation == Qt::Vertical) ? PointerInterfacePrivate::axis_vertical_scroll : PointerInterfacePrivate::axis_horizontal_scroll;
308
309 if (delta) {
310 if (version >= WL_POINTER_AXIS_RELATIVE_DIRECTION_SINCE_VERSION) {
311 auto wlRelativeDirection = direction == PointerAxisRelativeDirection::Normal ? PointerInterfacePrivate::axis_relative_direction_identical : PointerInterfacePrivate::axis_relative_direction_inverted;
312
313 d->send_axis_relative_direction(resource->handle, wlOrientation, wlRelativeDirection);
314 }
315 if (deltaV120) {
316 if (version >= WL_POINTER_AXIS_VALUE120_SINCE_VERSION) {
317 // Send high resolution scroll events if client supports them
318 d->send_axis_value120(resource->handle, wlOrientation, deltaV120);
319 d->send_axis(resource->handle, d->seat->timestamp().count(), wlOrientation,
320 wl_fixed_from_double(delta));
321 } else {
322 if (version >= WL_POINTER_AXIS_DISCRETE_SINCE_VERSION && valueDiscrete) {
323 // Send discrete scroll events if client supports them.
324 d->send_axis_discrete(resource->handle, wlOrientation,
325 valueDiscrete);
326 }
327 // Send accumulated axis values
328 d->send_axis(resource->handle, d->seat->timestamp().count(), wlOrientation,
329 wl_fixed_from_double(valueAxisLowRes));
330 }
331 } else {
332 // Finger or continuous scroll
333 d->send_axis(resource->handle, d->seat->timestamp().count(), wlOrientation,
334 wl_fixed_from_double(delta));
335 }
336 } else if (version >= WL_POINTER_AXIS_STOP_SINCE_VERSION) {
337 d->send_axis_stop(resource->handle, d->seat->timestamp().count(), wlOrientation);
338 }
339 }
340}
341
342void PointerInterface::sendMotion(const QPointF &position)
343{
344 d->lastPosition = position;
345
346 if (!d->focusedSurface) {
347 return;
348 }
349
350 const QPointF localPos = d->focusedSurface->toSurfaceLocal(position);
351
352 const auto pointerResources = d->pointersForClient(d->focusedSurface->client());
353 for (PointerInterfacePrivate::Resource *resource : pointerResources) {
354 d->send_motion(resource->handle, d->seat->timestamp().count(), wl_fixed_from_double(localPos.x()), wl_fixed_from_double(localPos.y()));
355 }
356}
357
359{
360 if (d->focusedSurface) {
361 d->sendFrame();
362 }
363}
364
366{
367 return d->seat;
368}
369
371{
372 if (PointerInterfacePrivate *pointerPrivate = resource_cast<PointerInterfacePrivate *>(native)) {
373 return pointerPrivate->q;
374 }
375 return nullptr;
376}
377
382
386
388{
389 return d->hotspot;
390}
391
393{
394 return d->surface;
395}
396
397} // namespace KWin
398
399#include "moc_pointer.cpp"
Convenient Class which represents a wl_client.
wl_client * client() const
quint32 nextSerial()
Definition display.cpp:134
quint32 focusedSerial() const
Definition pointer.cpp:163
void sendButton(quint32 button, PointerButtonState state, quint32 serial)
Definition pointer.cpp:213
SeatInterface * seat() const
Definition pointer.cpp:365
void sendLeave(quint32 serial)
Definition pointer.cpp:198
void sendAxis(Qt::Orientation orientation, qreal delta, qint32 deltaV120, PointerAxisSource source, PointerAxisRelativeDirection direction)
Definition pointer.cpp:258
~PointerInterface() override
Definition pointer.cpp:154
SurfaceInterface * focusedSurface() const
Definition pointer.cpp:158
void cursorChanged(const PointerCursor &cursor)
void sendEnter(SurfaceInterface *surface, const QPointF &position, quint32 serial)
Definition pointer.cpp:168
void sendMotion(const QPointF &position)
Definition pointer.cpp:342
static PointerInterface * get(wl_resource *native)
Definition pointer.cpp:370
SurfaceInterface * focusedSurface
Definition pointer_p.h:66
void sendLeave(quint32 serial)
Definition pointer.cpp:113
std::unique_ptr< PointerSurfaceCursor > cursor
Definition pointer_p.h:69
void pointer_set_cursor(Resource *resource, uint32_t serial, ::wl_resource *surface_resource, int32_t hotspot_x, int32_t hotspot_y) override
Definition pointer.cpp:53
QList< Resource * > pointersForClient(ClientConnection *client) const
Definition pointer.cpp:48
void pointer_bind_resource(Resource *resource) override
Definition pointer.cpp:100
AxisAccumulator axisAccumulator
Definition pointer_p.h:75
void pointer_release(Resource *resource) override
Definition pointer.cpp:95
void sendEnter(const QPointF &parentSurfacePosition, quint32 serial)
Definition pointer.cpp:121
PointerInterfacePrivate(PointerInterface *q, SeatInterface *seat)
Definition pointer.cpp:34
~PointerInterfacePrivate() override
Definition pointer.cpp:44
static PointerInterfacePrivate * get(PointerInterface *pointer)
Definition pointer.cpp:29
PointerInterface * q
Definition pointer_p.h:64
QPointF hotspot() const
Definition pointer.cpp:387
SurfaceInterface * surface() const
Definition pointer.cpp:392
QPointer< SurfaceInterface > surface
Definition pointer.cpp:26
Represents a Seat on the Wayland Display.
Definition seat.h:134
Display * display() const
Definition seat.cpp:424
std::chrono::milliseconds timestamp() const
Definition seat.cpp:483
Resource representing a wl_surface.
Definition surface.h:80
ClientConnection * client() const
Definition surface.cpp:444
static SurfaceInterface * get(wl_resource *native)
Definition surface.cpp:819
QPoint toSurfaceLocal(const QPoint &point) const
Definition surface.cpp:1071
SurfaceRole * role() const
Definition surface.cpp:429
wl_resource * resource() const
Definition surface.cpp:449
void setRole(SurfaceRole *role)
Definition surface.cpp:434
constexpr int version
PointerAxisSource
Definition seat.h:53
PointerButtonState
Definition seat.h:73
PointerAxisRelativeDirection
Definition seat.h:64
bool shouldReset(int newDirection, std::chrono::milliseconds newTimestamp) const
Definition pointer.cpp:139