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// Qt-Security score:significant reason:default
5
7
13#if QT_CONFIG(wayland_datadevice)
14#include "qwaylanddatadevice_p.h"
15#include "qwaylanddatadevicemanager_p.h"
16#endif
17#if QT_CONFIG(clipboard)
18#include "qwaylanddatacontrolv1_p.h"
19#endif
20#if QT_CONFIG(wayland_client_primary_selection)
21#include "qwaylandprimaryselectionv1_p.h"
22#endif
23#if QT_CONFIG(tabletevent)
24#include "qwaylandtabletv2_p.h"
25#endif
38
39#include <QtGui/private/qpixmap_raster_p.h>
40#include <QtGui/private/qguiapplication_p.h>
41#include <qpa/qplatformwindow.h>
42#include <qpa/qplatforminputcontext.h>
43#include <qpa/qplatformtheme.h>
44#include <QDebug>
45
46#include <unistd.h>
47#include <fcntl.h>
48#include <sys/mman.h>
49
50#if QT_CONFIG(cursor)
51#include <wayland-cursor.h>
52#endif
53
54#include <QtGui/QGuiApplication>
55#include <QtGui/QPointingDevice>
56
57QT_BEGIN_NAMESPACE
58
59namespace QtWaylandClient {
60
61Q_LOGGING_CATEGORY(lcQpaWaylandInput, "qt.qpa.wayland.input");
62
63// The maximum number of concurrent touchpoints is not exposed in wayland, so we assume a
64// reasonable number of them. As of 2021 most touchscreen panels support 10 concurrent touchpoints.
65static const int MaxTouchPoints = 10;
66
68{
69 timeElapsed.start();
70 delayTimer.setSingleShot(true);
71}
72
74{
75 using namespace std::chrono_literals;
76
77 if (!QCoreApplication::testAttribute(Qt::AA_CompressHighFrequencyEvents))
78 return false;
79
80 const auto elapsed = timeElapsed.durationElapsed();
81 timeElapsed.start();
82 if (elapsed < 100us || delayTimer.isActive())
83 {
84 // The highest USB HID polling rate is 8 kHz (125 μs). Most mice use lowe polling rate [125 Hz - 1000 Hz].
85 // Reject all events faster than 100 μs, because it definitely means the application main thread is
86 // freezed by long operation and events are delivered one after another from the queue. Since now we rely
87 // on the 0 ms timer to deliver the last pending event when application main thread is no longer freezed.
88 delayTimer.start(0);
89 return true;
90 }
91
92 return false;
93}
94
96 : mParent(p)
97{
99 mRepeatTimer.callOnTimeout(this, [&]() {
100 if (!focusWindow()) {
101 // We destroyed the keyboard focus surface, but the server didn't get the message yet...
102 // or the server didn't send an enter event first.
103 return;
104 }
109 mRepeatKey.text, true);
112 mRepeatKey.text, true);
113 });
114}
115
116#if QT_CONFIG(xkbcommon)
118{
120 if (!ctx)
121 return false;
122
123 struct xkb_rule_names names;
124 names.rules = "evdev";
125 names.model = "pc105";
126 names.layout = "us";
127 names.variant = "";
128 names.options = "";
129
131 if (mXkbKeymap)
133
134 if (!mXkbKeymap || !mXkbState) {
135 qCWarning(lcQpaWayland, "failed to create default keymap");
136 return false;
137 }
138
139 return true;
140}
141#endif
142
152
154{
155 return mFocus ? mFocus->waylandWindow() : nullptr;
156}
157
175
177{
178 if (version() >= 3)
180 else
182}
183
185{
186 return mFocus ? mFocus->waylandWindow() : nullptr;
187}
188
189#if QT_CONFIG(cursor)
190
192{
193 if (seat()->mQDisplay->compositor()->version() < 3) {
194 return 1;
195 }
196
197 if (auto *s = mCursor.surface.get()) {
198 if (s->outputScale() > 0)
199 return s->outputScale();
200 }
201
203}
204
206{
209
213 }
214
216 cursorThemeName = QStringLiteral("default");
217 if (cursorSize.isEmpty())
218 cursorSize = QSize(24, 24);
219
220 int scale = idealCursorScale();
222 auto *display = seat()->mQDisplay;
224
225 if (!mCursor.theme)
226 return; // A warning has already been printed in loadCursorTheme
227
228 if (auto *arrow = mCursor.theme->cursor(Qt::ArrowCursor)) {
229 int arrowPixelSize = qMax(arrow->images[0]->width, arrow->images[0]->height); // Not all cursor themes are square
230 while (scale > 1 && arrowPixelSize / scale < cursorSize.width())
231 --scale;
232 } else {
233 qCWarning(lcQpaWayland) << "Cursor theme does not support the arrow cursor";
234 }
236}
237
239{
240 if (mEnterSerial == 0)
241 return;
242
243 auto shape = seat()->mCursor.shape;
244
245 if (shape == Qt::BlankCursor) {
246 if (mCursor.surface)
248 set_cursor(mEnterSerial, nullptr, 0, 0);
249 return;
250 }
251
252 if (shape == Qt::BitmapCursor) {
254 if (!buffer) {
255 qCWarning(lcQpaWayland) << "No buffer for bitmap cursor, can't set cursor";
256 return;
257 }
258 auto hotspot = seat()->mCursor.hotspot;
261 return;
262 }
263
264 if (mCursor.shape) {
265 if (mCursor.surface) {
267 }
269 return;
270 }
271
274
275 if (!mCursor.theme)
276 return;
277
278 // Set from shape using theme
280 const uint time = timer.isValid() ? timer.elapsed() : 0;
281
282 if (struct ::wl_cursor *waylandCursor = mCursor.theme->cursor(shape)) {
283 uint duration = 0;
286
288 if (!buffer) {
289 qCWarning(lcQpaWayland) << "Could not find buffer for cursor" << shape;
290 return;
291 }
295 bool animated = duration > 0;
296 if (animated) {
300 }
302 return;
303 }
304
305 qCWarning(lcQpaWayland) << "Unable to change to cursor" << shape;
306}
307
310{
311 if (!mCursor.surface)
313 return mCursor.surface.get();
314}
315
317{
320 updateCursor();
321 }
322}
323
325{
328 updateCursor();
329 }
330}
331
332#endif // QT_CONFIG(cursor)
333
339
341{
342 if (version() >= 3)
344 else
346}
347
352 , mId(id)
353{
354
355#if QT_CONFIG(clipboard)
358 }
359#endif
360
361#if QT_CONFIG(wayland_datadevice)
364 }
365#endif
366
367#if QT_CONFIG(wayland_client_primary_selection)
368 // TODO: Could probably decouple this more if there was a signal for new seat added
371#endif
372
377 }
378
381
384
387
388#if QT_CONFIG(tabletevent)
389 if (auto *tm = mQDisplay->tabletManager())
391#endif
392}
393
401
403{
404 mCaps = caps;
405
408 } else if (!(mCaps & WL_SEAT_CAPABILITY_KEYBOARD) && mKeyboard) {
410 }
411
414
416 if (pointerGestures) {
420 // NOTE: The name of the device and its system ID are not exposed on Wayland.
430 }
431 } else if (!(mCaps & WL_SEAT_CAPABILITY_POINTER) && mPointer) {
432 mPointer.reset();
435 }
436
438 mTouch.reset(createTouch(this));
439
440 if (!mTouchDevice) {
441 // TODO number of touchpoints, actual name and ID
447 }
448 } else if (!(mCaps & WL_SEAT_CAPABILITY_TOUCH) && mTouch) {
449 mTouch.reset();
450 }
451}
452
457
462
467
472
477
482
487
492
494{
495 return mTouch.get();
496}
497
505
511
512#if QT_CONFIG(wayland_datadevice)
514{
516}
517
519{
520 return mDataDevice;
521}
522#endif
523
524#if QT_CONFIG(clipboard)
526{
528}
529
531{
532 return mDataControlDevice.get();
533}
534#endif
535
536#if QT_CONFIG(wayland_client_primary_selection)
538{
540}
541
543{
545}
546#endif
547
552
553#if QT_CONFIG(tabletevent)
555{
557}
558
560{
561 return mTabletSeat.get();
562}
563#endif
564
569
574
579
585
587{
588 return mPointer ? mPointer->focusWindow() : nullptr;
589}
590
592{
593 return mKeyboard ? mKeyboard->focusWindow() : nullptr;
594}
595
597{
598 return mTouch ? mTouch->mFocus : nullptr;
599}
600
605
607{
608#if QT_CONFIG(xkbcommon)
611#else
613#endif
614 return {};
615}
616
618{
619 if (!mKeyboard)
620 return Qt::NoModifier;
621
622 return mKeyboard->modifiers();
623}
624
626{
628
629#if QT_CONFIG(xkbcommon)
630 if (!mXkbState)
631 return ret;
632
634#endif
635
636 return ret;
637}
638
639#if QT_CONFIG(cursor)
641{
643 mCursor = CursorState(); // Clear any previous state
648
649 if (mCursor.shape == Qt::BitmapCursor) {
652 mCursor.bitmapScale = int(dpr); // Wayland doesn't support fractional buffer scale
653 // If there was a fractional part of the dpr, we need to scale the hotspot accordingly
654 if (mCursor.bitmapScale < dpr)
656 }
657
658 // Return early if setCursor was called redundantly (mostly happens from decorations)
663 return;
664 }
665
666 if (mPointer)
668
669#if QT_CONFIG(tabletevent)
670 if (mTabletSeat)
672#endif
673}
674#endif
675
677{
678public:
679 EnterEvent(QWaylandWindow *surface, const QPointF &local, const QPointF &global)
682 {}
683};
684
687{
688 if (!surface)
689 return;
690
692
693 if (!window)
694 return; // Ignore foreign surfaces
695
696 if (mFocus) {
697 qCWarning(lcQpaWayland) << "The compositor sent a wl_pointer.enter event before sending a"
698 << "leave event first, this is not allowed by the wayland protocol"
699 << "attempting to work around it by invalidating the current focus";
701 }
704
707
710
713 }
714
715#if QT_CONFIG(cursor)
716 // Depends on mEnterSerial being updated
717 updateCursor();
718#endif
719
721 if (!grab)
723}
724
726{
727public:
728 LeaveEvent(QWaylandWindow *surface, const QPointF &localPos, const QPointF &globalPos)
731 {}
732};
733
735{
737
740
741 // The event may arrive after destroying the window, indicated by
742 // a null surface.
743 if (!surface)
744 return;
745
747 if (!window)
748 return; // Ignore foreign surfaces
749
752}
753
755{
756public:
757 MotionEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
758 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
761 {
762 }
763};
764
766{
768 if (!window) {
769 // We destroyed the pointer focus surface, but the server didn't get the message yet...
770 // or the server didn't send an enter event first. In either case, ignore the event.
771 return;
772 }
773
776
779 mParent->mTime = time;
780
782 if (grab && grab != window) {
783 // We can't know the true position since we're getting events for another surface,
784 // so we just set it outside of the window boundaries.
785 pos = QPointF(-1, -1);
787 window = grab;
788 }
790}
791
793{
794public:
795 PressEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
796 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button,
797 Qt::KeyboardModifiers modifiers)
800 {
801 }
802};
803
805{
806public:
807 ReleaseEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
808 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button,
809 Qt::KeyboardModifiers modifiers)
812 {
813 }
814};
815
818{
820 if (!window) {
821 // We destroyed the pointer focus surface, but the server didn't get the message yet...
822 // or the server didn't send an enter event first. In either case, ignore the event.
823 return;
824 }
825
827
828 // translate from kernel (input.h) 'button' to corresponding Qt:MouseButton.
829 // The range of mouse values is 0x110 <= mouse_button < 0x120, the first Joystick button.
830 switch (button) {
831 case 0x110: qt_button = Qt::LeftButton; break; // kernel BTN_LEFT
832 case 0x111: qt_button = Qt::RightButton; break;
833 case 0x112: qt_button = Qt::MiddleButton; break;
834 case 0x113: qt_button = Qt::ExtraButton1; break; // AKA Qt::BackButton
835 case 0x114: qt_button = Qt::ExtraButton2; break; // AKA Qt::ForwardButton
836 case 0x115: qt_button = Qt::ExtraButton3; break; // AKA Qt::TaskButton
837 case 0x116: qt_button = Qt::ExtraButton4; break;
838 case 0x117: qt_button = Qt::ExtraButton5; break;
839 case 0x118: qt_button = Qt::ExtraButton6; break;
840 case 0x119: qt_button = Qt::ExtraButton7; break;
841 case 0x11a: qt_button = Qt::ExtraButton8; break;
842 case 0x11b: qt_button = Qt::ExtraButton9; break;
843 case 0x11c: qt_button = Qt::ExtraButton10; break;
844 case 0x11d: qt_button = Qt::ExtraButton11; break;
845 case 0x11e: qt_button = Qt::ExtraButton12; break;
846 case 0x11f: qt_button = Qt::ExtraButton13; break;
847 default: return; // invalid button number (as far as Qt is concerned)
848 }
849
851
852 if (state)
854 else
856
857 mParent->mTime = time;
859 if (state)
861
863
866 if (grab && grab != focusWindow()) {
867 pos = QPointF(-1, -1);
869
870 window = grab;
871 }
872
873 if (state)
875 if (state)
877 else
879}
880
882{
883 if (mFocus) {
885 mFocus = nullptr;
886 }
887 mEnterSerial = 0;
888}
889
895
903
905{
906public:
907 WheelEvent(QWaylandWindow *surface, Qt::ScrollPhase phase, ulong timestamp, const QPointF &local,
908 const QPointF &global, const QPoint &pixelDelta, const QPoint &angleDelta,
909 Qt::MouseEventSource source, Qt::KeyboardModifiers modifiers, bool inverted)
914 {
915 }
916};
917
919{
920 if (!focusWindow()) {
921 // We destroyed the pointer focus surface, but the server didn't get the message yet...
922 // or the server didn't send an enter event first. In either case, ignore the event.
923 return;
924 }
925
926 // Get the delta and convert it into the expected range
927 switch (axis) {
930 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis vertical:" << mFrameData.delta.y();
931 break;
934 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis horizontal:" << mFrameData.delta.x();
935 break;
936 default:
937 //TODO: is this really needed?
938 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis: Unknown axis:" << axis;
939 return;
940 }
941
942 mParent->mTime = time;
943
945}
946
948{
950 qCDebug(lcQpaWaylandInput) << "compressed pointer_frame event";
951 return;
952 }
953
955}
956
958{
959 switch (source) {
961 qCDebug(lcQpaWaylandInput) << "Axis source wheel";
962 break;
964 qCDebug(lcQpaWaylandInput) << "Axis source finger";
965 break;
967 qCDebug(lcQpaWaylandInput) << "Axis source continuous";
968 break;
970 qCDebug(lcQpaWaylandInput) << "Axis source wheel tilt";
971 }
973}
974
976{
977 if (!focusWindow())
978 return;
979
980 mParent->mTime = time;
981 switch (axis) {
983 qCDebug(lcQpaWaylandInput) << "Received vertical wl_pointer.axis_stop";
984 break;
986 qCDebug(lcQpaWaylandInput) << "Received horizontal wl_pointer.axis_stop";
987 break;
988 default:
989 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_stop: Unknown axis: " << axis
990 << "This is most likely a compositor bug";
991 return;
992 }
993
994 mScrollEnd = true;
995}
996
998{
999 if (!focusWindow())
1000 return;
1001
1002 const int32_t delta120 = value * 15 * 8;
1003
1004 switch (axis) {
1006 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete vertical:" << value;
1008 break;
1010 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete horizontal:" << value;
1012 break;
1013 default:
1014 //TODO: is this really needed?
1015 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_discrete: Unknown axis:" << axis;
1016 return;
1017 }
1018}
1019
1021{
1022 if (!focusWindow())
1023 return;
1024
1025 switch (axis) {
1027 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_value120 vertical:" << value;
1029 break;
1031 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_value120 horizontal:" << value;
1033 break;
1034 default:
1035 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_value120: Unknown axis:" << axis;
1036 return;
1037 }
1038}
1039
1041{
1043 switch (axis) {
1046 break;
1049 break;
1050 default:
1051 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_relative_direction: Unknown axis:" << axis;
1052 }
1053}
1054
1056{
1058 qCDebug(lcQpaWaylandInput) << "Flushing new event; no frame event in this version";
1059 pointer_frame();
1060 }
1061}
1062
1064{
1065 qCDebug(lcQpaWaylandInput) << "Setting frame event " << event->type;
1067 qCDebug(lcQpaWaylandInput) << "Flushing; previous was " << mFrameData.event->type;
1069 }
1070
1072
1074}
1075
1084
1086{
1087 switch (axisSource) {
1088 case axis_source_wheel_tilt: // sideways tilt of the wheel
1089 case axis_source_wheel:
1090 // In the case of wheel events, a pixel delta doesn't really make sense,
1091 // and will make Qt think this is a continuous scroll event when it isn't,
1092 // so just ignore it.
1093 return false;
1094 case axis_source_finger:
1096 return !delta.isNull();
1097 default:
1098 return false;
1099 }
1100}
1101
1103{
1104 if (!hasPixelDelta())
1105 return QPoint();
1106
1108 // Add accumulated rounding error before rounding again
1111 Q_ASSERT(qAbs(accumulatedError->x()) < 1.0);
1112 Q_ASSERT(qAbs(accumulatedError->y()) < 1.0);
1113
1114 // for continuous scroll events things should be
1115 // in the same direction
1116 // i.e converted so downwards surface co-ordinates (positive axis_value)
1117 // goes to downwards in wheel event (negative value)
1118 pixelDelta *= -1;
1119 return pixelDelta;
1120}
1121
1123{
1124 if (delta120.isNull()) {
1125 // If we didn't get any discrete events, then we need to fall back to
1126 // the continuous information.
1127 return (delta * -12).toPoint(); //TODO: why multiply by 12?
1128 }
1129
1130 // The angle delta is in eights of degrees, and our docs says most mice have
1131 // 1 click = 15 degrees, i.e. 120 is one click. It's also in the opposite
1132 // direction of surface space.
1133 return -delta120;
1134}
1135
1137{
1138 switch (axisSource) {
1139 case axis_source_wheel_tilt: // sideways tilt of the wheel
1140 case axis_source_wheel:
1142 case axis_source_finger:
1144 default: // Whatever other sources might be added are probably not mouse wheels
1146 }
1147}
1148
1150{
1152
1153 // The wayland protocol has separate horizontal and vertical axes, Qt has just the one inverted flag
1154 // Pragmatically it should't come up
1156
1157 // Angle delta is required for Qt wheel events, so don't try to send events if it's zero
1158 if (!angleDelta.isNull()) {
1160 if (!mScrollBeginSent) {
1161 if (!target)
1163 if (!target)
1164 target = focusWindow();
1165 }
1166 if (!target) {
1167 qCDebug(lcQpaWaylandInput) << "Flushing scroll event aborted - no scroll target";
1169 return;
1170 }
1171
1173 qCDebug(lcQpaWaylandInput) << "Flushing scroll event sending ScrollBegin";
1177 mParent->modifiers(), false));
1178 mScrollBeginSent = true;
1181 }
1182
1185
1186 qCDebug(lcQpaWaylandInput) << "Flushing scroll event" << phase << pixelDelta << angleDelta;
1189 }
1190
1191 if (mScrollEnd) {
1192 if (mScrollBeginSent) {
1193 if (auto target = mScrollTarget.get()) {
1194 qCDebug(lcQpaWaylandInput) << "Flushing scroll end event";
1197 }
1198 mScrollBeginSent = false;
1200 } else {
1201 // May receive axis_stop for events we haven't sent a ScrollBegin for because
1202 // most axis_sources do not mandate an axis_stop event to be sent.
1203
1204 // TODO: For now, we just ignore these events, but we could perhaps take this as an
1205 // indication that this compositor will in fact send axis_stop events for these sources
1206 // and send a ScrollBegin the next time an axis_source event with this type is encountered.
1207 }
1208 mScrollEnd = false;
1210 }
1211
1213}
1214
1216{
1218
1219 if (auto *event = mFrameData.event.get()) {
1220 if (auto window = event->surface) {
1222 } else if (mFrameData.event->type == QEvent::MouseButtonRelease) {
1223 // If the window has been destroyed, we still need to report an up event, but it can't
1224 // be handled by the destroyed window (obviously), so send the event here instead.
1226 nullptr, event->timestamp,
1229 event->modifiers); // , Qt::MouseEventSource source =
1230 // Qt::MouseEventNotSynthesized);
1231 }
1233 }
1234
1235 //TODO: do modifiers get passed correctly here?
1237}
1238
1240{
1241 return source == axis_source_finger;
1242}
1243
1245{
1247#if QT_CONFIG(xkbcommon)
1249 return;
1250
1252 qCWarning(lcQpaWayland) << "unknown keymap format:" << format;
1253 close(fd);
1254 return;
1255 }
1256
1257 char *map_str = static_cast<char *>(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0));
1258 if (map_str == MAP_FAILED) {
1259 close(fd);
1260 return;
1261 }
1262
1267
1269 close(fd);
1270
1271 if (mXkbKeymap)
1273 else
1274 mXkbState.reset(nullptr);
1275#else
1276 Q_UNUSED(fd);
1277 Q_UNUSED(size);
1278#endif
1279}
1280
1282{
1284 Q_UNUSED(keys);
1285
1286 if (!surface) {
1287 // Ignoring wl_keyboard.enter event with null surface. This is either a compositor bug,
1288 // or it's a race with a wl_surface.destroy request. In either case, ignore the event.
1289 return;
1290 }
1291
1293 if (!window)
1294 return;
1295
1296 if (mFocus) {
1297 qCWarning(lcQpaWayland()) << "Unexpected wl_keyboard.enter event. Keyboard already has focus";
1299 }
1300
1303
1305}
1306
1308{
1310
1311 if (!surface) {
1312 // Either a compositor bug, or a race condition with wl_surface.destroy, ignore the event.
1313 return;
1314 }
1315
1317 if (!window)
1318 return;
1319
1320 if (window->waylandSurface() != mFocus) {
1321 qCWarning(lcQpaWayland) << "Ignoring unexpected wl_keyboard.leave event."
1322 << "wl_surface argument does not match the current focus"
1323 << "This is most likely a compositor bug";
1324 return;
1325 }
1328}
1329
1333 const QString &text, bool autorepeat, ushort count)
1334{
1336 bool filtered = false;
1337
1338 if (inputContext) {
1343 }
1344
1345 if (!filtered) {
1348 : focusWindow()->window();
1349
1350 if (type == QEvent::KeyPress && key == Qt::Key_Menu) {
1351 auto cursor = window->screen()->handle()->cursor();
1352 if (cursor) {
1353 const QPoint globalPos = cursor->pos();
1356 }
1357 }
1358
1361 }
1362}
1363
1365{
1367 qCWarning(lcQpaWayland) << Q_FUNC_INFO << "unknown keymap format:" << mKeymapFormat;
1368 return;
1369 }
1370
1371 auto *window = focusWindow();
1372 if (!window) {
1373 // We destroyed the keyboard focus surface, but the server didn't get the message yet...
1374 // or the server didn't send an enter event first. In either case, ignore the event.
1375 return;
1376 }
1377
1379
1381 if (isDown)
1383
1385#if QT_CONFIG(xkbcommon)
1386 if ((!mXkbKeymap || !mXkbState) && !createDefaultKeymap())
1387 return;
1388
1389 auto code = key + 8; // map to wl_keyboard::keymap_format::keymap_format_xkb_v1
1390
1393
1396
1399 if (isAutoRepeat && mRepeatRate > 0) {
1400 qCWarning(lcQpaWayland, "received key repeat event while repeat rate is non-zero");
1401 return;
1402 }
1404 qCWarning(lcQpaWayland, "received key repeat event for a key that should not be repeated");
1405 return;
1406 }
1407 if (isAutoRepeat)
1409
1411
1420 } else if (mRepeatKey.code == code) {
1422 }
1423#else
1424 Q_UNUSED(time);
1425 Q_UNUSED(key);
1426 qCWarning(lcQpaWayland, "xkbcommon not available on this build, not performing key mapping");
1427 return;
1428#endif
1430 // raw scan code
1431 return;
1432 }
1433}
1434
1436{
1438}
1439
1441{
1442 mFocus = nullptr;
1445}
1446
1467
1473
1475 uint32_t time,
1476 struct wl_surface *surface,
1477 int32_t id,
1478 wl_fixed_t x,
1479 wl_fixed_t y)
1480{
1481 if (!surface)
1482 return;
1483
1485 if (!window)
1486 return; // Ignore foreign surfaces
1487
1488 mParent->mTime = time;
1490 mFocus = window;
1494}
1495
1497{
1499 mParent->mTime = time;
1501
1502 if (allTouchPointsReleased()) {
1503 mFocus = nullptr;
1504
1505 // As of Weston 7.0.0 there is no touch_frame after the last touch_up
1506 // (i.e. when the last finger is released). To accommodate for this, issue a
1507 // touch_frame. This cannot hurt since it is safe to call the touch_frame
1508 // handler multiple times when there are no points left.
1509 // See: https://gitlab.freedesktop.org/wayland/weston/issues/44
1510 // TODO: change logging category to lcQpaWaylandInput in newer versions.
1511 qCDebug(lcQpaWayland, "Generating fake frame event to work around Weston bug");
1512 touch_frame();
1513 }
1514}
1515
1522
1530
1532{
1534 auto it = std::find_if(mTouch->mPendingTouchPoints.begin(), end, [id](const QWindowSystemInterface::TouchPoint &tp){ return tp.id == id; });
1535 if (it == end) {
1537 it->id = id;
1538 }
1539 // If the touch points were up and down in same frame, send out frame right away
1540 else if ((it->state == QEventPoint::Pressed && state == QEventPoint::Released)
1544 it->id = id;
1545 }
1546
1548
1549 // Only moved and pressed needs to update/set position
1551 // We need a global (screen) position.
1553
1554 //is it possible that mTouchFocus is null;
1555 if (!win && mPointer)
1557 if (!win && mKeyboard)
1559 if (!win || !win->window())
1560 return;
1561
1562 tp.area = QRectF(0, 0, 8, 8);
1566 }
1567
1568 // If the touch point was pressed earlier this frame, we don't want to overwrite its state.
1569 if (tp.state != QEventPoint::Pressed)
1571
1572 tp.pressure = tp.state == QEventPoint::Released ? 0 : 1;
1573}
1574
1576{
1577 for (const auto &tp : std::as_const(mPendingTouchPoints)) {
1578 if (tp.state != QEventPoint::Released)
1579 return false;
1580 }
1581 return true;
1582}
1583
1594
1596{
1597 // TODO: early return if no events?
1598
1599 QWindow *window = mFocus ? mFocus->window() : nullptr;
1600
1601 if (mFocus) {
1602 // Returns a reference to the last item in the list. The list must not be empty.
1603 // If the list can be empty, call isEmpty() before calling this function.
1604 // See: https://doc.qt.io/qt-5.15/qlist.html#last
1606 return;
1608 // When the touch event is received, the global pos is calculated with the margins
1609 // in mind. Now we need to adjust again to get the correct local pos back.
1611 QPoint p = tp.area.center().toPoint();
1614 return;
1615 }
1616
1618
1619 // Prepare state for next frame
1622 for (const auto &prevPoint: prevTouchPoints) {
1623 // All non-released touch points should be part of the next touch event
1626 tp.state = QEventPoint::Stationary; // ... as stationary (unless proven otherwise)
1628 }
1629 }
1630
1631}
1632
1633}
1634
1635QT_END_NAMESPACE
1636
1637#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)