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