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 : mParent(p)
68{
70 mRepeatTimer.callOnTimeout(this, [&]() {
71 if (!focusWindow()) {
72 // We destroyed the keyboard focus surface, but the server didn't get the message yet...
73 // or the server didn't send an enter event first.
74 return;
75 }
80 mRepeatKey.text, true);
83 mRepeatKey.text, true);
84 });
85}
86
87#if QT_CONFIG(xkbcommon)
89{
91 if (!ctx)
92 return false;
93
94 struct xkb_rule_names names;
95 names.rules = "evdev";
96 names.model = "pc105";
97 names.layout = "us";
98 names.variant = "";
99 names.options = "";
100
102 if (mXkbKeymap)
104
105 if (!mXkbKeymap || !mXkbState) {
106 qCWarning(lcQpaWayland, "failed to create default keymap");
107 return false;
108 }
109
110 return true;
111}
112#endif
113
123
125{
126 return mFocus ? mFocus->waylandWindow() : nullptr;
127}
128
144
146{
147 if (version() >= 3)
149 else
151}
152
154{
155 return mFocus ? mFocus->waylandWindow() : nullptr;
156}
157
158#if QT_CONFIG(cursor)
159
161{
162 if (seat()->mQDisplay->compositor()->version() < 3) {
163 return 1;
164 }
165
166 if (auto *s = mCursor.surface.data()) {
167 if (s->outputScale() > 0)
168 return s->outputScale();
169 }
170
172}
173
175{
178
182 }
183
185 cursorThemeName = QStringLiteral("default");
186 if (cursorSize.isEmpty())
187 cursorSize = QSize(24, 24);
188
189 int scale = idealCursorScale();
191 auto *display = seat()->mQDisplay;
193
194 if (!mCursor.theme)
195 return; // A warning has already been printed in loadCursorTheme
196
197 if (auto *arrow = mCursor.theme->cursor(Qt::ArrowCursor)) {
198 int arrowPixelSize = qMax(arrow->images[0]->width, arrow->images[0]->height); // Not all cursor themes are square
199 while (scale > 1 && arrowPixelSize / scale < cursorSize.width())
200 --scale;
201 } else {
202 qCWarning(lcQpaWayland) << "Cursor theme does not support the arrow cursor";
203 }
205}
206
208{
209 if (mEnterSerial == 0)
210 return;
211
212 auto shape = seat()->mCursor.shape;
213
214 if (shape == Qt::BlankCursor) {
215 if (mCursor.surface)
217 set_cursor(mEnterSerial, nullptr, 0, 0);
218 return;
219 }
220
221 if (shape == Qt::BitmapCursor) {
223 if (!buffer) {
224 qCWarning(lcQpaWayland) << "No buffer for bitmap cursor, can't set cursor";
225 return;
226 }
227 auto hotspot = seat()->mCursor.hotspot;
230 return;
231 }
232
233 if (mCursor.shape) {
234 if (mCursor.surface) {
236 }
238 return;
239 }
240
243
244 if (!mCursor.theme)
245 return;
246
247 // Set from shape using theme
249 const uint time = timer.isValid() ? timer.elapsed() : 0;
250
251 if (struct ::wl_cursor *waylandCursor = mCursor.theme->cursor(shape)) {
252 uint duration = 0;
255
257 if (!buffer) {
258 qCWarning(lcQpaWayland) << "Could not find buffer for cursor" << shape;
259 return;
260 }
264 bool animated = duration > 0;
265 if (animated) {
269 }
271 return;
272 }
273
274 qCWarning(lcQpaWayland) << "Unable to change to cursor" << shape;
275}
276
279{
280 if (!mCursor.surface)
282 return mCursor.surface.get();
283}
284
286{
289 updateCursor();
290 }
291}
292
294{
297 updateCursor();
298 }
299}
300
301#endif // QT_CONFIG(cursor)
302
308
310{
311 if (version() >= 3)
313 else
315}
316
321 , mId(id)
322{
323
324#if QT_CONFIG(clipboard)
327 }
328#endif
329
330#if QT_CONFIG(wayland_datadevice)
333 }
334#endif
335
336#if QT_CONFIG(wayland_client_primary_selection)
337 // TODO: Could probably decouple this more if there was a signal for new seat added
340#endif
341
346 }
347
350
353
356
357#if QT_CONFIG(tabletevent)
358 if (auto *tm = mQDisplay->tabletManager())
360#endif
361}
362
370
376
383
385{
386 if (!mSeatNameKnown)
387 return; // too early
388
391 } else if (!(mCaps & WL_SEAT_CAPABILITY_KEYBOARD) && mKeyboard) {
393 }
394
397
399 if (pointerGestures) {
400 // NOTE: The name of the device and its system ID are not exposed on Wayland.
410 }
411 } else if (!(mCaps & WL_SEAT_CAPABILITY_POINTER) && mPointer) {
412 mPointer.reset();
415 }
416
418 mTouch.reset(createTouch(this));
419
420 if (!mTouchDevice) {
421 // TODO number of touchpoints, actual name and ID
427 }
428 } else if (!(mCaps & WL_SEAT_CAPABILITY_TOUCH) && mTouch) {
429 mTouch.reset();
430 }
431}
432
437
442
447
452
457
462
467
472
480
486
487#if QT_CONFIG(wayland_datadevice)
489{
491}
492
494{
495 return mDataDevice;
496}
497#endif
498
499#if QT_CONFIG(clipboard)
501{
503}
504
506{
507 return mDataControlDevice.data();
508}
509#endif
510
511#if QT_CONFIG(wayland_client_primary_selection)
513{
515}
516
518{
520}
521#endif
522
527
528#if QT_CONFIG(tabletevent)
530{
532}
533
535{
536 return mTabletSeat.get();
537}
538#endif
539
544
549
554
560
562{
563 return mPointer ? mPointer->focusWindow() : nullptr;
564}
565
567{
568 return mKeyboard ? mKeyboard->focusWindow() : nullptr;
569}
570
572{
573 return mTouch ? mTouch->mFocus : nullptr;
574}
575
580
582{
583#if QT_CONFIG(xkbcommon)
586#else
588#endif
589 return {};
590}
591
593{
594 if (!mKeyboard)
595 return Qt::NoModifier;
596
597 return mKeyboard->modifiers();
598}
599
601{
603
604#if QT_CONFIG(xkbcommon)
605 if (!mXkbState)
606 return ret;
607
609#endif
610
611 return ret;
612}
613
614#if QT_CONFIG(cursor)
616{
618 mCursor = CursorState(); // Clear any previous state
623
624 if (mCursor.shape == Qt::BitmapCursor) {
627 mCursor.bitmapScale = int(dpr); // Wayland doesn't support fractional buffer scale
628 // If there was a fractional part of the dpr, we need to scale the hotspot accordingly
629 if (mCursor.bitmapScale < dpr)
631 }
632
633 // Return early if setCursor was called redundantly (mostly happens from decorations)
638 return;
639 }
640
641 if (mPointer)
643
644 if (mTabletSeat)
646}
647#endif
648
650{
651public:
652 EnterEvent(QWaylandWindow *surface, const QPointF &local, const QPointF &global)
655 {}
656};
657
660{
661 if (!surface)
662 return;
663
665
666 if (!window)
667 return; // Ignore foreign surfaces
668
669 if (mFocus) {
670 qCWarning(lcQpaWayland) << "The compositor sent a wl_pointer.enter event before sending a"
671 << "leave event first, this is not allowed by the wayland protocol"
672 << "attempting to work around it by invalidating the current focus";
674 }
677
680
683
686 }
687
688#if QT_CONFIG(cursor)
689 // Depends on mEnterSerial being updated
690 updateCursor();
691#endif
692
694 if (!grab)
696}
697
699{
700public:
701 LeaveEvent(QWaylandWindow *surface, const QPointF &localPos, const QPointF &globalPos)
704 {}
705};
706
708{
711
713
714 // The event may arrive after destroying the window, indicated by
715 // a null surface.
716 if (!surface)
717 return;
718
720 if (!window)
721 return; // Ignore foreign surfaces
722
725}
726
728{
729public:
730 MotionEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
731 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
734 {
735 }
736};
737
739{
741 if (!window) {
742 // We destroyed the pointer focus surface, but the server didn't get the message yet...
743 // or the server didn't send an enter event first. In either case, ignore the event.
744 return;
745 }
746
749
752 mParent->mTime = time;
753
755 if (grab && grab != window) {
756 // We can't know the true position since we're getting events for another surface,
757 // so we just set it outside of the window boundaries.
758 pos = QPointF(-1, -1);
760 window = grab;
761 }
763}
764
766{
767public:
768 PressEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
769 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button,
770 Qt::KeyboardModifiers modifiers)
773 {
774 }
775};
776
778{
779public:
780 ReleaseEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
781 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button,
782 Qt::KeyboardModifiers modifiers)
785 {
786 }
787};
788
791{
793 if (!window) {
794 // We destroyed the pointer focus surface, but the server didn't get the message yet...
795 // or the server didn't send an enter event first. In either case, ignore the event.
796 return;
797 }
798
800
801 // translate from kernel (input.h) 'button' to corresponding Qt:MouseButton.
802 // The range of mouse values is 0x110 <= mouse_button < 0x120, the first Joystick button.
803 switch (button) {
804 case 0x110: qt_button = Qt::LeftButton; break; // kernel BTN_LEFT
805 case 0x111: qt_button = Qt::RightButton; break;
806 case 0x112: qt_button = Qt::MiddleButton; break;
807 case 0x113: qt_button = Qt::ExtraButton1; break; // AKA Qt::BackButton
808 case 0x114: qt_button = Qt::ExtraButton2; break; // AKA Qt::ForwardButton
809 case 0x115: qt_button = Qt::ExtraButton3; break; // AKA Qt::TaskButton
810 case 0x116: qt_button = Qt::ExtraButton4; break;
811 case 0x117: qt_button = Qt::ExtraButton5; break;
812 case 0x118: qt_button = Qt::ExtraButton6; break;
813 case 0x119: qt_button = Qt::ExtraButton7; break;
814 case 0x11a: qt_button = Qt::ExtraButton8; break;
815 case 0x11b: qt_button = Qt::ExtraButton9; break;
816 case 0x11c: qt_button = Qt::ExtraButton10; break;
817 case 0x11d: qt_button = Qt::ExtraButton11; break;
818 case 0x11e: qt_button = Qt::ExtraButton12; break;
819 case 0x11f: qt_button = Qt::ExtraButton13; break;
820 default: return; // invalid button number (as far as Qt is concerned)
821 }
822
824
825 if (state)
827 else
829
830 mParent->mTime = time;
832 if (state)
834
836
839 if (grab && grab != focusWindow()) {
840 pos = QPointF(-1, -1);
842
843 window = grab;
844 }
845
846 if (state)
848 else
850}
851
853{
854 if (mFocus) {
856 mFocus = nullptr;
857 }
858 mEnterSerial = 0;
859}
860
865
873
875{
876public:
877 WheelEvent(QWaylandWindow *surface, Qt::ScrollPhase phase, ulong timestamp, const QPointF &local,
878 const QPointF &global, const QPoint &pixelDelta, const QPoint &angleDelta,
879 Qt::MouseEventSource source, Qt::KeyboardModifiers modifiers, bool inverted)
884 {
885 }
886};
887
889{
890 if (!focusWindow()) {
891 // We destroyed the pointer focus surface, but the server didn't get the message yet...
892 // or the server didn't send an enter event first. In either case, ignore the event.
893 return;
894 }
895
896 // Get the delta and convert it into the expected range
897 switch (axis) {
900 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis vertical:" << mFrameData.delta.y();
901 break;
904 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis horizontal:" << mFrameData.delta.x();
905 break;
906 default:
907 //TODO: is this really needed?
908 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis: Unknown axis:" << axis;
909 return;
910 }
911
912 mParent->mTime = time;
913
915 qCDebug(lcQpaWaylandInput) << "Flushing new event; no frame event in this version";
917 }
918}
919
924
926{
927 switch (source) {
929 qCDebug(lcQpaWaylandInput) << "Axis source wheel";
930 break;
932 qCDebug(lcQpaWaylandInput) << "Axis source finger";
933 break;
935 qCDebug(lcQpaWaylandInput) << "Axis source continuous";
936 break;
938 qCDebug(lcQpaWaylandInput) << "Axis source wheel tilt";
939 }
941}
942
944{
945 if (!focusWindow())
946 return;
947
948 mParent->mTime = time;
949 switch (axis) {
951 qCDebug(lcQpaWaylandInput) << "Received vertical wl_pointer.axis_stop";
952 mFrameData.delta.setY(0); //TODO: what's the point of doing this?
953 break;
955 qCDebug(lcQpaWaylandInput) << "Received horizontal wl_pointer.axis_stop";
957 break;
958 default:
959 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_stop: Unknown axis: " << axis
960 << "This is most likely a compositor bug";
961 return;
962 }
963
964 // May receive axis_stop for events we haven't sent a ScrollBegin for because
965 // most axis_sources do not mandate an axis_stop event to be sent.
966 if (!mScrollBeginSent) {
967 // TODO: For now, we just ignore these events, but we could perhaps take this as an
968 // indication that this compositor will in fact send axis_stop events for these sources
969 // and send a ScrollBegin the next time an axis_source event with this type is encountered.
970 return;
971 }
972
974 if (!target)
981 mScrollBeginSent = false;
983}
984
986{
987 if (!focusWindow())
988 return;
989
990 const int32_t delta120 = value * 15 * 8;
991
992 switch (axis) {
994 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete vertical:" << value;
996 break;
998 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete horizontal:" << value;
1000 break;
1001 default:
1002 //TODO: is this really needed?
1003 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_discrete: Unknown axis:" << axis;
1004 return;
1005 }
1006}
1007
1009{
1010 if (!focusWindow())
1011 return;
1012
1013 switch (axis) {
1015 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_value120 vertical:" << value;
1017 break;
1019 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_value120 horizontal:" << value;
1021 break;
1022 default:
1023 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_value120: Unknown axis:" << axis;
1024 return;
1025 }
1026}
1027
1029{
1031 switch (axis) {
1034 break;
1037 break;
1038 default:
1039 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_relative_direction: Unknown axis:" << axis;
1040 }
1041}
1042
1044{
1045 qCDebug(lcQpaWaylandInput) << "Setting frame event " << event->type;
1047 qCDebug(lcQpaWaylandInput) << "Flushing; previous was " << mFrameData.event->type;
1049 }
1050
1052
1054 qCDebug(lcQpaWaylandInput) << "Flushing new event; no frame event in this version";
1056 }
1057}
1058
1067
1069{
1070 switch (axisSource) {
1071 case axis_source_wheel_tilt: // sideways tilt of the wheel
1072 case axis_source_wheel:
1073 // In the case of wheel events, a pixel delta doesn't really make sense,
1074 // and will make Qt think this is a continuous scroll event when it isn't,
1075 // so just ignore it.
1076 return false;
1077 case axis_source_finger:
1079 return !delta.isNull();
1080 default:
1081 return false;
1082 }
1083}
1084
1086{
1087 if (!hasPixelDelta())
1088 return QPoint();
1089
1091 // Add accumulated rounding error before rounding again
1094 Q_ASSERT(qAbs(accumulatedError->x()) < 1.0);
1095 Q_ASSERT(qAbs(accumulatedError->y()) < 1.0);
1096
1097 // for continuous scroll events things should be
1098 // in the same direction
1099 // i.e converted so downwards surface co-ordinates (positive axis_value)
1100 // goes to downwards in wheel event (negative value)
1101 pixelDelta *= -1;
1102 return pixelDelta;
1103}
1104
1106{
1107 if (delta120.isNull()) {
1108 // If we didn't get any discrete events, then we need to fall back to
1109 // the continuous information.
1110 return (delta * -12).toPoint(); //TODO: why multiply by 12?
1111 }
1112
1113 // The angle delta is in eights of degrees, and our docs says most mice have
1114 // 1 click = 15 degrees, i.e. 120 is one click. It's also in the opposite
1115 // direction of surface space.
1116 return -delta120;
1117}
1118
1120{
1121 switch (axisSource) {
1122 case axis_source_wheel_tilt: // sideways tilt of the wheel
1123 case axis_source_wheel:
1125 case axis_source_finger:
1127 default: // Whatever other sources might be added are probably not mouse wheels
1129 }
1130}
1131
1133{
1135
1136 // Angle delta is required for Qt wheel events, so don't try to send events if it's zero
1137 if (!angleDelta.isNull()) {
1139 if (!target)
1140 target = focusWindow();
1141
1143 qCDebug(lcQpaWaylandInput) << "Flushing scroll event sending ScrollBegin";
1147 mParent->modifiers(), false));
1148 mScrollBeginSent = true;
1150 }
1151
1155
1156
1157 // The wayland protocol has separate horizontal and vertical axes, Qt has just the one inverted flag
1158 // Pragmatically it should't come up
1160
1161 qCDebug(lcQpaWaylandInput) << "Flushing scroll event" << phase << pixelDelta << angleDelta;
1164 }
1166}
1167
1169{
1170 if (auto *event = mFrameData.event) {
1171 if (auto window = event->surface) {
1173 } else if (mFrameData.event->type == QEvent::MouseButtonRelease) {
1174 // If the window has been destroyed, we still need to report an up event, but it can't
1175 // be handled by the destroyed window (obviously), so send the event here instead.
1177 nullptr, event->timestamp,
1180 event->modifiers); // , Qt::MouseEventSource source =
1181 // Qt::MouseEventNotSynthesized);
1182 }
1183 delete mFrameData.event;
1184 mFrameData.event = nullptr;
1185 }
1186
1187 //TODO: do modifiers get passed correctly here?
1189}
1190
1192{
1193 return source == axis_source_finger;
1194}
1195
1197{
1199#if QT_CONFIG(xkbcommon)
1201 qCWarning(lcQpaWayland) << "unknown keymap format:" << format;
1202 close(fd);
1203 return;
1204 }
1205
1206 char *map_str = static_cast<char *>(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0));
1207 if (map_str == MAP_FAILED) {
1208 close(fd);
1209 return;
1210 }
1211
1216
1218 close(fd);
1219
1220 if (mXkbKeymap)
1222 else
1223 mXkbState.reset(nullptr);
1224#else
1225 Q_UNUSED(fd);
1226 Q_UNUSED(size);
1227#endif
1228}
1229
1231{
1233 Q_UNUSED(keys);
1234
1235 if (!surface) {
1236 // Ignoring wl_keyboard.enter event with null surface. This is either a compositor bug,
1237 // or it's a race with a wl_surface.destroy request. In either case, ignore the event.
1238 return;
1239 }
1240
1242 if (!window)
1243 return;
1244
1245 if (mFocus) {
1246 qCWarning(lcQpaWayland()) << "Unexpected wl_keyboard.enter event. Keyboard already has focus";
1248 }
1249
1252
1254}
1255
1257{
1259
1260 if (!surface) {
1261 // Either a compositor bug, or a race condition with wl_surface.destroy, ignore the event.
1262 return;
1263 }
1264
1266 if (!window)
1267 return;
1268
1269 if (window->waylandSurface() != mFocus) {
1270 qCWarning(lcQpaWayland) << "Ignoring unexpected wl_keyboard.leave event."
1271 << "wl_surface argument does not match the current focus"
1272 << "This is most likely a compositor bug";
1273 return;
1274 }
1277}
1278
1282 const QString &text, bool autorepeat, ushort count)
1283{
1285 bool filtered = false;
1286
1287 if (inputContext) {
1292 }
1293
1294 if (!filtered) {
1295 auto window = focusWindow()->window();
1296
1297 if (type == QEvent::KeyPress && key == Qt::Key_Menu) {
1298 auto cursor = window->screen()->handle()->cursor();
1299 if (cursor) {
1300 const QPoint globalPos = cursor->pos();
1303 }
1304 }
1305
1308 }
1309}
1310
1312{
1314 qCWarning(lcQpaWayland) << Q_FUNC_INFO << "unknown keymap format:" << mKeymapFormat;
1315 return;
1316 }
1317
1318 auto *window = focusWindow();
1319 if (!window) {
1320 // We destroyed the keyboard focus surface, but the server didn't get the message yet...
1321 // or the server didn't send an enter event first. In either case, ignore the event.
1322 return;
1323 }
1324
1326
1328 if (isDown)
1330
1332#if QT_CONFIG(xkbcommon)
1333 if ((!mXkbKeymap || !mXkbState) && !createDefaultKeymap())
1334 return;
1335
1336 auto code = key + 8; // map to wl_keyboard::keymap_format::keymap_format_xkb_v1
1337
1340
1343
1346
1355 } else if (mRepeatKey.code == code) {
1357 }
1358#else
1359 Q_UNUSED(time);
1360 Q_UNUSED(key);
1361 qCWarning(lcQpaWayland, "xkbcommon not available on this build, not performing key mapping");
1362 return;
1363#endif
1365 // raw scan code
1366 return;
1367 }
1368}
1369
1371{
1373}
1374
1376{
1377 mFocus = nullptr;
1380}
1381
1402
1408
1410 uint32_t time,
1411 struct wl_surface *surface,
1412 int32_t id,
1413 wl_fixed_t x,
1414 wl_fixed_t y)
1415{
1416 if (!surface)
1417 return;
1418
1420 if (!window)
1421 return; // Ignore foreign surfaces
1422
1423 mParent->mTime = time;
1425 mFocus = window;
1429}
1430
1432{
1434 mParent->mTime = time;
1436
1437 if (allTouchPointsReleased()) {
1438 mFocus = nullptr;
1439
1440 // As of Weston 7.0.0 there is no touch_frame after the last touch_up
1441 // (i.e. when the last finger is released). To accommodate for this, issue a
1442 // touch_frame. This cannot hurt since it is safe to call the touch_frame
1443 // handler multiple times when there are no points left.
1444 // See: https://gitlab.freedesktop.org/wayland/weston/issues/44
1445 // TODO: change logging category to lcQpaWaylandInput in newer versions.
1446 qCDebug(lcQpaWayland, "Generating fake frame event to work around Weston bug");
1447 touch_frame();
1448 }
1449}
1450
1457
1465
1467{
1469 auto it = std::find_if(mTouch->mPendingTouchPoints.begin(), end, [id](const QWindowSystemInterface::TouchPoint &tp){ return tp.id == id; });
1470 if (it == end) {
1472 it->id = id;
1473 }
1474 // If the touch points were up and down in same frame, send out frame right away
1475 else if ((it->state == QEventPoint::Pressed && state == QEventPoint::Released)
1479 it->id = id;
1480 }
1481
1483
1484 // Only moved and pressed needs to update/set position
1486 // We need a global (screen) position.
1488
1489 //is it possible that mTouchFocus is null;
1490 if (!win && mPointer)
1492 if (!win && mKeyboard)
1494 if (!win || !win->window())
1495 return;
1496
1497 tp.area = QRectF(0, 0, 8, 8);
1501 }
1502
1503 // If the touch point was pressed earlier this frame, we don't want to overwrite its state.
1504 if (tp.state != QEventPoint::Pressed)
1506
1507 tp.pressure = tp.state == QEventPoint::Released ? 0 : 1;
1508}
1509
1511{
1512 for (const auto &tp : std::as_const(mPendingTouchPoints)) {
1513 if (tp.state != QEventPoint::Released)
1514 return false;
1515 }
1516 return true;
1517}
1518
1529
1531{
1532 // TODO: early return if no events?
1533
1534 QWindow *window = mFocus ? mFocus->window() : nullptr;
1535
1536 if (mFocus) {
1537 // Returns a reference to the last item in the list. The list must not be empty.
1538 // If the list can be empty, call isEmpty() before calling this function.
1539 // See: https://doc.qt.io/qt-5.15/qlist.html#last
1541 return;
1543 // When the touch event is received, the global pos is calculated with the margins
1544 // in mind. Now we need to adjust again to get the correct local pos back.
1546 QPoint p = tp.area.center().toPoint();
1549 return;
1550 }
1551
1553
1554 // Prepare state for next frame
1557 for (const auto &prevPoint: prevTouchPoints) {
1558 // All non-released touch points should be part of the next touch event
1561 tp.state = QEventPoint::Stationary; // ... as stationary (unless proven otherwise)
1563 }
1564 }
1565
1566}
1567
1568}
1569
1570QT_END_NAMESPACE
1571
1572#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)