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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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