Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwaylandinputdevice.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2024 Jie Liu <liujie01@kylinos.cn>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
6
12#if QT_CONFIG(wayland_datadevice)
13#include "qwaylanddatadevice_p.h"
14#include "qwaylanddatadevicemanager_p.h"
15#endif
16#if QT_CONFIG(clipboard)
17#include "qwaylanddatacontrolv1_p.h"
18#endif
19#if QT_CONFIG(wayland_client_primary_selection)
20#include "qwaylandprimaryselectionv1_p.h"
21#endif
22#if QT_CONFIG(tabletevent)
23#include "qwaylandtabletv2_p.h"
24#endif
37
38#include <QtGui/private/qpixmap_raster_p.h>
39#include <QtGui/private/qguiapplication_p.h>
40#include <qpa/qplatformwindow.h>
41#include <qpa/qplatforminputcontext.h>
42#include <qpa/qplatformtheme.h>
43#include <QDebug>
44
45#include <unistd.h>
46#include <fcntl.h>
47#include <sys/mman.h>
48
49#if QT_CONFIG(cursor)
50#include <wayland-cursor.h>
51#endif
52
53#include <QtGui/QGuiApplication>
54#include <QtGui/QPointingDevice>
55
56QT_BEGIN_NAMESPACE
57
58namespace QtWaylandClient {
59
60Q_LOGGING_CATEGORY(lcQpaWaylandInput, "qt.qpa.wayland.input");
61
62// The maximum number of concurrent touchpoints is not exposed in wayland, so we assume a
63// reasonable number of them. As of 2021 most touchscreen panels support 10 concurrent touchpoints.
64static const int MaxTouchPoints = 10;
65
67{
68 timeElapsed.start();
69 delayTimer.setSingleShot(true);
70}
71
73{
74 using namespace std::chrono_literals;
75
76 if (!QCoreApplication::testAttribute(Qt::AA_CompressHighFrequencyEvents))
77 return false;
78
79 const auto elapsed = timeElapsed.durationElapsed();
80 timeElapsed.start();
81 if (elapsed < 100us || delayTimer.isActive())
82 {
83 // The highest USB HID polling rate is 8 kHz (125 μs). Most mice use lowe polling rate [125 Hz - 1000 Hz].
84 // Reject all events faster than 100 μs, because it definitely means the application main thread is
85 // freezed by long operation and events are delivered one after another from the queue. Since now we rely
86 // on the 0 ms timer to deliver the last pending event when application main thread is no longer freezed.
87 delayTimer.start(0);
88 return true;
89 }
90
91 return false;
92}
93
95 : mParent(p)
96{
98 mRepeatTimer.callOnTimeout(this, [&]() {
99 if (!focusWindow()) {
100 // We destroyed the keyboard focus surface, but the server didn't get the message yet...
101 // or the server didn't send an enter event first.
102 return;
103 }
108 mRepeatKey.text, true);
111 mRepeatKey.text, true);
112 });
113}
114
115#if QT_CONFIG(xkbcommon)
117{
119 if (!ctx)
120 return false;
121
122 struct xkb_rule_names names;
123 names.rules = "evdev";
124 names.model = "pc105";
125 names.layout = "us";
126 names.variant = "";
127 names.options = "";
128
130 if (mXkbKeymap)
132
133 if (!mXkbKeymap || !mXkbState) {
134 qCWarning(lcQpaWayland, "failed to create default keymap");
135 return false;
136 }
137
138 return true;
139}
140#endif
141
151
153{
154 return mFocus ? mFocus->waylandWindow() : nullptr;
155}
156
174
176{
177 if (version() >= 3)
179 else
181}
182
184{
185 return mFocus ? mFocus->waylandWindow() : nullptr;
186}
187
188#if QT_CONFIG(cursor)
189
191{
192 if (seat()->mQDisplay->compositor()->version() < 3) {
193 return 1;
194 }
195
196 if (auto *s = mCursor.surface.data()) {
197 if (s->outputScale() > 0)
198 return s->outputScale();
199 }
200
202}
203
205{
208
212 }
213
215 cursorThemeName = QStringLiteral("default");
216 if (cursorSize.isEmpty())
217 cursorSize = QSize(24, 24);
218
219 int scale = idealCursorScale();
221 auto *display = seat()->mQDisplay;
223
224 if (!mCursor.theme)
225 return; // A warning has already been printed in loadCursorTheme
226
227 if (auto *arrow = mCursor.theme->cursor(Qt::ArrowCursor)) {
228 int arrowPixelSize = qMax(arrow->images[0]->width, arrow->images[0]->height); // Not all cursor themes are square
229 while (scale > 1 && arrowPixelSize / scale < cursorSize.width())
230 --scale;
231 } else {
232 qCWarning(lcQpaWayland) << "Cursor theme does not support the arrow cursor";
233 }
235}
236
238{
239 if (mEnterSerial == 0)
240 return;
241
242 auto shape = seat()->mCursor.shape;
243
244 if (shape == Qt::BlankCursor) {
245 if (mCursor.surface)
247 set_cursor(mEnterSerial, nullptr, 0, 0);
248 return;
249 }
250
251 if (shape == Qt::BitmapCursor) {
253 if (!buffer) {
254 qCWarning(lcQpaWayland) << "No buffer for bitmap cursor, can't set cursor";
255 return;
256 }
257 auto hotspot = seat()->mCursor.hotspot;
260 return;
261 }
262
263 if (mCursor.shape) {
264 if (mCursor.surface) {
266 }
268 return;
269 }
270
273
274 if (!mCursor.theme)
275 return;
276
277 // Set from shape using theme
279 const uint time = timer.isValid() ? timer.elapsed() : 0;
280
281 if (struct ::wl_cursor *waylandCursor = mCursor.theme->cursor(shape)) {
282 uint duration = 0;
285
287 if (!buffer) {
288 qCWarning(lcQpaWayland) << "Could not find buffer for cursor" << shape;
289 return;
290 }
294 bool animated = duration > 0;
295 if (animated) {
299 }
301 return;
302 }
303
304 qCWarning(lcQpaWayland) << "Unable to change to cursor" << shape;
305}
306
309{
310 if (!mCursor.surface)
312 return mCursor.surface.get();
313}
314
316{
319 updateCursor();
320 }
321}
322
324{
327 updateCursor();
328 }
329}
330
331#endif // QT_CONFIG(cursor)
332
338
340{
341 if (version() >= 3)
343 else
345}
346
351 , mId(id)
352{
353
354#if QT_CONFIG(clipboard)
357 }
358#endif
359
360#if QT_CONFIG(wayland_datadevice)
363 }
364#endif
365
366#if QT_CONFIG(wayland_client_primary_selection)
367 // TODO: Could probably decouple this more if there was a signal for new seat added
370#endif
371
376 }
377
380
383
386
387#if QT_CONFIG(tabletevent)
388 if (auto *tm = mQDisplay->tabletManager())
390#endif
391}
392
400
402{
403 mCaps = caps;
404
407 } else if (!(mCaps & WL_SEAT_CAPABILITY_KEYBOARD) && mKeyboard) {
409 }
410
413
415 if (pointerGestures) {
416 // NOTE: The name of the device and its system ID are not exposed on Wayland.
426 }
427 } else if (!(mCaps & WL_SEAT_CAPABILITY_POINTER) && mPointer) {
428 mPointer.reset();
431 }
432
434 mTouch.reset(createTouch(this));
435
436 if (!mTouchDevice) {
437 // TODO number of touchpoints, actual name and ID
443 }
444 } else if (!(mCaps & WL_SEAT_CAPABILITY_TOUCH) && mTouch) {
445 mTouch.reset();
446 }
447}
448
453
458
463
468
473
478
483
488
493
501
507
508#if QT_CONFIG(wayland_datadevice)
510{
512}
513
515{
516 return mDataDevice;
517}
518#endif
519
520#if QT_CONFIG(clipboard)
522{
524}
525
527{
528 return mDataControlDevice.data();
529}
530#endif
531
532#if QT_CONFIG(wayland_client_primary_selection)
534{
536}
537
539{
541}
542#endif
543
548
549#if QT_CONFIG(tabletevent)
551{
553}
554
556{
557 return mTabletSeat.get();
558}
559#endif
560
565
570
575
581
583{
584 return mPointer ? mPointer->focusWindow() : nullptr;
585}
586
588{
589 return mKeyboard ? mKeyboard->focusWindow() : nullptr;
590}
591
593{
594 return mTouch ? mTouch->mFocus : nullptr;
595}
596
601
603{
604#if QT_CONFIG(xkbcommon)
607#else
609#endif
610 return {};
611}
612
614{
615 if (!mKeyboard)
616 return Qt::NoModifier;
617
618 return mKeyboard->modifiers();
619}
620
622{
624
625#if QT_CONFIG(xkbcommon)
626 if (!mXkbState)
627 return ret;
628
630#endif
631
632 return ret;
633}
634
635#if QT_CONFIG(cursor)
637{
639 mCursor = CursorState(); // Clear any previous state
644
645 if (mCursor.shape == Qt::BitmapCursor) {
648 mCursor.bitmapScale = int(dpr); // Wayland doesn't support fractional buffer scale
649 // If there was a fractional part of the dpr, we need to scale the hotspot accordingly
650 if (mCursor.bitmapScale < dpr)
652 }
653
654 // Return early if setCursor was called redundantly (mostly happens from decorations)
659 return;
660 }
661
662 if (mPointer)
664
665#if QT_CONFIG(tabletevent)
666 if (mTabletSeat)
668#endif
669}
670#endif
671
673{
674public:
675 EnterEvent(QWaylandWindow *surface, const QPointF &local, const QPointF &global)
678 {}
679};
680
683{
684 if (!surface)
685 return;
686
688
689 if (!window)
690 return; // Ignore foreign surfaces
691
692 if (mFocus) {
693 qCWarning(lcQpaWayland) << "The compositor sent a wl_pointer.enter event before sending a"
694 << "leave event first, this is not allowed by the wayland protocol"
695 << "attempting to work around it by invalidating the current focus";
697 }
700
703
706
709 }
710
711#if QT_CONFIG(cursor)
712 // Depends on mEnterSerial being updated
713 updateCursor();
714#endif
715
717 if (!grab)
719}
720
722{
723public:
724 LeaveEvent(QWaylandWindow *surface, const QPointF &localPos, const QPointF &globalPos)
727 {}
728};
729
731{
734
736
737 // The event may arrive after destroying the window, indicated by
738 // a null surface.
739 if (!surface)
740 return;
741
743 if (!window)
744 return; // Ignore foreign surfaces
745
748}
749
751{
752public:
753 MotionEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
754 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
757 {
758 }
759};
760
762{
764 if (!window) {
765 // We destroyed the pointer focus surface, but the server didn't get the message yet...
766 // or the server didn't send an enter event first. In either case, ignore the event.
767 return;
768 }
769
772
775 mParent->mTime = time;
776
778 if (grab && grab != window) {
779 // We can't know the true position since we're getting events for another surface,
780 // so we just set it outside of the window boundaries.
781 pos = QPointF(-1, -1);
783 window = grab;
784 }
786}
787
789{
790public:
791 PressEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
792 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button,
793 Qt::KeyboardModifiers modifiers)
796 {
797 }
798};
799
801{
802public:
803 ReleaseEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
804 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button,
805 Qt::KeyboardModifiers modifiers)
808 {
809 }
810};
811
814{
816 if (!window) {
817 // We destroyed the pointer focus surface, but the server didn't get the message yet...
818 // or the server didn't send an enter event first. In either case, ignore the event.
819 return;
820 }
821
823
824 // translate from kernel (input.h) 'button' to corresponding Qt:MouseButton.
825 // The range of mouse values is 0x110 <= mouse_button < 0x120, the first Joystick button.
826 switch (button) {
827 case 0x110: qt_button = Qt::LeftButton; break; // kernel BTN_LEFT
828 case 0x111: qt_button = Qt::RightButton; break;
829 case 0x112: qt_button = Qt::MiddleButton; break;
830 case 0x113: qt_button = Qt::ExtraButton1; break; // AKA Qt::BackButton
831 case 0x114: qt_button = Qt::ExtraButton2; break; // AKA Qt::ForwardButton
832 case 0x115: qt_button = Qt::ExtraButton3; break; // AKA Qt::TaskButton
833 case 0x116: qt_button = Qt::ExtraButton4; break;
834 case 0x117: qt_button = Qt::ExtraButton5; break;
835 case 0x118: qt_button = Qt::ExtraButton6; break;
836 case 0x119: qt_button = Qt::ExtraButton7; break;
837 case 0x11a: qt_button = Qt::ExtraButton8; break;
838 case 0x11b: qt_button = Qt::ExtraButton9; break;
839 case 0x11c: qt_button = Qt::ExtraButton10; break;
840 case 0x11d: qt_button = Qt::ExtraButton11; break;
841 case 0x11e: qt_button = Qt::ExtraButton12; break;
842 case 0x11f: qt_button = Qt::ExtraButton13; break;
843 default: return; // invalid button number (as far as Qt is concerned)
844 }
845
847
848 if (state)
850 else
852
853 mParent->mTime = time;
855 if (state)
857
859
862 if (grab && grab != focusWindow()) {
863 pos = QPointF(-1, -1);
865
866 window = grab;
867 }
868
869 if (state)
871 else
873}
874
876{
877 if (mFocus) {
879 mFocus = nullptr;
880 }
881 mEnterSerial = 0;
882}
883
889
897
899{
900public:
901 WheelEvent(QWaylandWindow *surface, Qt::ScrollPhase phase, ulong timestamp, const QPointF &local,
902 const QPointF &global, const QPoint &pixelDelta, const QPoint &angleDelta,
903 Qt::MouseEventSource source, Qt::KeyboardModifiers modifiers, bool inverted)
908 {
909 }
910};
911
913{
914 if (!focusWindow()) {
915 // We destroyed the pointer focus surface, but the server didn't get the message yet...
916 // or the server didn't send an enter event first. In either case, ignore the event.
917 return;
918 }
919
920 // Get the delta and convert it into the expected range
921 switch (axis) {
924 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis vertical:" << mFrameData.delta.y();
925 break;
928 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis horizontal:" << mFrameData.delta.x();
929 break;
930 default:
931 //TODO: is this really needed?
932 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis: Unknown axis:" << axis;
933 return;
934 }
935
936 mParent->mTime = time;
937
939}
940
942{
944 qCDebug(lcQpaWaylandInput) << "compressed pointer_frame event";
945 return;
946 }
947
949}
950
952{
953 switch (source) {
955 qCDebug(lcQpaWaylandInput) << "Axis source wheel";
956 break;
958 qCDebug(lcQpaWaylandInput) << "Axis source finger";
959 break;
961 qCDebug(lcQpaWaylandInput) << "Axis source continuous";
962 break;
964 qCDebug(lcQpaWaylandInput) << "Axis source wheel tilt";
965 }
967}
968
970{
971 if (!focusWindow())
972 return;
973
974 mParent->mTime = time;
975 switch (axis) {
977 qCDebug(lcQpaWaylandInput) << "Received vertical wl_pointer.axis_stop";
978 break;
980 qCDebug(lcQpaWaylandInput) << "Received horizontal wl_pointer.axis_stop";
981 break;
982 default:
983 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_stop: Unknown axis: " << axis
984 << "This is most likely a compositor bug";
985 return;
986 }
987
988 mScrollEnd = true;
989}
990
992{
993 if (!focusWindow())
994 return;
995
996 const int32_t delta120 = value * 15 * 8;
997
998 switch (axis) {
1000 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete vertical:" << value;
1002 break;
1004 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete horizontal:" << value;
1006 break;
1007 default:
1008 //TODO: is this really needed?
1009 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_discrete: Unknown axis:" << axis;
1010 return;
1011 }
1012}
1013
1015{
1016 if (!focusWindow())
1017 return;
1018
1019 switch (axis) {
1021 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_value120 vertical:" << value;
1023 break;
1025 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_value120 horizontal:" << value;
1027 break;
1028 default:
1029 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_value120: Unknown axis:" << axis;
1030 return;
1031 }
1032}
1033
1035{
1037 switch (axis) {
1040 break;
1043 break;
1044 default:
1045 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_relative_direction: Unknown axis:" << axis;
1046 }
1047}
1048
1050{
1052 qCDebug(lcQpaWaylandInput) << "Flushing new event; no frame event in this version";
1053 pointer_frame();
1054 }
1055}
1056
1058{
1059 qCDebug(lcQpaWaylandInput) << "Setting frame event " << event->type;
1061 qCDebug(lcQpaWaylandInput) << "Flushing; previous was " << mFrameData.event->type;
1063 }
1064
1066
1068}
1069
1078
1080{
1081 switch (axisSource) {
1082 case axis_source_wheel_tilt: // sideways tilt of the wheel
1083 case axis_source_wheel:
1084 // In the case of wheel events, a pixel delta doesn't really make sense,
1085 // and will make Qt think this is a continuous scroll event when it isn't,
1086 // so just ignore it.
1087 return false;
1088 case axis_source_finger:
1090 return !delta.isNull();
1091 default:
1092 return false;
1093 }
1094}
1095
1097{
1098 if (!hasPixelDelta())
1099 return QPoint();
1100
1102 // Add accumulated rounding error before rounding again
1105 Q_ASSERT(qAbs(accumulatedError->x()) < 1.0);
1106 Q_ASSERT(qAbs(accumulatedError->y()) < 1.0);
1107
1108 // for continuous scroll events things should be
1109 // in the same direction
1110 // i.e converted so downwards surface co-ordinates (positive axis_value)
1111 // goes to downwards in wheel event (negative value)
1112 pixelDelta *= -1;
1113 return pixelDelta;
1114}
1115
1117{
1118 if (delta120.isNull()) {
1119 // If we didn't get any discrete events, then we need to fall back to
1120 // the continuous information.
1121 return (delta * -12).toPoint(); //TODO: why multiply by 12?
1122 }
1123
1124 // The angle delta is in eights of degrees, and our docs says most mice have
1125 // 1 click = 15 degrees, i.e. 120 is one click. It's also in the opposite
1126 // direction of surface space.
1127 return -delta120;
1128}
1129
1131{
1132 switch (axisSource) {
1133 case axis_source_wheel_tilt: // sideways tilt of the wheel
1134 case axis_source_wheel:
1136 case axis_source_finger:
1138 default: // Whatever other sources might be added are probably not mouse wheels
1140 }
1141}
1142
1144{
1146
1147 // The wayland protocol has separate horizontal and vertical axes, Qt has just the one inverted flag
1148 // Pragmatically it should't come up
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()) {
1154 if (!mScrollBeginSent) {
1155 if (!target)
1157 if (!target)
1158 target = focusWindow();
1159 }
1160 if (!target) {
1161 qCDebug(lcQpaWaylandInput) << "Flushing scroll event aborted - no scroll target";
1163 return;
1164 }
1165
1167 qCDebug(lcQpaWaylandInput) << "Flushing scroll event sending ScrollBegin";
1171 mParent->modifiers(), false));
1172 mScrollBeginSent = true;
1175 }
1176
1179
1180 qCDebug(lcQpaWaylandInput) << "Flushing scroll event" << phase << pixelDelta << angleDelta;
1183 }
1184
1185 if (mScrollEnd) {
1186 if (mScrollBeginSent) {
1187 if (auto target = mScrollTarget.get()) {
1188 qCDebug(lcQpaWaylandInput) << "Flushing scroll end event";
1191 }
1192 mScrollBeginSent = false;
1194 } else {
1195 // May receive axis_stop for events we haven't sent a ScrollBegin for because
1196 // most axis_sources do not mandate an axis_stop event to be sent.
1197
1198 // TODO: For now, we just ignore these events, but we could perhaps take this as an
1199 // indication that this compositor will in fact send axis_stop events for these sources
1200 // and send a ScrollBegin the next time an axis_source event with this type is encountered.
1201 }
1202 mScrollEnd = false;
1204 }
1205
1207}
1208
1210{
1212
1213 if (auto *event = mFrameData.event.get()) {
1214 if (auto window = event->surface) {
1216 } else if (mFrameData.event->type == QEvent::MouseButtonRelease) {
1217 // If the window has been destroyed, we still need to report an up event, but it can't
1218 // be handled by the destroyed window (obviously), so send the event here instead.
1220 nullptr, event->timestamp,
1223 event->modifiers); // , Qt::MouseEventSource source =
1224 // Qt::MouseEventNotSynthesized);
1225 }
1227 }
1228
1229 //TODO: do modifiers get passed correctly here?
1231}
1232
1234{
1235 return source == axis_source_finger;
1236}
1237
1239{
1241#if QT_CONFIG(xkbcommon)
1243 return;
1244
1246 qCWarning(lcQpaWayland) << "unknown keymap format:" << format;
1247 close(fd);
1248 return;
1249 }
1250
1251 char *map_str = static_cast<char *>(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0));
1252 if (map_str == MAP_FAILED) {
1253 close(fd);
1254 return;
1255 }
1256
1261
1263 close(fd);
1264
1265 if (mXkbKeymap)
1267 else
1268 mXkbState.reset(nullptr);
1269#else
1270 Q_UNUSED(fd);
1271 Q_UNUSED(size);
1272#endif
1273}
1274
1276{
1278 Q_UNUSED(keys);
1279
1280 if (!surface) {
1281 // Ignoring wl_keyboard.enter event with null surface. This is either a compositor bug,
1282 // or it's a race with a wl_surface.destroy request. In either case, ignore the event.
1283 return;
1284 }
1285
1287 if (!window)
1288 return;
1289
1290 if (mFocus) {
1291 qCWarning(lcQpaWayland()) << "Unexpected wl_keyboard.enter event. Keyboard already has focus";
1293 }
1294
1297
1299}
1300
1302{
1304
1305 if (!surface) {
1306 // Either a compositor bug, or a race condition with wl_surface.destroy, ignore the event.
1307 return;
1308 }
1309
1311 if (!window)
1312 return;
1313
1314 if (window->waylandSurface() != mFocus) {
1315 qCWarning(lcQpaWayland) << "Ignoring unexpected wl_keyboard.leave event."
1316 << "wl_surface argument does not match the current focus"
1317 << "This is most likely a compositor bug";
1318 return;
1319 }
1322}
1323
1327 const QString &text, bool autorepeat, ushort count)
1328{
1330 bool filtered = false;
1331
1332 if (inputContext) {
1337 }
1338
1339 if (!filtered) {
1340 auto window = focusWindow()->window();
1341
1342 if (type == QEvent::KeyPress && key == Qt::Key_Menu) {
1343 auto cursor = window->screen()->handle()->cursor();
1344 if (cursor) {
1345 const QPoint globalPos = cursor->pos();
1348 }
1349 }
1350
1353 }
1354}
1355
1357{
1359 qCWarning(lcQpaWayland) << Q_FUNC_INFO << "unknown keymap format:" << mKeymapFormat;
1360 return;
1361 }
1362
1363 auto *window = focusWindow();
1364 if (!window) {
1365 // We destroyed the keyboard focus surface, but the server didn't get the message yet...
1366 // or the server didn't send an enter event first. In either case, ignore the event.
1367 return;
1368 }
1369
1371
1373 if (isDown)
1375
1377#if QT_CONFIG(xkbcommon)
1378 if ((!mXkbKeymap || !mXkbState) && !createDefaultKeymap())
1379 return;
1380
1381 auto code = key + 8; // map to wl_keyboard::keymap_format::keymap_format_xkb_v1
1382
1385
1388
1391 if (isAutoRepeat && mRepeatRate > 0) {
1392 qCWarning(lcQpaWayland, "received key repeat event while repeat rate is non-zero");
1393 return;
1394 }
1396 qCWarning(lcQpaWayland, "received key repeat event for a key that should not be repeated");
1397 return;
1398 }
1399 if (isAutoRepeat)
1401
1403
1412 } else if (mRepeatKey.code == code) {
1414 }
1415#else
1416 Q_UNUSED(time);
1417 Q_UNUSED(key);
1418 qCWarning(lcQpaWayland, "xkbcommon not available on this build, not performing key mapping");
1419 return;
1420#endif
1422 // raw scan code
1423 return;
1424 }
1425}
1426
1428{
1430}
1431
1433{
1434 mFocus = nullptr;
1437}
1438
1459
1465
1467 uint32_t time,
1468 struct wl_surface *surface,
1469 int32_t id,
1470 wl_fixed_t x,
1471 wl_fixed_t y)
1472{
1473 if (!surface)
1474 return;
1475
1477 if (!window)
1478 return; // Ignore foreign surfaces
1479
1480 mParent->mTime = time;
1482 mFocus = window;
1486}
1487
1489{
1491 mParent->mTime = time;
1493
1494 if (allTouchPointsReleased()) {
1495 mFocus = nullptr;
1496
1497 // As of Weston 7.0.0 there is no touch_frame after the last touch_up
1498 // (i.e. when the last finger is released). To accommodate for this, issue a
1499 // touch_frame. This cannot hurt since it is safe to call the touch_frame
1500 // handler multiple times when there are no points left.
1501 // See: https://gitlab.freedesktop.org/wayland/weston/issues/44
1502 // TODO: change logging category to lcQpaWaylandInput in newer versions.
1503 qCDebug(lcQpaWayland, "Generating fake frame event to work around Weston bug");
1504 touch_frame();
1505 }
1506}
1507
1514
1522
1524{
1526 auto it = std::find_if(mTouch->mPendingTouchPoints.begin(), end, [id](const QWindowSystemInterface::TouchPoint &tp){ return tp.id == id; });
1527 if (it == end) {
1529 it->id = id;
1530 }
1531 // If the touch points were up and down in same frame, send out frame right away
1532 else if ((it->state == QEventPoint::Pressed && state == QEventPoint::Released)
1536 it->id = id;
1537 }
1538
1540
1541 // Only moved and pressed needs to update/set position
1543 // We need a global (screen) position.
1545
1546 //is it possible that mTouchFocus is null;
1547 if (!win && mPointer)
1549 if (!win && mKeyboard)
1551 if (!win || !win->window())
1552 return;
1553
1554 tp.area = QRectF(0, 0, 8, 8);
1558 }
1559
1560 // If the touch point was pressed earlier this frame, we don't want to overwrite its state.
1561 if (tp.state != QEventPoint::Pressed)
1563
1564 tp.pressure = tp.state == QEventPoint::Released ? 0 : 1;
1565}
1566
1568{
1569 for (const auto &tp : std::as_const(mPendingTouchPoints)) {
1570 if (tp.state != QEventPoint::Released)
1571 return false;
1572 }
1573 return true;
1574}
1575
1586
1588{
1589 // TODO: early return if no events?
1590
1591 QWindow *window = mFocus ? mFocus->window() : nullptr;
1592
1593 if (mFocus) {
1594 // Returns a reference to the last item in the list. The list must not be empty.
1595 // If the list can be empty, call isEmpty() before calling this function.
1596 // See: https://doc.qt.io/qt-5.15/qlist.html#last
1598 return;
1600 // When the touch event is received, the global pos is calculated with the margins
1601 // in mind. Now we need to adjust again to get the correct local pos back.
1603 QPoint p = tp.area.center().toPoint();
1606 return;
1607 }
1608
1610
1611 // Prepare state for next frame
1614 for (const auto &prevPoint: prevTouchPoints) {
1615 // All non-released touch points should be part of the next touch event
1618 tp.state = QEventPoint::Stationary; // ... as stationary (unless proven otherwise)
1620 }
1621 }
1622
1623}
1624
1625}
1626
1627QT_END_NAMESPACE
1628
1629#include "moc_qwaylandinputdevice_p.cpp"
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)
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)
Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland")
static const int MaxTouchPoints
Q_DECLARE_LOGGING_CATEGORY(lcQpaWaylandInput)