1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qwaylandinputdevice_p.h"
5
6#include "qwaylandintegration_p.h"
7#include "qwaylandwindow_p.h"
8#include "qwaylandsurface_p.h"
9#include "qwaylandbuffer_p.h"
10#if QT_CONFIG(wayland_datadevice)
11#include "qwaylanddatadevice_p.h"
12#include "qwaylanddatadevicemanager_p.h"
13#endif
14#if QT_CONFIG(wayland_client_primary_selection)
15#include "qwaylandprimaryselectionv1_p.h"
16#endif
17#if QT_CONFIG(tabletevent)
18#include "qwaylandtabletv2_p.h"
19#endif
20#include "qwaylandpointergestures_p.h"
21#include "qwaylandtouch_p.h"
22#include "qwaylandscreen_p.h"
23#include "qwaylandcursor_p.h"
24#include "qwaylanddisplay_p.h"
25#include "qwaylandshmbackingstore_p.h"
26#include "qwaylandtextinputv1_p.h"
27#include "qwaylandtextinputv2_p.h"
28#if QT_WAYLAND_TEXT_INPUT_V4_WIP
29#include "qwaylandtextinputv4_p.h"
30#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
31#include "qwaylandtextinputinterface_p.h"
32#include "qwaylandinputcontext_p.h"
33#include "qwaylandinputmethodcontext_p.h"
34
35#include <QtGui/private/qpixmap_raster_p.h>
36#include <QtGui/private/qguiapplication_p.h>
37#include <qpa/qplatformwindow.h>
38#include <qpa/qplatforminputcontext.h>
39#include <qpa/qplatformtheme.h>
40#include <QDebug>
41
42#include <unistd.h>
43#include <fcntl.h>
44#include <sys/mman.h>
45
46#if QT_CONFIG(cursor)
47#include <wayland-cursor.h>
48#endif
49
50#include <QtGui/QGuiApplication>
51#include <QtGui/QPointingDevice>
52
53QT_BEGIN_NAMESPACE
54
55namespace QtWaylandClient {
56
57Q_LOGGING_CATEGORY(lcQpaWaylandInput, "qt.qpa.wayland.input");
58
59// The maximum number of concurrent touchpoints is not exposed in wayland, so we assume a
60// reasonable number of them. As of 2021 most touchscreen panels support 10 concurrent touchpoints.
61static const int MaxTouchPoints = 10;
62
63QWaylandInputDevice::Keyboard::Keyboard(QWaylandInputDevice *p)
64 : mParent(p)
65{
66 init(p->get_keyboard());
67 mRepeatTimer.callOnTimeout(args: [&]() {
68 if (!focusWindow()) {
69 // We destroyed the keyboard focus surface, but the server didn't get the message yet...
70 // or the server didn't send an enter event first.
71 return;
72 }
73 mRepeatTimer.setInterval(1000 / mRepeatRate);
74 handleKey(timestamp: mRepeatKey.time, type: QEvent::KeyRelease, key: mRepeatKey.key, modifiers: mRepeatKey.modifiers,
75 nativeScanCode: mRepeatKey.code, nativeVirtualKey: mRepeatKey.nativeVirtualKey, nativeModifiers: mRepeatKey.nativeModifiers,
76 text: mRepeatKey.text, autorepeat: true);
77 handleKey(timestamp: mRepeatKey.time, type: QEvent::KeyPress, key: mRepeatKey.key, modifiers: mRepeatKey.modifiers,
78 nativeScanCode: mRepeatKey.code, nativeVirtualKey: mRepeatKey.nativeVirtualKey, nativeModifiers: mRepeatKey.nativeModifiers,
79 text: mRepeatKey.text, autorepeat: true);
80 });
81}
82
83#if QT_CONFIG(xkbcommon)
84bool QWaylandInputDevice::Keyboard::createDefaultKeymap()
85{
86 struct xkb_context *ctx = mParent->mQDisplay->xkbContext();
87 if (!ctx)
88 return false;
89
90 struct xkb_rule_names names;
91 names.rules = "evdev";
92 names.model = "pc105";
93 names.layout = "us";
94 names.variant = "";
95 names.options = "";
96
97 mXkbKeymap.reset(p: xkb_keymap_new_from_names(context: ctx, names: &names, flags: XKB_KEYMAP_COMPILE_NO_FLAGS));
98 if (mXkbKeymap)
99 mXkbState.reset(p: xkb_state_new(keymap: mXkbKeymap.get()));
100
101 if (!mXkbKeymap || !mXkbState) {
102 qCWarning(lcQpaWayland, "failed to create default keymap");
103 return false;
104 }
105
106 return true;
107}
108#endif
109
110QWaylandInputDevice::Keyboard::~Keyboard()
111{
112 if (mFocus)
113 QWindowSystemInterface::handleWindowActivated(window: nullptr);
114 if (version() >= 3)
115 wl_keyboard_release(object());
116 else
117 wl_keyboard_destroy(object());
118}
119
120QWaylandWindow *QWaylandInputDevice::Keyboard::focusWindow() const
121{
122 return mFocus ? mFocus->waylandWindow() : nullptr;
123}
124
125QWaylandInputDevice::Pointer::Pointer(QWaylandInputDevice *seat)
126 : mParent(seat)
127{
128 init(seat->get_pointer());
129#if QT_CONFIG(cursor)
130 if (auto cursorShapeManager = seat->mQDisplay->cursorShapeManager()) {
131 mCursor.shape.reset(other: new QWaylandCursorShape(cursorShapeManager->get_pointer(object())));
132 }
133
134 mCursor.frameTimer.setSingleShot(true);
135 mCursor.frameTimer.callOnTimeout(args: [&]() {
136 cursorTimerCallback();
137 });
138#endif
139}
140
141QWaylandInputDevice::Pointer::~Pointer()
142{
143 if (version() >= 3)
144 wl_pointer_release(object());
145 else
146 wl_pointer_destroy(object());
147}
148
149QWaylandWindow *QWaylandInputDevice::Pointer::focusWindow() const
150{
151 return mFocus ? mFocus->waylandWindow() : nullptr;
152}
153
154#if QT_CONFIG(cursor)
155
156class WlCallback : public QtWayland::wl_callback {
157public:
158 explicit WlCallback(::wl_callback *callback, std::function<void(uint32_t)> fn, bool autoDelete = false)
159 : QtWayland::wl_callback(callback)
160 , m_fn(fn)
161 , m_autoDelete(autoDelete)
162 {}
163 ~WlCallback() override { wl_callback_destroy(object()); }
164 bool done() const { return m_done; }
165 void callback_done(uint32_t callback_data) override {
166 m_done = true;
167 m_fn(callback_data);
168 if (m_autoDelete)
169 delete this;
170 }
171private:
172 bool m_done = false;
173 std::function<void(uint32_t)> m_fn;
174 bool m_autoDelete = false;
175};
176
177class CursorSurface : public QWaylandSurface
178{
179public:
180 explicit CursorSurface(QWaylandInputDevice::Pointer *pointer, QWaylandDisplay *display)
181 : QWaylandSurface(display)
182 , m_pointer(pointer)
183 {
184 connect(this, &QWaylandSurface::screensChanged,
185 m_pointer, &QWaylandInputDevice::Pointer::updateCursor);
186 }
187
188 void reset()
189 {
190 m_setSerial = 0;
191 m_hotspot = QPoint();
192 }
193
194 // Size and hotspot are in surface coordinates
195 void update(wl_buffer *buffer, const QPoint &hotspot, const QSize &size, int bufferScale, bool animated = false)
196 {
197 // Calling code needs to ensure buffer scale is supported if != 1
198 Q_ASSERT(bufferScale == 1 || version() >= 3);
199
200 auto enterSerial = m_pointer->mEnterSerial;
201 if (m_setSerial < enterSerial || m_hotspot != hotspot) {
202 m_pointer->set_cursor(m_pointer->mEnterSerial, object(), hotspot.x(), hotspot.y());
203 m_setSerial = enterSerial;
204 m_hotspot = hotspot;
205 }
206
207 if (version() >= 3)
208 set_buffer_scale(bufferScale);
209
210 attach(buffer, 0, 0);
211 damage(0, 0, size.width(), size.height());
212 m_frameCallback.reset();
213 if (animated) {
214 m_frameCallback.reset(other: new WlCallback(frame(), [this](uint32_t time){
215 Q_UNUSED(time);
216 m_pointer->cursorFrameCallback();
217 }));
218 }
219 commit();
220 }
221
222 int outputScale() const
223 {
224 int scale = 0;
225 for (auto *screen : m_screens)
226 scale = qMax(a: scale, b: screen->scale());
227 return scale;
228 }
229
230private:
231 QScopedPointer<WlCallback> m_frameCallback;
232 QWaylandInputDevice::Pointer *m_pointer = nullptr;
233 uint m_setSerial = 0;
234 QPoint m_hotspot;
235};
236
237int QWaylandInputDevice::Pointer::idealCursorScale() const
238{
239 if (seat()->mQDisplay->compositor()->version() < 3) {
240 return 1;
241 }
242
243 if (auto *s = mCursor.surface.data()) {
244 if (s->outputScale() > 0)
245 return s->outputScale();
246 }
247
248 return seat()->mCursor.fallbackOutputScale;
249}
250
251void QWaylandInputDevice::Pointer::updateCursorTheme()
252{
253 QString cursorThemeName;
254 QSize cursorSize;
255
256 if (const QPlatformTheme *platformTheme = QGuiApplicationPrivate::platformTheme()) {
257 cursorThemeName = platformTheme->themeHint(hint: QPlatformTheme::MouseCursorTheme).toString();
258 cursorSize = platformTheme->themeHint(hint: QPlatformTheme::MouseCursorSize).toSize();
259 }
260
261 if (cursorThemeName.isEmpty())
262 cursorThemeName = QStringLiteral("default");
263 if (cursorSize.isEmpty())
264 cursorSize = QSize(24, 24);
265
266 int scale = idealCursorScale();
267 int pixelSize = cursorSize.width() * scale;
268 auto *display = seat()->mQDisplay;
269 mCursor.theme = display->loadCursorTheme(name: cursorThemeName, pixelSize);
270
271 if (!mCursor.theme)
272 return; // A warning has already been printed in loadCursorTheme
273
274 if (auto *arrow = mCursor.theme->cursor(shape: Qt::ArrowCursor)) {
275 int arrowPixelSize = qMax(a: arrow->images[0]->width, b: arrow->images[0]->height); // Not all cursor themes are square
276 while (scale > 1 && arrowPixelSize / scale < cursorSize.width())
277 --scale;
278 } else {
279 qCWarning(lcQpaWayland) << "Cursor theme does not support the arrow cursor";
280 }
281 mCursor.themeBufferScale = scale;
282}
283
284void QWaylandInputDevice::Pointer::updateCursor()
285{
286 if (mEnterSerial == 0)
287 return;
288
289 auto shape = seat()->mCursor.shape;
290
291 if (shape == Qt::BlankCursor) {
292 if (mCursor.surface)
293 mCursor.surface->reset();
294 set_cursor(mEnterSerial, nullptr, 0, 0);
295 return;
296 }
297
298 if (shape == Qt::BitmapCursor) {
299 auto buffer = seat()->mCursor.bitmapBuffer;
300 if (!buffer) {
301 qCWarning(lcQpaWayland) << "No buffer for bitmap cursor, can't set cursor";
302 return;
303 }
304 auto hotspot = seat()->mCursor.hotspot;
305 int bufferScale = seat()->mCursor.bitmapScale;
306 getOrCreateCursorSurface()->update(buffer: buffer->buffer(), hotspot, size: buffer->size(), bufferScale);
307 return;
308 }
309
310 if (mCursor.shape) {
311 if (mCursor.surface) {
312 mCursor.surface->reset();
313 }
314 mCursor.shape->setShape(serial: mEnterSerial, shape);
315 return;
316 }
317
318 if (!mCursor.theme || idealCursorScale() != mCursor.themeBufferScale)
319 updateCursorTheme();
320
321 if (!mCursor.theme)
322 return;
323
324 // Set from shape using theme
325 uint time = seat()->mCursor.animationTimer.elapsed();
326
327 if (struct ::wl_cursor *waylandCursor = mCursor.theme->cursor(shape)) {
328 uint duration = 0;
329 int frame = wl_cursor_frame_and_duration(cursor: waylandCursor, time, duration: &duration);
330 ::wl_cursor_image *image = waylandCursor->images[frame];
331
332 struct wl_buffer *buffer = wl_cursor_image_get_buffer(image);
333 if (!buffer) {
334 qCWarning(lcQpaWayland) << "Could not find buffer for cursor" << shape;
335 return;
336 }
337 int bufferScale = mCursor.themeBufferScale;
338 QPoint hotspot = QPoint(image->hotspot_x, image->hotspot_y) / bufferScale;
339 QSize size = QSize(image->width, image->height) / bufferScale;
340 bool animated = duration > 0;
341 if (animated) {
342 mCursor.gotFrameCallback = false;
343 mCursor.gotTimerCallback = false;
344 mCursor.frameTimer.start(msec: duration);
345 }
346 getOrCreateCursorSurface()->update(buffer, hotspot, size, bufferScale, animated);
347 return;
348 }
349
350 qCWarning(lcQpaWayland) << "Unable to change to cursor" << shape;
351}
352
353CursorSurface *QWaylandInputDevice::Pointer::getOrCreateCursorSurface()
354{
355 if (!mCursor.surface)
356 mCursor.surface.reset(other: new CursorSurface(this, seat()->mQDisplay));
357 return mCursor.surface.get();
358}
359
360void QWaylandInputDevice::Pointer::cursorTimerCallback()
361{
362 mCursor.gotTimerCallback = true;
363 if (mCursor.gotFrameCallback) {
364 updateCursor();
365 }
366}
367
368void QWaylandInputDevice::Pointer::cursorFrameCallback()
369{
370 mCursor.gotFrameCallback = true;
371 if (mCursor.gotTimerCallback) {
372 updateCursor();
373 }
374}
375
376#endif // QT_CONFIG(cursor)
377
378QWaylandInputDevice::Touch::Touch(QWaylandInputDevice *p)
379 : mParent(p)
380{
381 init(p->get_touch());
382}
383
384QWaylandInputDevice::Touch::~Touch()
385{
386 if (version() >= 3)
387 wl_touch_release(object());
388 else
389 wl_touch_destroy(object());
390}
391
392QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version, uint32_t id)
393 : QtWayland::wl_seat(display->wl_registry(), id, qMin(version, 8))
394 , mQDisplay(display)
395 , mDisplay(display->wl_display())
396{
397#if QT_CONFIG(wayland_datadevice)
398 if (mQDisplay->dndSelectionHandler()) {
399 mDataDevice = mQDisplay->dndSelectionHandler()->getDataDevice(inputDevice: this);
400 }
401#endif
402
403#if QT_CONFIG(wayland_client_primary_selection)
404 // TODO: Could probably decouple this more if there was a signal for new seat added
405 if (auto *psm = mQDisplay->primarySelectionManager())
406 setPrimarySelectionDevice(psm->createDevice(seat: this));
407#endif
408
409 if (mQDisplay->textInputManagerv1()) {
410 auto textInput = new QWaylandTextInputv1(mQDisplay, mQDisplay->textInputManagerv1()->create_text_input());
411 textInput->setSeat(wl_seat());
412 mTextInput.reset(other: textInput);
413 }
414
415 if (mQDisplay->textInputManagerv2())
416 mTextInput.reset(new QWaylandTextInputv2(mQDisplay, mQDisplay->textInputManagerv2()->get_text_input(wl_seat())));
417
418#if QT_WAYLAND_TEXT_INPUT_V4_WIP
419 if (mQDisplay->textInputManagerv4())
420 mTextInput.reset(new QWaylandTextInputv4(mQDisplay, mQDisplay->textInputManagerv4()->get_text_input(wl_seat())));
421#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
422
423 if (mQDisplay->textInputMethodManager())
424 mTextInputMethod.reset(new QWaylandTextInputMethod(mQDisplay, mQDisplay->textInputMethodManager()->get_text_input_method(wl_seat())));
425
426#if QT_CONFIG(tabletevent)
427 if (auto *tm = mQDisplay->tabletManager())
428 mTabletSeat.reset(other: new QWaylandTabletSeatV2(tm, this));
429#endif
430}
431
432// Can't be in header because dtors for scoped pointers aren't known there.
433QWaylandInputDevice::~QWaylandInputDevice() = default;
434
435void QWaylandInputDevice::seat_capabilities(uint32_t caps)
436{
437 mCaps = caps;
438
439 if (caps & WL_SEAT_CAPABILITY_KEYBOARD && !mKeyboard) {
440 mKeyboard.reset(other: createKeyboard(device: this));
441 } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && mKeyboard) {
442 mKeyboard.reset();
443 }
444
445 if (caps & WL_SEAT_CAPABILITY_POINTER && !mPointer) {
446 mPointer.reset(other: createPointer(device: this));
447
448 auto *pointerGestures = mQDisplay->pointerGestures();
449 if (pointerGestures) {
450 // NOTE: The name of the device and its system ID are not exposed on Wayland.
451 mTouchPadDevice = new QPointingDevice(
452 QLatin1String("touchpad"), 0, QInputDevice::DeviceType::TouchPad,
453 QPointingDevice::PointerType::Finger, QInputDevice::Capability::Position,
454 MaxTouchPoints, 0, QString(), QPointingDeviceUniqueId(), this);
455 QWindowSystemInterface::registerInputDevice(device: mTouchPadDevice);
456 mPointerGesturePinch.reset(other: pointerGestures->createPointerGesturePinch(device: this));
457 mPointerGesturePinch->init(pointerGestures->get_pinch_gesture(get_pointer()));
458 mPointerGestureSwipe.reset(other: pointerGestures->createPointerGestureSwipe(device: this));
459 mPointerGestureSwipe->init(pointerGestures->get_swipe_gesture(get_pointer()));
460 }
461 } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && mPointer) {
462 mPointer.reset();
463 mPointerGesturePinch.reset();
464 mPointerGestureSwipe.reset();
465 }
466
467 if (caps & WL_SEAT_CAPABILITY_TOUCH && !mTouch) {
468 mTouch.reset(other: createTouch(device: this));
469
470 if (!mTouchDevice) {
471 // TODO number of touchpoints, actual name and ID
472 mTouchDevice = new QPointingDevice(
473 QLatin1String("some touchscreen"), 0, QInputDevice::DeviceType::TouchScreen,
474 QPointingDevice::PointerType::Finger, QInputDevice::Capability::Position,
475 MaxTouchPoints, 0,QString(), QPointingDeviceUniqueId(), this);
476 QWindowSystemInterface::registerInputDevice(device: mTouchDevice);
477 }
478 } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && mTouch) {
479 mTouch.reset();
480 }
481}
482
483QWaylandInputDevice::Keyboard *QWaylandInputDevice::createKeyboard(QWaylandInputDevice *device)
484{
485 return new Keyboard(device);
486}
487
488QWaylandInputDevice::Pointer *QWaylandInputDevice::createPointer(QWaylandInputDevice *device)
489{
490 return new Pointer(device);
491}
492
493QWaylandInputDevice::Touch *QWaylandInputDevice::createTouch(QWaylandInputDevice *device)
494{
495 return new Touch(device);
496}
497
498QWaylandInputDevice::Keyboard *QWaylandInputDevice::keyboard() const
499{
500 return mKeyboard.data();
501}
502
503QWaylandInputDevice::Pointer *QWaylandInputDevice::pointer() const
504{
505 return mPointer.data();
506}
507
508QWaylandPointerGestureSwipe *QWaylandInputDevice::pointerGestureSwipe() const
509{
510 return mPointerGestureSwipe.data();
511}
512
513QWaylandPointerGesturePinch *QWaylandInputDevice::pointerGesturePinch() const
514{
515 return mPointerGesturePinch.data();
516}
517
518QWaylandInputDevice::Touch *QWaylandInputDevice::touch() const
519{
520 return mTouch.data();
521}
522
523void QWaylandInputDevice::handleEndDrag()
524{
525 if (mTouch)
526 mTouch->releasePoints();
527 if (mPointer)
528 mPointer->releaseButtons();
529}
530
531#if QT_CONFIG(wayland_datadevice)
532void QWaylandInputDevice::setDataDevice(QWaylandDataDevice *device)
533{
534 mDataDevice = device;
535}
536
537QWaylandDataDevice *QWaylandInputDevice::dataDevice() const
538{
539 return mDataDevice;
540}
541#endif
542
543#if QT_CONFIG(wayland_client_primary_selection)
544void QWaylandInputDevice::setPrimarySelectionDevice(QWaylandPrimarySelectionDeviceV1 *primarySelectionDevice)
545{
546 mPrimarySelectionDevice.reset(other: primarySelectionDevice);
547}
548
549QWaylandPrimarySelectionDeviceV1 *QWaylandInputDevice::primarySelectionDevice() const
550{
551 return mPrimarySelectionDevice.data();
552}
553#endif
554
555void QWaylandInputDevice::setTextInput(QWaylandTextInputInterface *textInput)
556{
557 mTextInput.reset(other: textInput);
558}
559
560void QWaylandInputDevice::setTextInputMethod(QWaylandTextInputMethod *textInputMethod)
561{
562 mTextInputMethod.reset(other: textInputMethod);
563}
564
565QWaylandTextInputInterface *QWaylandInputDevice::textInput() const
566{
567 return mTextInput.data();
568}
569
570QWaylandTextInputMethod *QWaylandInputDevice::textInputMethod() const
571{
572 return mTextInputMethod.data();
573}
574
575void QWaylandInputDevice::removeMouseButtonFromState(Qt::MouseButton button)
576{
577 if (mPointer)
578 mPointer->mButtons = mPointer->mButtons & !button;
579}
580
581QWaylandWindow *QWaylandInputDevice::pointerFocus() const
582{
583 return mPointer ? mPointer->focusWindow() : nullptr;
584}
585
586QWaylandWindow *QWaylandInputDevice::keyboardFocus() const
587{
588 return mKeyboard ? mKeyboard->focusWindow() : nullptr;
589}
590
591QWaylandWindow *QWaylandInputDevice::touchFocus() const
592{
593 return mTouch ? mTouch->mFocus : nullptr;
594}
595
596QPointF QWaylandInputDevice::pointerSurfacePosition() const
597{
598 return mPointer ? mPointer->mSurfacePos : QPointF();
599}
600
601QList<int> QWaylandInputDevice::possibleKeys(const QKeyEvent *event) const
602{
603#if QT_CONFIG(xkbcommon)
604 if (mKeyboard && mKeyboard->mXkbState)
605 return QXkbCommon::possibleKeys(state: mKeyboard->mXkbState.get(), event);
606#else
607 Q_UNUSED(event);
608#endif
609 return {};
610}
611
612Qt::KeyboardModifiers QWaylandInputDevice::modifiers() const
613{
614 if (!mKeyboard)
615 return Qt::NoModifier;
616
617 return mKeyboard->modifiers();
618}
619
620Qt::KeyboardModifiers QWaylandInputDevice::Keyboard::modifiers() const
621{
622 Qt::KeyboardModifiers ret = Qt::NoModifier;
623
624#if QT_CONFIG(xkbcommon)
625 if (!mXkbState)
626 return ret;
627
628 ret = QXkbCommon::modifiers(state: mXkbState.get());
629#endif
630
631 return ret;
632}
633
634#if QT_CONFIG(cursor)
635void QWaylandInputDevice::setCursor(const QCursor *cursor, const QSharedPointer<QWaylandBuffer> &cachedBuffer, int fallbackOutputScale)
636{
637 CursorState oldCursor = mCursor;
638 mCursor = CursorState(); // Clear any previous state
639 mCursor.shape = cursor ? cursor->shape() : Qt::ArrowCursor;
640 mCursor.hotspot = cursor ? cursor->hotSpot() : QPoint();
641 mCursor.fallbackOutputScale = fallbackOutputScale;
642 mCursor.animationTimer.start();
643
644 if (mCursor.shape == Qt::BitmapCursor) {
645 mCursor.bitmapBuffer = cachedBuffer ? cachedBuffer : QWaylandCursor::cursorBitmapBuffer(display: mQDisplay, cursor);
646 qreal dpr = cursor->pixmap().devicePixelRatio();
647 mCursor.bitmapScale = int(dpr); // Wayland doesn't support fractional buffer scale
648 // If there was a fractional part of the dpr, we need to scale the hotspot accordingly
649 if (mCursor.bitmapScale < dpr)
650 mCursor.hotspot *= dpr / mCursor.bitmapScale;
651 }
652
653 // Return early if setCursor was called redundantly (mostly happens from decorations)
654 if (mCursor.shape != Qt::BitmapCursor
655 && mCursor.shape == oldCursor.shape
656 && mCursor.hotspot == oldCursor.hotspot
657 && mCursor.fallbackOutputScale == oldCursor.fallbackOutputScale) {
658 return;
659 }
660
661 if (mPointer)
662 mPointer->updateCursor();
663}
664#endif
665
666class EnterEvent : public QWaylandPointerEvent
667{
668public:
669 EnterEvent(QWaylandWindow *surface, const QPointF &local, const QPointF &global)
670 : QWaylandPointerEvent(QEvent::Enter, Qt::NoScrollPhase, surface, 0,
671 local, global, Qt::NoButton, Qt::NoButton, Qt::NoModifier)
672 {}
673};
674
675void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surface *surface,
676 wl_fixed_t sx, wl_fixed_t sy)
677{
678 if (!surface)
679 return;
680
681 QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
682
683 if (!window)
684 return; // Ignore foreign surfaces
685
686 if (mFocus) {
687 qCWarning(lcQpaWayland) << "The compositor sent a wl_pointer.enter event before sending a"
688 << "leave event first, this is not allowed by the wayland protocol"
689 << "attempting to work around it by invalidating the current focus";
690 invalidateFocus();
691 }
692 mFocus = window->waylandSurface();
693 connect(mFocus.data(), &QObject::destroyed, this, &Pointer::handleFocusDestroyed);
694
695 mSurfacePos = QPointF(wl_fixed_to_double(sx), wl_fixed_to_double(sy));
696 mGlobalPos = window->mapToGlobal(mSurfacePos.toPoint());
697
698 mParent->mSerial = serial;
699 mEnterSerial = serial;
700
701#if QT_CONFIG(cursor)
702 // Depends on mEnterSerial being updated
703 updateCursor();
704#endif
705
706 QWaylandWindow *grab = QWaylandWindow::mouseGrab();
707 if (!grab)
708 setFrameEvent(new EnterEvent(window, mSurfacePos, mGlobalPos));
709}
710
711class LeaveEvent : public QWaylandPointerEvent
712{
713public:
714 LeaveEvent(QWaylandWindow *surface, const QPointF &localPos, const QPointF &globalPos)
715 : QWaylandPointerEvent(QEvent::Leave, Qt::NoScrollPhase, surface, 0,
716 localPos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier)
717 {}
718};
719
720void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surface *surface)
721{
722 invalidateFocus();
723 mButtons = Qt::NoButton;
724
725 mParent->mTime = time;
726
727 // The event may arrive after destroying the window, indicated by
728 // a null surface.
729 if (!surface)
730 return;
731
732 auto *window = QWaylandWindow::fromWlSurface(surface);
733 if (!window)
734 return; // Ignore foreign surfaces
735
736 if (!QWaylandWindow::mouseGrab())
737 setFrameEvent(new LeaveEvent(window, mSurfacePos, mGlobalPos));
738}
739
740class MotionEvent : public QWaylandPointerEvent
741{
742public:
743 MotionEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
744 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
745 : QWaylandPointerEvent(QEvent::MouseMove, Qt::NoScrollPhase, surface,
746 timestamp, localPos, globalPos, buttons, Qt::NoButton, modifiers)
747 {
748 }
749};
750
751void QWaylandInputDevice::Pointer::pointer_motion(uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y)
752{
753 QWaylandWindow *window = focusWindow();
754 if (!window) {
755 // We destroyed the pointer focus surface, but the server didn't get the message yet...
756 // or the server didn't send an enter event first. In either case, ignore the event.
757 return;
758 }
759
760 QPointF pos(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y));
761 QPointF delta = pos - pos.toPoint();
762 QPointF global = window->mapToGlobal(pos.toPoint());
763 global += delta;
764
765 mSurfacePos = pos;
766 mGlobalPos = global;
767 mParent->mTime = time;
768
769 QWaylandWindow *grab = QWaylandWindow::mouseGrab();
770 if (grab && grab != window) {
771 // We can't know the true position since we're getting events for another surface,
772 // so we just set it outside of the window boundaries.
773 pos = QPointF(-1, -1);
774 global = grab->mapToGlobal(pos.toPoint());
775 window = grab;
776 }
777 setFrameEvent(new MotionEvent(window, time, pos, global, mButtons, mParent->modifiers()));
778}
779
780class PressEvent : public QWaylandPointerEvent
781{
782public:
783 PressEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
784 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button,
785 Qt::KeyboardModifiers modifiers)
786 : QWaylandPointerEvent(QEvent::MouseButtonPress, Qt::NoScrollPhase, surface,
787 timestamp, localPos, globalPos, buttons, button, modifiers)
788 {
789 }
790};
791
792class ReleaseEvent : public QWaylandPointerEvent
793{
794public:
795 ReleaseEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
796 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button,
797 Qt::KeyboardModifiers modifiers)
798 : QWaylandPointerEvent(QEvent::MouseButtonRelease, Qt::NoScrollPhase, surface,
799 timestamp, localPos, globalPos, buttons, button, modifiers)
800 {
801 }
802};
803
804void QWaylandInputDevice::Pointer::pointer_button(uint32_t serial, uint32_t time,
805 uint32_t button, uint32_t state)
806{
807 QWaylandWindow *window = focusWindow();
808 if (!window) {
809 // We destroyed the pointer focus surface, but the server didn't get the message yet...
810 // or the server didn't send an enter event first. In either case, ignore the event.
811 return;
812 }
813
814 Qt::MouseButton qt_button;
815
816 // translate from kernel (input.h) 'button' to corresponding Qt:MouseButton.
817 // The range of mouse values is 0x110 <= mouse_button < 0x120, the first Joystick button.
818 switch (button) {
819 case 0x110: qt_button = Qt::LeftButton; break; // kernel BTN_LEFT
820 case 0x111: qt_button = Qt::RightButton; break;
821 case 0x112: qt_button = Qt::MiddleButton; break;
822 case 0x113: qt_button = Qt::ExtraButton1; break; // AKA Qt::BackButton
823 case 0x114: qt_button = Qt::ExtraButton2; break; // AKA Qt::ForwardButton
824 case 0x115: qt_button = Qt::ExtraButton3; break; // AKA Qt::TaskButton
825 case 0x116: qt_button = Qt::ExtraButton4; break;
826 case 0x117: qt_button = Qt::ExtraButton5; break;
827 case 0x118: qt_button = Qt::ExtraButton6; break;
828 case 0x119: qt_button = Qt::ExtraButton7; break;
829 case 0x11a: qt_button = Qt::ExtraButton8; break;
830 case 0x11b: qt_button = Qt::ExtraButton9; break;
831 case 0x11c: qt_button = Qt::ExtraButton10; break;
832 case 0x11d: qt_button = Qt::ExtraButton11; break;
833 case 0x11e: qt_button = Qt::ExtraButton12; break;
834 case 0x11f: qt_button = Qt::ExtraButton13; break;
835 default: return; // invalid button number (as far as Qt is concerned)
836 }
837
838 if (state)
839 mButtons |= qt_button;
840 else
841 mButtons &= ~qt_button;
842
843 mParent->mTime = time;
844 mParent->mSerial = serial;
845 if (state)
846 mParent->mQDisplay->setLastInputDevice(device: mParent, serial, window);
847
848 QWaylandWindow *grab = QWaylandWindow::mouseGrab();
849
850 QPointF pos = mSurfacePos;
851 QPointF global = mGlobalPos;
852 if (grab && grab != focusWindow()) {
853 pos = QPointF(-1, -1);
854 global = grab->mapToGlobal(pos.toPoint());
855
856 window = grab;
857 }
858
859 if (state)
860 setFrameEvent(new PressEvent(window, time, pos, global, mButtons, qt_button, mParent->modifiers()));
861 else
862 setFrameEvent(new ReleaseEvent(window, time, pos, global, mButtons, qt_button, mParent->modifiers()));
863}
864
865void QWaylandInputDevice::Pointer::invalidateFocus()
866{
867 if (mFocus) {
868 disconnect(sender: mFocus.data(), signal: &QObject::destroyed, receiver: this, slot: &Pointer::handleFocusDestroyed);
869 mFocus = nullptr;
870 }
871 mEnterSerial = 0;
872}
873
874void QWaylandInputDevice::Pointer::releaseButtons()
875{
876 mButtons = Qt::NoButton;
877
878 if (auto *window = focusWindow()) {
879 ReleaseEvent e(focusWindow(), mParent->mTime, mSurfacePos, mGlobalPos, mButtons, Qt::NoButton, mParent->modifiers());
880 window->handleMouse(inputDevice: mParent, e);
881 }
882}
883
884class WheelEvent : public QWaylandPointerEvent
885{
886public:
887 WheelEvent(QWaylandWindow *surface, Qt::ScrollPhase phase, ulong timestamp, const QPointF &local,
888 const QPointF &global, const QPoint &pixelDelta, const QPoint &angleDelta,
889 Qt::MouseEventSource source, Qt::KeyboardModifiers modifiers)
890 : QWaylandPointerEvent(QEvent::Wheel, phase, surface, timestamp,
891 local, global, pixelDelta, angleDelta, source, modifiers)
892 {
893 }
894};
895
896void QWaylandInputDevice::Pointer::pointer_axis(uint32_t time, uint32_t axis, int32_t value)
897{
898 if (!focusWindow()) {
899 // We destroyed the pointer focus surface, but the server didn't get the message yet...
900 // or the server didn't send an enter event first. In either case, ignore the event.
901 return;
902 }
903
904 // Get the delta and convert it into the expected range
905 switch (axis) {
906 case WL_POINTER_AXIS_VERTICAL_SCROLL:
907 mFrameData.delta.ry() += wl_fixed_to_double(value);
908 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis vertical:" << mFrameData.delta.y();
909 break;
910 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
911 mFrameData.delta.rx() += wl_fixed_to_double(value);
912 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis horizontal:" << mFrameData.delta.x();
913 break;
914 default:
915 //TODO: is this really needed?
916 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis: Unknown axis:" << axis;
917 return;
918 }
919
920 mParent->mTime = time;
921
922 if (version() < WL_POINTER_FRAME_SINCE_VERSION) {
923 qCDebug(lcQpaWaylandInput) << "Flushing new event; no frame event in this version";
924 flushFrameEvent();
925 }
926}
927
928void QWaylandInputDevice::Pointer::pointer_frame()
929{
930 flushFrameEvent();
931}
932
933void QWaylandInputDevice::Pointer::pointer_axis_source(uint32_t source)
934{
935 switch (source) {
936 case axis_source_wheel:
937 qCDebug(lcQpaWaylandInput) << "Axis source wheel";
938 break;
939 case axis_source_finger:
940 qCDebug(lcQpaWaylandInput) << "Axis source finger";
941 break;
942 case axis_source_continuous:
943 qCDebug(lcQpaWaylandInput) << "Axis source continuous";
944 break;
945 case axis_source_wheel_tilt:
946 qCDebug(lcQpaWaylandInput) << "Axis source wheel tilt";
947 }
948 mFrameData.axisSource = axis_source(source);
949}
950
951void QWaylandInputDevice::Pointer::pointer_axis_stop(uint32_t time, uint32_t axis)
952{
953 if (!focusWindow())
954 return;
955
956 mParent->mTime = time;
957 switch (axis) {
958 case axis_vertical_scroll:
959 qCDebug(lcQpaWaylandInput) << "Received vertical wl_pointer.axis_stop";
960 mFrameData.delta.setY(0); //TODO: what's the point of doing this?
961 break;
962 case axis_horizontal_scroll:
963 qCDebug(lcQpaWaylandInput) << "Received horizontal wl_pointer.axis_stop";
964 mFrameData.delta.setX(0);
965 break;
966 default:
967 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_stop: Unknown axis: " << axis
968 << "This is most likely a compositor bug";
969 return;
970 }
971
972 // May receive axis_stop for events we haven't sent a ScrollBegin for because
973 // most axis_sources do not mandate an axis_stop event to be sent.
974 if (!mScrollBeginSent) {
975 // TODO: For now, we just ignore these events, but we could perhaps take this as an
976 // indication that this compositor will in fact send axis_stop events for these sources
977 // and send a ScrollBegin the next time an axis_source event with this type is encountered.
978 return;
979 }
980
981 QWaylandWindow *target = QWaylandWindow::mouseGrab();
982 if (!target)
983 target = focusWindow();
984 Qt::KeyboardModifiers mods = mParent->modifiers();
985 WheelEvent wheelEvent(focusWindow(), Qt::ScrollEnd, mParent->mTime, mSurfacePos, mGlobalPos,
986 QPoint(), QPoint(), Qt::MouseEventNotSynthesized, mods);
987 target->handleMouse(inputDevice: mParent, e: wheelEvent);
988 mScrollBeginSent = false;
989 mScrollDeltaRemainder = QPointF();
990}
991
992void QWaylandInputDevice::Pointer::pointer_axis_discrete(uint32_t axis, int32_t value)
993{
994 if (!focusWindow())
995 return;
996
997 const int32_t delta120 = value * 15 * 8;
998
999 switch (axis) {
1000 case axis_vertical_scroll:
1001 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete vertical:" << value;
1002 mFrameData.delta120.ry() += delta120;
1003 break;
1004 case axis_horizontal_scroll:
1005 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete horizontal:" << value;
1006 mFrameData.delta120.rx() += delta120;
1007 break;
1008 default:
1009 //TODO: is this really needed?
1010 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_discrete: Unknown axis:" << axis;
1011 return;
1012 }
1013}
1014
1015void QWaylandInputDevice::Pointer::pointer_axis_value120(uint32_t axis, int32_t value)
1016{
1017 if (!focusWindow())
1018 return;
1019
1020 switch (axis) {
1021 case axis_vertical_scroll:
1022 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_value120 vertical:" << value;
1023 mFrameData.delta120.ry() += value;
1024 break;
1025 case axis_horizontal_scroll:
1026 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_value120 horizontal:" << value;
1027 mFrameData.delta120.rx() += value;
1028 break;
1029 default:
1030 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_value120: Unknown axis:" << axis;
1031 return;
1032 }
1033}
1034
1035void QWaylandInputDevice::Pointer::setFrameEvent(QWaylandPointerEvent *event)
1036{
1037 qCDebug(lcQpaWaylandInput) << "Setting frame event " << event->type;
1038 if (mFrameData.event && mFrameData.event->type != event->type) {
1039 qCDebug(lcQpaWaylandInput) << "Flushing; previous was " << mFrameData.event->type;
1040 flushFrameEvent();
1041 }
1042
1043 mFrameData.event = event;
1044
1045 if (version() < WL_POINTER_FRAME_SINCE_VERSION) {
1046 qCDebug(lcQpaWaylandInput) << "Flushing new event; no frame event in this version";
1047 flushFrameEvent();
1048 }
1049}
1050
1051void QWaylandInputDevice::Pointer::FrameData::resetScrollData()
1052{
1053 delta120 = QPoint();
1054 delta = QPointF();
1055 axisSource = axis_source_wheel;
1056}
1057
1058bool QWaylandInputDevice::Pointer::FrameData::hasPixelDelta() const
1059{
1060 switch (axisSource) {
1061 case axis_source_wheel_tilt: // sideways tilt of the wheel
1062 case axis_source_wheel:
1063 // In the case of wheel events, a pixel delta doesn't really make sense,
1064 // and will make Qt think this is a continuous scroll event when it isn't,
1065 // so just ignore it.
1066 return false;
1067 case axis_source_finger:
1068 case axis_source_continuous:
1069 return !delta.isNull();
1070 default:
1071 return false;
1072 }
1073}
1074
1075QPoint QWaylandInputDevice::Pointer::FrameData::pixelDeltaAndError(QPointF *accumulatedError) const
1076{
1077 if (!hasPixelDelta())
1078 return QPoint();
1079
1080 Q_ASSERT(accumulatedError);
1081 // Add accumulated rounding error before rounding again
1082 QPoint pixelDelta = (delta + *accumulatedError).toPoint();
1083 *accumulatedError += delta - pixelDelta;
1084 Q_ASSERT(qAbs(accumulatedError->x()) < 1.0);
1085 Q_ASSERT(qAbs(accumulatedError->y()) < 1.0);
1086
1087 // for continuous scroll events things should be
1088 // in the same direction
1089 // i.e converted so downwards surface co-ordinates (positive axis_value)
1090 // goes to downwards in wheel event (negative value)
1091 pixelDelta *= -1;
1092 return pixelDelta;
1093}
1094
1095QPoint QWaylandInputDevice::Pointer::FrameData::angleDelta() const
1096{
1097 if (delta120.isNull()) {
1098 // If we didn't get any discrete events, then we need to fall back to
1099 // the continuous information.
1100 return (delta * -12).toPoint(); //TODO: why multiply by 12?
1101 }
1102
1103 // The angle delta is in eights of degrees, and our docs says most mice have
1104 // 1 click = 15 degrees, i.e. 120 is one click. It's also in the opposite
1105 // direction of surface space.
1106 return -delta120;
1107}
1108
1109Qt::MouseEventSource QWaylandInputDevice::Pointer::FrameData::wheelEventSource() const
1110{
1111 switch (axisSource) {
1112 case axis_source_wheel_tilt: // sideways tilt of the wheel
1113 case axis_source_wheel:
1114 return Qt::MouseEventNotSynthesized;
1115 case axis_source_finger:
1116 case axis_source_continuous:
1117 default: // Whatever other sources might be added are probably not mouse wheels
1118 return Qt::MouseEventSynthesizedBySystem;
1119 }
1120}
1121
1122void QWaylandInputDevice::Pointer::flushScrollEvent()
1123{
1124 QPoint angleDelta = mFrameData.angleDelta();
1125
1126 // Angle delta is required for Qt wheel events, so don't try to send events if it's zero
1127 if (!angleDelta.isNull()) {
1128 QWaylandWindow *target = QWaylandWindow::mouseGrab();
1129 if (!target)
1130 target = focusWindow();
1131
1132 if (isDefinitelyTerminated(mFrameData.axisSource) && !mScrollBeginSent) {
1133 qCDebug(lcQpaWaylandInput) << "Flushing scroll event sending ScrollBegin";
1134 target->handleMouse(inputDevice: mParent, e: WheelEvent(focusWindow(), Qt::ScrollBegin, mParent->mTime,
1135 mSurfacePos, mGlobalPos, QPoint(), QPoint(),
1136 Qt::MouseEventNotSynthesized,
1137 mParent->modifiers()));
1138 mScrollBeginSent = true;
1139 mScrollDeltaRemainder = QPointF();
1140 }
1141
1142 Qt::ScrollPhase phase = mScrollBeginSent ? Qt::ScrollUpdate : Qt::NoScrollPhase;
1143 QPoint pixelDelta = mFrameData.pixelDeltaAndError(&mScrollDeltaRemainder);
1144 Qt::MouseEventSource source = mFrameData.wheelEventSource();
1145
1146 qCDebug(lcQpaWaylandInput) << "Flushing scroll event" << phase << pixelDelta << angleDelta;
1147 target->handleMouse(inputDevice: mParent, e: WheelEvent(focusWindow(), phase, mParent->mTime, mSurfacePos, mGlobalPos,
1148 pixelDelta, angleDelta, source, mParent->modifiers()));
1149 }
1150
1151 mFrameData.resetScrollData();
1152}
1153
1154void QWaylandInputDevice::Pointer::flushFrameEvent()
1155{
1156 if (auto *event = mFrameData.event) {
1157 if (auto window = event->surface) {
1158 window->handleMouse(mParent, *event);
1159 } else if (mFrameData.event->type == QEvent::MouseButtonRelease) {
1160 // If the window has been destroyed, we still need to report an up event, but it can't
1161 // be handled by the destroyed window (obviously), so send the event here instead.
1162 QWindowSystemInterface::handleMouseEvent(nullptr, event->timestamp, event->local,
1163 event->global, event->buttons,
1164 event->button, event->type,
1165 event->modifiers);// , Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
1166 }
1167 delete mFrameData.event;
1168 mFrameData.event = nullptr;
1169 }
1170
1171 //TODO: do modifiers get passed correctly here?
1172 flushScrollEvent();
1173}
1174
1175bool QWaylandInputDevice::Pointer::isDefinitelyTerminated(QtWayland::wl_pointer::axis_source source) const
1176{
1177 return source == axis_source_finger;
1178}
1179
1180void QWaylandInputDevice::Keyboard::keyboard_keymap(uint32_t format, int32_t fd, uint32_t size)
1181{
1182 mKeymapFormat = format;
1183#if QT_CONFIG(xkbcommon)
1184 if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
1185 qCWarning(lcQpaWayland) << "unknown keymap format:" << format;
1186 close(fd: fd);
1187 return;
1188 }
1189
1190 char *map_str = static_cast<char *>(mmap(addr: nullptr, len: size, PROT_READ, MAP_PRIVATE, fd: fd, offset: 0));
1191 if (map_str == MAP_FAILED) {
1192 close(fd: fd);
1193 return;
1194 }
1195
1196 mXkbKeymap.reset(p: xkb_keymap_new_from_string(context: mParent->mQDisplay->xkbContext(), string: map_str,
1197 format: XKB_KEYMAP_FORMAT_TEXT_V1,
1198 flags: XKB_KEYMAP_COMPILE_NO_FLAGS));
1199 QXkbCommon::verifyHasLatinLayout(keymap: mXkbKeymap.get());
1200
1201 munmap(addr: map_str, len: size);
1202 close(fd: fd);
1203
1204 if (mXkbKeymap)
1205 mXkbState.reset(p: xkb_state_new(keymap: mXkbKeymap.get()));
1206 else
1207 mXkbState.reset(p: nullptr);
1208#else
1209 Q_UNUSED(fd);
1210 Q_UNUSED(size);
1211#endif
1212}
1213
1214void QWaylandInputDevice::Keyboard::keyboard_enter(uint32_t time, struct wl_surface *surface, struct wl_array *keys)
1215{
1216 Q_UNUSED(time);
1217 Q_UNUSED(keys);
1218
1219 if (!surface) {
1220 // Ignoring wl_keyboard.enter event with null surface. This is either a compositor bug,
1221 // or it's a race with a wl_surface.destroy request. In either case, ignore the event.
1222 return;
1223 }
1224
1225 QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
1226 if (!window)
1227 return;
1228
1229 if (mFocus) {
1230 qCWarning(lcQpaWayland()) << "Unexpected wl_keyboard.enter event. Keyboard already has focus";
1231 disconnect(sender: mFocus, signal: &QWaylandSurface::destroyed, receiver: this, slot: &Keyboard::handleFocusDestroyed);
1232 }
1233
1234 mFocus = window->waylandSurface();
1235 connect(sender: mFocus, signal: &QWaylandSurface::destroyed, context: this, slot: &Keyboard::handleFocusDestroyed);
1236
1237 mParent->mQDisplay->handleKeyboardFocusChanged(inputDevice: mParent);
1238}
1239
1240void QWaylandInputDevice::Keyboard::keyboard_leave(uint32_t time, struct wl_surface *surface)
1241{
1242 Q_UNUSED(time);
1243
1244 if (!surface) {
1245 // Either a compositor bug, or a race condition with wl_surface.destroy, ignore the event.
1246 return;
1247 }
1248
1249 QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
1250 if (!window)
1251 return;
1252
1253 if (window->waylandSurface() != mFocus) {
1254 qCWarning(lcQpaWayland) << "Ignoring unexpected wl_keyboard.leave event."
1255 << "wl_surface argument does not match the current focus"
1256 << "This is most likely a compositor bug";
1257 return;
1258 }
1259 disconnect(sender: mFocus, signal: &QWaylandSurface::destroyed, receiver: this, slot: &Keyboard::handleFocusDestroyed);
1260 handleFocusLost();
1261}
1262
1263void QWaylandInputDevice::Keyboard::handleKey(ulong timestamp, QEvent::Type type, int key,
1264 Qt::KeyboardModifiers modifiers, quint32 nativeScanCode,
1265 quint32 nativeVirtualKey, quint32 nativeModifiers,
1266 const QString &text, bool autorepeat, ushort count)
1267{
1268 QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
1269 bool filtered = false;
1270
1271 if (inputContext) {
1272 QKeyEvent event(type, key, modifiers, nativeScanCode, nativeVirtualKey,
1273 nativeModifiers, text, autorepeat, count);
1274 event.setTimestamp(timestamp);
1275 filtered = inputContext->filterEvent(event: &event);
1276 }
1277
1278 if (!filtered) {
1279 auto window = focusWindow()->window();
1280
1281 if (type == QEvent::KeyPress && key == Qt::Key_Menu) {
1282 auto cursor = window->screen()->handle()->cursor();
1283 if (cursor) {
1284 const QPoint globalPos = cursor->pos();
1285 const QPoint pos = window->mapFromGlobal(globalPos);
1286 QWindowSystemInterface::handleContextMenuEvent(window: window, mouseTriggered: false, pos, globalPos, modifiers);
1287 }
1288 }
1289
1290 QWindowSystemInterface::handleExtendedKeyEvent(window, timestamp, type, key, modifiers,
1291 nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorepeat, count);
1292 }
1293}
1294
1295void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
1296{
1297 if (mKeymapFormat != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 && mKeymapFormat != WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP) {
1298 qCWarning(lcQpaWayland) << Q_FUNC_INFO << "unknown keymap format:" << mKeymapFormat;
1299 return;
1300 }
1301
1302 auto *window = focusWindow();
1303 if (!window) {
1304 // We destroyed the keyboard focus surface, but the server didn't get the message yet...
1305 // or the server didn't send an enter event first. In either case, ignore the event.
1306 return;
1307 }
1308
1309 mParent->mSerial = serial;
1310
1311 const bool isDown = state != WL_KEYBOARD_KEY_STATE_RELEASED;
1312 if (isDown)
1313 mParent->mQDisplay->setLastInputDevice(device: mParent, serial, window);
1314
1315 if (mKeymapFormat == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
1316#if QT_CONFIG(xkbcommon)
1317 if ((!mXkbKeymap || !mXkbState) && !createDefaultKeymap())
1318 return;
1319
1320 auto code = key + 8; // map to wl_keyboard::keymap_format::keymap_format_xkb_v1
1321
1322 xkb_keysym_t sym = xkb_state_key_get_one_sym(state: mXkbState.get(), key: code);
1323
1324 Qt::KeyboardModifiers modifiers = mParent->modifiers();
1325
1326 int qtkey = keysymToQtKey(keysym: sym, modifiers, state: mXkbState.get(), code);
1327 QString text = QXkbCommon::lookupString(state: mXkbState.get(), code);
1328
1329 QEvent::Type type = isDown ? QEvent::KeyPress : QEvent::KeyRelease;
1330 handleKey(timestamp: time, type, key: qtkey, modifiers, nativeScanCode: code, nativeVirtualKey: sym, nativeModifiers: mNativeModifiers, text);
1331
1332 if (state == WL_KEYBOARD_KEY_STATE_PRESSED && xkb_keymap_key_repeats(mXkbKeymap.get(), code) && mRepeatRate > 0) {
1333 mRepeatKey.key = qtkey;
1334 mRepeatKey.code = code;
1335 mRepeatKey.time = time;
1336 mRepeatKey.text = text;
1337 mRepeatKey.modifiers = modifiers;
1338 mRepeatKey.nativeModifiers = mNativeModifiers;
1339 mRepeatKey.nativeVirtualKey = sym;
1340 mRepeatTimer.setInterval(mRepeatDelay);
1341 mRepeatTimer.start();
1342 } else if (mRepeatKey.code == code) {
1343 mRepeatTimer.stop();
1344 }
1345#else
1346 Q_UNUSED(time);
1347 Q_UNUSED(key);
1348 qCWarning(lcQpaWayland, "xkbcommon not available on this build, not performing key mapping");
1349 return;
1350#endif
1351 } else if (mKeymapFormat == WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP) {
1352 // raw scan code
1353 return;
1354 }
1355}
1356
1357void QWaylandInputDevice::Keyboard::handleFocusDestroyed()
1358{
1359 handleFocusLost();
1360}
1361
1362void QWaylandInputDevice::Keyboard::handleFocusLost()
1363{
1364 mFocus = nullptr;
1365 mParent->mQDisplay->handleKeyboardFocusChanged(inputDevice: mParent);
1366 mRepeatTimer.stop();
1367}
1368
1369void QWaylandInputDevice::Keyboard::keyboard_modifiers(uint32_t serial,
1370 uint32_t mods_depressed,
1371 uint32_t mods_latched,
1372 uint32_t mods_locked,
1373 uint32_t group)
1374{
1375 Q_UNUSED(serial);
1376#if QT_CONFIG(xkbcommon)
1377 if (mXkbState)
1378 xkb_state_update_mask(state: mXkbState.get(),
1379 depressed_mods: mods_depressed, latched_mods: mods_latched, locked_mods: mods_locked,
1380 depressed_layout: 0, latched_layout: 0, locked_layout: group);
1381 mNativeModifiers = mods_depressed | mods_latched | mods_locked;
1382#else
1383 Q_UNUSED(mods_depressed);
1384 Q_UNUSED(mods_latched);
1385 Q_UNUSED(mods_locked);
1386 Q_UNUSED(group);
1387#endif
1388}
1389
1390void QWaylandInputDevice::Keyboard::keyboard_repeat_info(int32_t rate, int32_t delay)
1391{
1392 mRepeatRate = rate;
1393 mRepeatDelay = delay;
1394}
1395
1396void QWaylandInputDevice::Touch::touch_down(uint32_t serial,
1397 uint32_t time,
1398 struct wl_surface *surface,
1399 int32_t id,
1400 wl_fixed_t x,
1401 wl_fixed_t y)
1402{
1403 if (!surface)
1404 return;
1405
1406 auto *window = QWaylandWindow::fromWlSurface(surface);
1407 if (!window)
1408 return; // Ignore foreign surfaces
1409
1410 mParent->mTime = time;
1411 mParent->mSerial = serial;
1412 mFocus = window;
1413 mParent->mQDisplay->setLastInputDevice(device: mParent, serial, window: mFocus);
1414 QPointF position(wl_fixed_to_double(x), wl_fixed_to_double(y));
1415 mParent->handleTouchPoint(id, state: QEventPoint::Pressed, surfacePosition: position);
1416}
1417
1418void QWaylandInputDevice::Touch::touch_up(uint32_t serial, uint32_t time, int32_t id)
1419{
1420 Q_UNUSED(serial);
1421 mParent->mTime = time;
1422 mParent->handleTouchPoint(id, state: QEventPoint::Released);
1423
1424 if (allTouchPointsReleased()) {
1425 mFocus = nullptr;
1426
1427 // As of Weston 7.0.0 there is no touch_frame after the last touch_up
1428 // (i.e. when the last finger is released). To accommodate for this, issue a
1429 // touch_frame. This cannot hurt since it is safe to call the touch_frame
1430 // handler multiple times when there are no points left.
1431 // See: https://gitlab.freedesktop.org/wayland/weston/issues/44
1432 // TODO: change logging category to lcQpaWaylandInput in newer versions.
1433 qCDebug(lcQpaWayland, "Generating fake frame event to work around Weston bug");
1434 touch_frame();
1435 }
1436}
1437
1438void QWaylandInputDevice::Touch::touch_motion(uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y)
1439{
1440 QPointF position(wl_fixed_to_double(x), wl_fixed_to_double(y));
1441 mParent->mTime = time;
1442 mParent->handleTouchPoint(id, state: QEventPoint::Updated, surfacePosition: position);
1443}
1444
1445void QWaylandInputDevice::Touch::touch_cancel()
1446{
1447 mPendingTouchPoints.clear();
1448
1449 QWaylandTouchExtension *touchExt = mParent->mQDisplay->touchExtension();
1450 if (touchExt)
1451 touchExt->touchCanceled();
1452
1453 mFocus = nullptr;
1454 QWindowSystemInterface::handleTouchCancelEvent(window: nullptr, device: mParent->mTouchDevice);
1455}
1456
1457void QWaylandInputDevice::handleTouchPoint(int id, QEventPoint::State state, const QPointF &surfacePosition)
1458{
1459 auto end = mTouch->mPendingTouchPoints.end();
1460 auto it = std::find_if(first: mTouch->mPendingTouchPoints.begin(), last: end, pred: [id](const QWindowSystemInterface::TouchPoint &tp){ return tp.id == id; });
1461 if (it == end) {
1462 it = mTouch->mPendingTouchPoints.insert(before: end, t: QWindowSystemInterface::TouchPoint());
1463 it->id = id;
1464 }
1465 // If the touch points were up and down in same frame, send out frame right away
1466 else if ((it->state == QEventPoint::Pressed && state == QEventPoint::Released)
1467 || (it->state == QEventPoint::Released && state == QEventPoint::Pressed)) {
1468 mTouch->touch_frame();
1469 it = mTouch->mPendingTouchPoints.insert(before: mTouch->mPendingTouchPoints.end(), t: QWindowSystemInterface::TouchPoint());
1470 it->id = id;
1471 }
1472
1473 QWindowSystemInterface::TouchPoint &tp = *it;
1474
1475 // Only moved and pressed needs to update/set position
1476 if (state == QEventPoint::Updated || state == QEventPoint::Pressed) {
1477 // We need a global (screen) position.
1478 QWaylandWindow *win = mTouch->mFocus;
1479
1480 //is it possible that mTouchFocus is null;
1481 if (!win && mPointer)
1482 win = mPointer->focusWindow();
1483 if (!win && mKeyboard)
1484 win = mKeyboard->focusWindow();
1485 if (!win || !win->window())
1486 return;
1487
1488 tp.area = QRectF(0, 0, 8, 8);
1489 QPointF localPosition = win->mapFromWlSurface(surfacePosition);
1490 // TODO: This doesn't account for high dpi scaling for the delta, but at least it matches
1491 // what we have for mouse input.
1492 QPointF delta = localPosition - localPosition.toPoint();
1493 QPointF globalPosition = win->mapToGlobal(localPosition.toPoint()) + delta;
1494 tp.area.moveCenter(p: globalPosition);
1495 }
1496
1497 // If the touch point was pressed earlier this frame, we don't want to overwrite its state.
1498 if (tp.state != QEventPoint::Pressed)
1499 tp.state = QEventPoint::State(state);
1500
1501 tp.pressure = tp.state == QEventPoint::Released ? 0 : 1;
1502}
1503
1504bool QWaylandInputDevice::Touch::allTouchPointsReleased()
1505{
1506 for (const auto &tp : std::as_const(t&: mPendingTouchPoints)) {
1507 if (tp.state != QEventPoint::Released)
1508 return false;
1509 }
1510 return true;
1511}
1512
1513void QWaylandInputDevice::Touch::releasePoints()
1514{
1515 if (mPendingTouchPoints.empty())
1516 return;
1517
1518 for (QWindowSystemInterface::TouchPoint &tp : mPendingTouchPoints)
1519 tp.state = QEventPoint::Released;
1520
1521 touch_frame();
1522}
1523
1524void QWaylandInputDevice::Touch::touch_frame()
1525{
1526 // TODO: early return if no events?
1527
1528 QWindow *window = mFocus ? mFocus->window() : nullptr;
1529
1530 if (mFocus) {
1531 const QWindowSystemInterface::TouchPoint &tp = mPendingTouchPoints.last();
1532 // When the touch event is received, the global pos is calculated with the margins
1533 // in mind. Now we need to adjust again to get the correct local pos back.
1534 QMargins margins = window->frameMargins();
1535 QPoint p = tp.area.center().toPoint();
1536 QPointF localPos(window->mapFromGlobal(pos: QPoint(p.x() + margins.left(), p.y() + margins.top())));
1537 if (mFocus->touchDragDecoration(inputDevice: mParent, local: localPos, global: tp.area.center(), state: tp.state, mods: mParent->modifiers()))
1538 return;
1539 }
1540
1541 QWindowSystemInterface::handleTouchEvent(window, timestamp: mParent->mTime, device: mParent->mTouchDevice, points: mPendingTouchPoints, mods: mParent->modifiers());
1542
1543 // Prepare state for next frame
1544 const auto prevTouchPoints = mPendingTouchPoints;
1545 mPendingTouchPoints.clear();
1546 for (const auto &prevPoint: prevTouchPoints) {
1547 // All non-released touch points should be part of the next touch event
1548 if (prevPoint.state != QEventPoint::Released) {
1549 QWindowSystemInterface::TouchPoint tp = prevPoint;
1550 tp.state = QEventPoint::Stationary; // ... as stationary (unless proven otherwise)
1551 mPendingTouchPoints.append(t: tp);
1552 }
1553 }
1554
1555}
1556
1557}
1558
1559QT_END_NAMESPACE
1560
1561#include "moc_qwaylandinputdevice_p.cpp"
1562

source code of qtwayland/src/client/qwaylandinputdevice.cpp