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 (!focusSurface()) {
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
157
175
177{
178 if (version() >= 3)
180 else
182}
183
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.
428 }
429 } else if (!(mCaps & WL_SEAT_CAPABILITY_POINTER) && mPointer) {
430 mPointer.reset();
433 }
434
436 mTouch.reset(createTouch(this));
437
438 if (!mTouchDevice) {
439 // TODO number of touchpoints, actual name and ID
445 }
446 } else if (!(mCaps & WL_SEAT_CAPABILITY_TOUCH) && mTouch) {
447 mTouch.reset();
448 }
449}
450
455
460
465
470
475
480
485
490
492{
493 return mTouch.get();
494}
495
503
509
510#if QT_CONFIG(wayland_datadevice)
512{
514}
515
517{
518 return mDataDevice;
519}
520#endif
521
522#if QT_CONFIG(clipboard)
524{
526}
527
529{
530 return mDataControlDevice.get();
531}
532#endif
533
534#if QT_CONFIG(wayland_client_primary_selection)
536{
538}
539
541{
543}
544#endif
545
550
551#if QT_CONFIG(tabletevent)
553{
555}
556
558{
559 return mTabletSeat.get();
560}
561#endif
562
567
572
577
583
585{
586 //TODO GUARD!
587 return mPointer && mPointer->focusSurface() ? mPointer->focusSurface()->waylandWindow() : nullptr;
588}
589
591{
592 //GUARD!
593
595}
596
598{
599 //GUARD!
600
601 return mTouch && mTouch->mFocus ? mTouch->mFocus->waylandWindow() : nullptr;
602}
603
608
610{
611#if QT_CONFIG(xkbcommon)
614#else
616#endif
617 return {};
618}
619
621{
622 if (!mKeyboard)
623 return Qt::NoModifier;
624
625 return mKeyboard->modifiers();
626}
627
629{
631
632#if QT_CONFIG(xkbcommon)
633 if (!mXkbState)
634 return ret;
635
637#endif
638
639 return ret;
640}
641
642#if QT_CONFIG(cursor)
644{
646 mCursor = CursorState(); // Clear any previous state
651
652 if (mCursor.shape == Qt::BitmapCursor) {
655 mCursor.bitmapScale = int(dpr); // Wayland doesn't support fractional buffer scale
656 // If there was a fractional part of the dpr, we need to scale the hotspot accordingly
657 if (mCursor.bitmapScale < dpr)
659 }
660
661 // Return early if setCursor was called redundantly (mostly happens from decorations)
666 return;
667 }
668
669 if (mPointer)
671
672#if QT_CONFIG(tabletevent)
673 if (mTabletSeat)
675#endif
676}
677#endif
678
680{
681public:
682 EnterEvent(QWaylandSurface *surface, const QPointF &local, const QPointF &global)
685 {}
686};
687
690{
691 if (!wlSurface)
692 return;
693
695
696 if (!surface)
697 return; // Ignore foreign surfaces
698
699 if (mFocus) {
700 qCWarning(lcQpaWayland) << "The compositor sent a wl_pointer.enter event before sending a"
701 << "leave event first, this is not allowed by the wayland protocol"
702 << "attempting to work around it by invalidating the current focus";
704 }
705 mFocus = surface;
707
709 if (auto *ww = surface->waylandWindow())
711 else
712 return;
713
716
719 }
720
721#if QT_CONFIG(cursor)
722 // Depends on mEnterSerial being updated
723 updateCursor();
724#endif
725
727 if (!grab)
729}
730
732{
733public:
734 LeaveEvent(QWaylandSurface *surface, const QPointF &localPos, const QPointF &globalPos)
737 {}
738};
739
741{
743
746
747 // The event may arrive after destroying the window, indicated by
748 // a null surface.
749 if (!wlSurface)
750 return;
751
753 if (!surface)
754 return; // Ignore foreign surfaces
755
758}
759
761{
762public:
763 MotionEvent(QWaylandSurface *surface, ulong timestamp, const QPointF &localPos,
764 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
767 {
768 }
769};
770
772{
774 if (!surface) {
775 // We destroyed the pointer focus surface, but the server didn't get the message yet...
776 // or the server didn't send an enter event first. In either case, ignore the event.
777 return;
778 }
779
782 if (auto *ww = surface->waylandWindow())
784 else
785 return;
786
789 mParent->mTime = time;
790
792 if (grab && grab->waylandSurface() != surface) {
793 // We can't know the true position since we're getting events for another surface,
794 // so we just set it outside of the window boundaries.
795 pos = QPointF(-1, -1);
798 }
800}
801
803{
804public:
805 PressEvent(QWaylandSurface *surface, ulong timestamp, const QPointF &localPos,
806 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button,
807 Qt::KeyboardModifiers modifiers)
810 {
811 }
812};
813
815{
816public:
817 ReleaseEvent(QWaylandSurface *surface, ulong timestamp, const QPointF &localPos,
818 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button,
819 Qt::KeyboardModifiers modifiers)
822 {
823 }
824};
825
828{
830 if (!surface) {
831 // We destroyed the pointer focus surface, but the server didn't get the message yet...
832 // or the server didn't send an enter event first. In either case, ignore the event.
833 return;
834 }
835
837
838 // translate from kernel (input.h) 'button' to corresponding Qt:MouseButton.
839 // The range of mouse values is 0x110 <= mouse_button < 0x120, the first Joystick button.
840 switch (button) {
841 case 0x110: qt_button = Qt::LeftButton; break; // kernel BTN_LEFT
842 case 0x111: qt_button = Qt::RightButton; break;
843 case 0x112: qt_button = Qt::MiddleButton; break;
844 case 0x113: qt_button = Qt::ExtraButton1; break; // AKA Qt::BackButton
845 case 0x114: qt_button = Qt::ExtraButton2; break; // AKA Qt::ForwardButton
846 case 0x115: qt_button = Qt::ExtraButton3; break; // AKA Qt::TaskButton
847 case 0x116: qt_button = Qt::ExtraButton4; break;
848 case 0x117: qt_button = Qt::ExtraButton5; break;
849 case 0x118: qt_button = Qt::ExtraButton6; break;
850 case 0x119: qt_button = Qt::ExtraButton7; break;
851 case 0x11a: qt_button = Qt::ExtraButton8; break;
852 case 0x11b: qt_button = Qt::ExtraButton9; break;
853 case 0x11c: qt_button = Qt::ExtraButton10; break;
854 case 0x11d: qt_button = Qt::ExtraButton11; break;
855 case 0x11e: qt_button = Qt::ExtraButton12; break;
856 case 0x11f: qt_button = Qt::ExtraButton13; break;
857 default: return; // invalid button number (as far as Qt is concerned)
858 }
859
861 const bool firstPress = state && mButtons == Qt::NoButton;
862
863 if (state)
865 else
867
868 mParent->mTime = time;
870 if (state)
872
874
877 if (grab && grab->waylandSurface() != focusSurface()) {
878 pos = QPointF(-1, -1);
880
882 }
883
884 if (firstPress)
886
887 if (state) {
888 if (auto *window = surface->waylandWindow())
891 } else {
893 if (mButtons == Qt::NoButton)
895 }
896}
897
899{
900 if (mFocus) {
902 mFocus = nullptr;
903 }
904 mEnterSerial = 0;
905}
906
917
919{
920 if (auto *surface = focusSurface()) {
922 if (auto *ww = surface->waylandWindow())
924 }
925}
926
928{
929public:
930 WheelEvent(QWaylandSurface *surface, Qt::ScrollPhase phase, ulong timestamp, const QPointF &local,
931 const QPointF &global, const QPoint &pixelDelta, const QPoint &angleDelta,
932 Qt::MouseEventSource source, Qt::KeyboardModifiers modifiers, bool inverted)
937 {
938 }
939};
940
942{
943 if (!focusSurface()) {
944 // We destroyed the pointer focus surface, but the server didn't get the message yet...
945 // or the server didn't send an enter event first. In either case, ignore the event.
946 return;
947 }
948
949 // Get the delta and convert it into the expected range
950 switch (axis) {
953 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis vertical:" << mFrameData.delta.y();
954 break;
957 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis horizontal:" << mFrameData.delta.x();
958 break;
959 default:
960 //TODO: is this really needed?
961 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis: Unknown axis:" << axis;
962 return;
963 }
964
965 mParent->mTime = time;
966
968}
969
971{
973 qCDebug(lcQpaWaylandInput) << "compressed pointer_frame event";
974 return;
975 }
976
978}
979
981{
982 switch (source) {
984 qCDebug(lcQpaWaylandInput) << "Axis source wheel";
985 break;
987 qCDebug(lcQpaWaylandInput) << "Axis source finger";
988 break;
990 qCDebug(lcQpaWaylandInput) << "Axis source continuous";
991 break;
993 qCDebug(lcQpaWaylandInput) << "Axis source wheel tilt";
994 }
996}
997
999{
1000 if (!focusSurface())
1001 return;
1002
1003 mParent->mTime = time;
1004 switch (axis) {
1006 qCDebug(lcQpaWaylandInput) << "Received vertical wl_pointer.axis_stop";
1007 break;
1009 qCDebug(lcQpaWaylandInput) << "Received horizontal wl_pointer.axis_stop";
1010 break;
1011 default:
1012 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_stop: Unknown axis: " << axis
1013 << "This is most likely a compositor bug";
1014 return;
1015 }
1016
1017 mScrollEnd = true;
1018}
1019
1021{
1022 if (!focusSurface())
1023 return;
1024
1025 const int32_t delta120 = value * 15 * 8;
1026
1027 switch (axis) {
1029 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete vertical:" << value;
1031 break;
1033 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete horizontal:" << value;
1035 break;
1036 default:
1037 //TODO: is this really needed?
1038 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_discrete: Unknown axis:" << axis;
1039 return;
1040 }
1041}
1042
1044{
1045 if (!focusSurface())
1046 return;
1047
1048 switch (axis) {
1050 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_value120 vertical:" << value;
1052 break;
1054 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_value120 horizontal:" << value;
1056 break;
1057 default:
1058 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_value120: Unknown axis:" << axis;
1059 return;
1060 }
1061}
1062
1064{
1066 switch (axis) {
1069 break;
1072 break;
1073 default:
1074 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_relative_direction: Unknown axis:" << axis;
1075 }
1076}
1077
1079{
1081 qCDebug(lcQpaWaylandInput) << "Flushing new event; no frame event in this version";
1082 pointer_frame();
1083 }
1084}
1085
1087{
1088 qCDebug(lcQpaWaylandInput) << "Setting frame event " << event->type;
1090 qCDebug(lcQpaWaylandInput) << "Flushing; previous was " << mFrameData.event->type;
1092 }
1093
1095
1097}
1098
1107
1109{
1110 switch (axisSource) {
1111 case axis_source_wheel_tilt: // sideways tilt of the wheel
1112 case axis_source_wheel:
1113 // In the case of wheel events, a pixel delta doesn't really make sense,
1114 // and will make Qt think this is a continuous scroll event when it isn't,
1115 // so just ignore it.
1116 return false;
1117 case axis_source_finger:
1119 return !delta.isNull();
1120 default:
1121 return false;
1122 }
1123}
1124
1126{
1127 if (!hasPixelDelta())
1128 return QPoint();
1129
1131 // Add accumulated rounding error before rounding again
1134 Q_ASSERT(qAbs(accumulatedError->x()) < 1.0);
1135 Q_ASSERT(qAbs(accumulatedError->y()) < 1.0);
1136
1137 // for continuous scroll events things should be
1138 // in the same direction
1139 // i.e converted so downwards surface co-ordinates (positive axis_value)
1140 // goes to downwards in wheel event (negative value)
1141 pixelDelta *= -1;
1142 return pixelDelta;
1143}
1144
1146{
1147 if (delta120.isNull()) {
1148 // If we didn't get any discrete events, then we need to fall back to
1149 // the continuous information.
1150 return (delta * -12).toPoint(); //TODO: why multiply by 12?
1151 }
1152
1153 // The angle delta is in eights of degrees, and our docs says most mice have
1154 // 1 click = 15 degrees, i.e. 120 is one click. It's also in the opposite
1155 // direction of surface space.
1156 return -delta120;
1157}
1158
1160{
1161 switch (axisSource) {
1162 case axis_source_wheel_tilt: // sideways tilt of the wheel
1163 case axis_source_wheel:
1165 case axis_source_finger:
1167 default: // Whatever other sources might be added are probably not mouse wheels
1169 }
1170}
1171
1173{
1175
1176 // The wayland protocol has separate horizontal and vertical axes, Qt has just the one inverted flag
1177 // Pragmatically it should't come up
1179
1180 // Angle delta is required for Qt wheel events, so don't try to send events if it's zero
1181 if (!angleDelta.isNull()) {
1183 if (!mScrollBeginSent) {
1184 if (!target && QWaylandWindow::mouseGrab())
1186 if (!target)
1187 target = focusSurface();
1188 }
1189 if (!target) {
1190 qCDebug(lcQpaWaylandInput) << "Flushing scroll event aborted - no scroll target";
1192 return;
1193 }
1194
1196 qCDebug(lcQpaWaylandInput) << "Flushing scroll event sending ScrollBegin";
1197 if (auto *ww = target->waylandWindow())
1201 mParent->modifiers(), false));
1202 mScrollBeginSent = true;
1205 }
1206
1209
1210 qCDebug(lcQpaWaylandInput) << "Flushing scroll event" << phase << pixelDelta << angleDelta;
1211 if (auto *ww = target->waylandWindow())
1214 }
1215
1216 if (mScrollEnd) {
1217 if (mScrollBeginSent) {
1218 if (auto target = mScrollTarget.get()) {
1219 qCDebug(lcQpaWaylandInput) << "Flushing scroll end event";
1220 if (auto *ww = target->waylandWindow())
1223 }
1224 mScrollBeginSent = false;
1226 } else {
1227 // May receive axis_stop for events we haven't sent a ScrollBegin for because
1228 // most axis_sources do not mandate an axis_stop event to be sent.
1229
1230 // TODO: For now, we just ignore these events, but we could perhaps take this as an
1231 // indication that this compositor will in fact send axis_stop events for these sources
1232 // and send a ScrollBegin the next time an axis_source event with this type is encountered.
1233 }
1234 mScrollEnd = false;
1236 }
1237
1239}
1240
1242{
1244
1245 if (auto *event = mFrameData.event.get()) {
1246 if (auto surface = event->surface) {
1247 if (auto *ww = surface->waylandWindow())
1249 } else if (mFrameData.event->type == QEvent::MouseButtonRelease) {
1250 // If the window has been destroyed, we still need to report an up event, but it can't
1251 // be handled by the destroyed window (obviously), so send the event here instead.
1253 nullptr, event->timestamp,
1256 event->modifiers); // , Qt::MouseEventSource source =
1257 // Qt::MouseEventNotSynthesized);
1258 }
1260 }
1261
1262 //TODO: do modifiers get passed correctly here?
1264}
1265
1267{
1268 return source == axis_source_finger;
1269}
1270
1272{
1274#if QT_CONFIG(xkbcommon)
1276 return;
1277
1279 qCWarning(lcQpaWayland) << "unknown keymap format:" << format;
1280 close(fd);
1281 return;
1282 }
1283
1284 char *map_str = static_cast<char *>(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0));
1285 if (map_str == MAP_FAILED) {
1286 close(fd);
1287 return;
1288 }
1289
1294
1296 close(fd);
1297
1298 if (mXkbKeymap)
1300 else
1301 mXkbState.reset(nullptr);
1302#else
1303 Q_UNUSED(fd);
1304 Q_UNUSED(size);
1305#endif
1306}
1307
1309{
1311 Q_UNUSED(keys);
1312
1313 if (!surface) {
1314 // Ignoring wl_keyboard.enter event with null surface. This is either a compositor bug,
1315 // or it's a race with a wl_surface.destroy request. In either case, ignore the event.
1316 return;
1317 }
1318
1320 if (!window)
1321 return;
1322
1323 if (mFocus) {
1324 qCWarning(lcQpaWayland()) << "Unexpected wl_keyboard.enter event. Keyboard already has focus";
1326 }
1327
1330
1332}
1333
1335{
1337
1338 if (!surface) {
1339 // Either a compositor bug, or a race condition with wl_surface.destroy, ignore the event.
1340 return;
1341 }
1342
1344 if (!window)
1345 return;
1346
1347 if (window->waylandSurface() != mFocus) {
1348 qCWarning(lcQpaWayland) << "Ignoring unexpected wl_keyboard.leave event."
1349 << "wl_surface argument does not match the current focus"
1350 << "This is most likely a compositor bug";
1351 return;
1352 }
1355}
1356
1360 const QString &text, bool autorepeat, ushort count)
1361{
1363 bool filtered = false;
1364
1365 if (inputContext) {
1370 }
1371
1372 if (!filtered) {
1373 QWindow *window = nullptr;
1376 } else if (auto *surface = focusSurface()) {
1377 if (auto *ww = surface->waylandWindow())
1378 window = ww->window();
1379 }
1380 if (!window)
1381 return;
1382
1383 if (type == QEvent::KeyPress && key == Qt::Key_Menu) {
1384 auto cursor = window->screen()->handle()->cursor();
1385 if (cursor) {
1386 const QPoint globalPos = cursor->pos();
1389 }
1390 }
1391
1394 }
1395}
1396
1398{
1400 qCWarning(lcQpaWayland) << Q_FUNC_INFO << "unknown keymap format:" << mKeymapFormat;
1401 return;
1402 }
1403
1404 auto *surface = focusSurface();
1405 if (!surface) {
1406 // We destroyed the keyboard focus surface, but the server didn't get the message yet...
1407 // or the server didn't send an enter event first. In either case, ignore the event.
1408 return;
1409 }
1410
1412
1414 if (isDown)
1416
1418#if QT_CONFIG(xkbcommon)
1419 if ((!mXkbKeymap || !mXkbState) && !createDefaultKeymap())
1420 return;
1421
1422 auto code = key + 8; // map to wl_keyboard::keymap_format::keymap_format_xkb_v1
1423
1426
1429
1432 if (isAutoRepeat && mRepeatRate > 0) {
1433 qCWarning(lcQpaWayland, "received key repeat event while repeat rate is non-zero");
1434 return;
1435 }
1437 qCWarning(lcQpaWayland, "received key repeat event for a key that should not be repeated");
1438 return;
1439 }
1440 if (isAutoRepeat)
1442
1444
1453 } else if (mRepeatKey.code == code) {
1455 }
1456#else
1457 Q_UNUSED(time);
1458 Q_UNUSED(key);
1459 qCWarning(lcQpaWayland, "xkbcommon not available on this build, not performing key mapping");
1460 return;
1461#endif
1463 // raw scan code
1464 return;
1465 }
1466}
1467
1469{
1471}
1472
1474{
1475 mFocus = nullptr;
1478}
1479
1500
1506
1508 uint32_t time,
1509 struct wl_surface *surface,
1510 int32_t id,
1511 wl_fixed_t x,
1512 wl_fixed_t y)
1513{
1514 if (!surface)
1515 return;
1516
1518 if (!window)
1519 return; // Ignore foreign surfaces
1520
1521 mParent->mTime = time;
1523 mFocus = window;
1527}
1528
1530{
1532 mParent->mTime = time;
1534
1535 if (allTouchPointsReleased()) {
1536 mFocus = nullptr;
1537
1538 // As of Weston 7.0.0 there is no touch_frame after the last touch_up
1539 // (i.e. when the last finger is released). To accommodate for this, issue a
1540 // touch_frame. This cannot hurt since it is safe to call the touch_frame
1541 // handler multiple times when there are no points left.
1542 // See: https://gitlab.freedesktop.org/wayland/weston/issues/44
1543 // TODO: change logging category to lcQpaWaylandInput in newer versions.
1544 qCDebug(lcQpaWayland, "Generating fake frame event to work around Weston bug");
1545 touch_frame();
1546 }
1547}
1548
1555
1563
1565{
1567 auto it = std::find_if(mTouch->mPendingTouchPoints.begin(), end, [id](const QWindowSystemInterface::TouchPoint &tp){ return tp.id == id; });
1568 if (it == end) {
1570 it->id = id;
1571 }
1572 // If the touch points were up and down in same frame, send out frame right away
1573 else if ((it->state == QEventPoint::Pressed && state == QEventPoint::Released)
1577 it->id = id;
1578 }
1579
1581
1582 // Only moved and pressed needs to update/set position
1584 // We need a global (screen) position.
1586
1587 //is it possible that mTouchFocus is null;
1588 if (!surface && mPointer)
1590 if (!surface && mKeyboard)
1592 if (!surface)
1593 return;
1594
1595 auto *ww = surface->waylandWindow();
1596 if (!ww || !ww->window())
1597 return;
1598
1599 tp.area = QRectF(0, 0, 8, 8);
1603 }
1604
1605 // If the touch point was pressed earlier this frame, we don't want to overwrite its state.
1606 if (tp.state != QEventPoint::Pressed)
1608
1609 tp.pressure = tp.state == QEventPoint::Released ? 0 : 1;
1610}
1611
1613{
1614 for (const auto &tp : std::as_const(mPendingTouchPoints)) {
1615 if (tp.state != QEventPoint::Released)
1616 return false;
1617 }
1618 return true;
1619}
1620
1631
1633{
1634 // TODO: early return if no events?
1635
1636 QWindow *window = nullptr;
1637 if (mFocus) {
1638 if (auto *ww = mFocus->waylandWindow())
1639 window = ww->window();
1640 }
1641
1642 if (mFocus) {
1643 // Returns a reference to the last item in the list. The list must not be empty.
1644 // If the list can be empty, call isEmpty() before calling this function.
1645 // See: https://doc.qt.io/qt-5.15/qlist.html#last
1647 return;
1649 // When the touch event is received, the global pos is calculated with the margins
1650 // in mind. Now we need to adjust again to get the correct local pos back.
1651 auto *ww = mFocus->waylandWindow();
1652 if (!ww)
1653 return;
1655 QPoint p = tp.area.center().toPoint();
1658 return;
1659 }
1660
1662
1663 // Prepare state for next frame
1666 for (const auto &prevPoint: prevTouchPoints) {
1667 // All non-released touch points should be part of the next touch event
1670 tp.state = QEventPoint::Stationary; // ... as stationary (unless proven otherwise)
1672 }
1673 }
1674
1675}
1676
1677}
1678
1679QT_END_NAMESPACE
1680
1681#include "moc_qwaylandinputdevice_p.cpp"
EnterEvent(QWaylandSurface *surface, const QPointF &local, const QPointF &global)
LeaveEvent(QWaylandSurface *surface, const QPointF &localPos, const QPointF &globalPos)
MotionEvent(QWaylandSurface *surface, ulong timestamp, const QPointF &localPos, const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
PressEvent(QWaylandSurface *surface, ulong timestamp, const QPointF &localPos, const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button, Qt::KeyboardModifiers modifiers)
ReleaseEvent(QWaylandSurface *surface, ulong timestamp, const QPointF &localPos, const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button, Qt::KeyboardModifiers modifiers)
WheelEvent(QWaylandSurface *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)