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

source code of qtbase/src/plugins/platforms/wayland/qwaylandinputdevice.cpp