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