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
39
40#include <QtGui/private/qpixmap_raster_p.h>
41#include <QtGui/private/qguiapplication_p.h>
42#include <qpa/qplatformwindow.h>
43#include <qpa/qplatforminputcontext.h>
44#include <qpa/qplatformtheme.h>
45#include <QDebug>
46#include <QtCore/qbytearrayview.h>
47#include <QtCore/qscopeguard.h>
48
49#include <unistd.h>
50#include <fcntl.h>
51#include <sys/mman.h>
52
53#if QT_CONFIG(cursor)
54#include <wayland-cursor.h>
55#endif
56
57#include <QtGui/QGuiApplication>
58#include <QtGui/QPointingDevice>
59
60QT_BEGIN_NAMESPACE
61
62namespace QtWaylandClient {
63
64Q_LOGGING_CATEGORY(lcQpaWaylandInput, "qt.qpa.wayland.input");
65
66// The maximum number of concurrent touchpoints is not exposed in wayland, so we assume a
67// reasonable number of them. As of 2021 most touchscreen panels support 10 concurrent touchpoints.
68static const int MaxTouchPoints = 10;
69
71{
72 timeElapsed.start();
73 delayTimer.setSingleShot(true);
74}
75
77{
78 using namespace std::chrono_literals;
79
80 if (!QCoreApplication::testAttribute(Qt::AA_CompressHighFrequencyEvents))
81 return false;
82
83 const auto elapsed = timeElapsed.durationElapsed();
84 timeElapsed.start();
85 if (elapsed < 100us || delayTimer.isActive())
86 {
87 // The highest USB HID polling rate is 8 kHz (125 μs). Most mice use lowe polling rate [125 Hz - 1000 Hz].
88 // Reject all events faster than 100 μs, because it definitely means the application main thread is
89 // freezed by long operation and events are delivered one after another from the queue. Since now we rely
90 // on the 0 ms timer to deliver the last pending event when application main thread is no longer freezed.
91 delayTimer.start(0);
92 return true;
93 }
94
95 return false;
96}
97
99 : mParent(p)
100{
101 init(p->get_keyboard());
102 mRepeatTimer.callOnTimeout(this, [&]() {
103 if (!focusSurface()) {
104 // We destroyed the keyboard focus surface, but the server didn't get the message yet...
105 // or the server didn't send an enter event first.
106 return;
107 }
112 mRepeatKey.text, true);
115 mRepeatKey.text, true);
116 });
117}
118
119#if QT_CONFIG(xkbcommon)
121{
123 if (!ctx)
124 return false;
125
126 struct xkb_rule_names names;
127 names.rules = "evdev";
128 names.model = "pc105";
129 names.layout = "us";
130 names.variant = "";
131 names.options = "";
132
134 if (mXkbKeymap)
136
137 if (!mXkbKeymap || !mXkbState) {
138 qCWarning(lcQpaWayland, "failed to create default keymap");
139 return false;
140 }
141
142 return true;
143}
144#endif
145
155
160
178
180{
181 if (version() >= 3)
183 else
185}
186
191
192#if QT_CONFIG(cursor)
193
195{
196 if (seat()->mQDisplay->compositor()->version() < 3) {
197 return 1;
198 }
199
200 if (auto *s = mCursor.surface.get()) {
201 if (s->outputScale() > 0)
202 return s->outputScale();
203 }
204
206}
207
209{
212
216 }
217
219 cursorThemeName = QStringLiteral("default");
220 if (cursorSize.isEmpty())
221 cursorSize = QSize(24, 24);
222
223 int scale = idealCursorScale();
225 auto *display = seat()->mQDisplay;
227
228 if (!mCursor.theme)
229 return; // A warning has already been printed in loadCursorTheme
230
231 if (auto *arrow = mCursor.theme->cursor(Qt::ArrowCursor)) {
232 int arrowPixelSize = qMax(arrow->images[0]->width, arrow->images[0]->height); // Not all cursor themes are square
233 while (scale > 1 && arrowPixelSize / scale < cursorSize.width())
234 --scale;
235 } else {
236 qCWarning(lcQpaWayland) << "Cursor theme does not support the arrow cursor";
237 }
239}
240
242{
243 if (mEnterSerial == 0)
244 return;
245
246 auto shape = seat()->mCursor.shape;
247
248 if (shape == Qt::BlankCursor) {
249 if (mCursor.surface)
251 set_cursor(mEnterSerial, nullptr, 0, 0);
252 return;
253 }
254
255 if (shape == Qt::BitmapCursor) {
257 if (!buffer) {
258 qCWarning(lcQpaWayland) << "No buffer for bitmap cursor, can't set cursor";
259 return;
260 }
261 auto hotspot = seat()->mCursor.hotspot;
264 return;
265 }
266
267 if (mCursor.shape) {
268 if (mCursor.surface) {
270 }
272 return;
273 }
274
277
278 if (!mCursor.theme)
279 return;
280
281 // Set from shape using theme
283 const uint time = timer.isValid() ? timer.elapsed() : 0;
284
285 if (struct ::wl_cursor *waylandCursor = mCursor.theme->cursor(shape)) {
286 uint duration = 0;
289
291 if (!buffer) {
292 qCWarning(lcQpaWayland) << "Could not find buffer for cursor" << shape;
293 return;
294 }
298 bool animated = duration > 0;
299 if (animated) {
303 }
305 return;
306 }
307
308 qCWarning(lcQpaWayland) << "Unable to change to cursor" << shape;
309}
310
313{
314 if (!mCursor.surface)
316 return mCursor.surface.get();
317}
318
320{
323 updateCursor();
324 }
325}
326
328{
331 updateCursor();
332 }
333}
334
335#endif // QT_CONFIG(cursor)
336
342
344{
345 if (version() >= 3)
347 else
349}
350
355 , mId(id)
356{
357
358#if QT_CONFIG(clipboard)
361 }
362#endif
363
364#if QT_CONFIG(wayland_datadevice)
367 }
368#endif
369
370#if QT_CONFIG(wayland_client_primary_selection)
371 // TODO: Could probably decouple this more if there was a signal for new seat added
374#endif
375
380 }
381
384
387
390
391#if QT_CONFIG(tabletevent)
392 if (auto *tm = mQDisplay->tabletManager())
394#endif
395}
396
404
406{
407 mCaps = caps;
408
411 } else if (!(mCaps & WL_SEAT_CAPABILITY_KEYBOARD) && mKeyboard) {
413 }
414
417
419 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
588
593
595{
596 return mTouch && mTouch->mFocus ? mTouch->mFocus->waylandWindow() : nullptr;
597}
598
603
605{
606#if QT_CONFIG(xkbcommon)
609#else
611#endif
612 return {};
613}
614
616{
617 if (!mKeyboard)
618 return Qt::NoModifier;
619
620 return mKeyboard->modifiers();
621}
622
624{
626
627#if QT_CONFIG(xkbcommon)
628 if (!mXkbState)
629 return ret;
630
632#endif
633
634 return ret;
635}
636
637#if QT_CONFIG(cursor)
639{
641 mCursor = CursorState(); // Clear any previous state
646
647 if (mCursor.shape == Qt::BitmapCursor) {
650 mCursor.bitmapScale = int(dpr); // Wayland doesn't support fractional buffer scale
651 // If there was a fractional part of the dpr, we need to scale the hotspot accordingly
652 if (mCursor.bitmapScale < dpr)
654 }
655
656 // Return early if setCursor was called redundantly (mostly happens from decorations)
661 return;
662 }
663
664 if (mPointer)
666
667#if QT_CONFIG(tabletevent)
668 if (mTabletSeat)
670#endif
671}
672#endif
673
675{
676public:
677 EnterEvent(QWaylandSurface *surface, const QPointF &local, const QPointF &global)
680 {}
681};
682
685{
686 if (!wlSurface)
687 return;
688
690
691 if (!surface)
692 return; // Ignore foreign surfaces
693
694 if (mFocus) {
695 qCWarning(lcQpaWayland) << "The compositor sent a wl_pointer.enter event before sending a"
696 << "leave event first, this is not allowed by the wayland protocol"
697 << "attempting to work around it by invalidating the current focus";
699 }
700 mFocus = surface;
702
704 if (auto *ww = surface->waylandWindow())
706 else if (auto *decoration = surface->decoration())
708
711
714 }
715
716#if QT_CONFIG(cursor)
717 // Depends on mEnterSerial being updated
718 updateCursor();
719#endif
720
722 if (!grab)
724}
725
727{
728public:
729 LeaveEvent(QWaylandSurface *surface, const QPointF &localPos, const QPointF &globalPos)
732 {}
733};
734
736{
738
741
742 // The event may arrive after destroying the window, indicated by
743 // a null surface.
744 if (!wlSurface)
745 return;
746
748 if (!surface)
749 return; // Ignore foreign surfaces
750
753}
754
756{
757public:
758 MotionEvent(QWaylandSurface *surface, ulong timestamp, const QPointF &localPos,
759 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
762 {
763 }
764};
765
767{
769 if (!surface) {
770 // We destroyed the pointer focus surface, but the server didn't get the message yet...
771 // or the server didn't send an enter event first. In either case, ignore the event.
772 return;
773 }
774
777 if (auto *ww = surface->waylandWindow())
779 else if (auto *decoration = surface->decoration())
781
784 mParent->mTime = time;
785
787 if (grab && grab->waylandSurface() != surface) {
788 // We can't know the true position since we're getting events for another surface,
789 // so we just set it outside of the window boundaries.
790 pos = QPointF(-1, -1);
793 }
795}
796
798{
799public:
800 PressEvent(QWaylandSurface *surface, ulong timestamp, const QPointF &localPos,
801 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button,
802 Qt::KeyboardModifiers modifiers)
805 {
806 }
807};
808
810{
811public:
812 ReleaseEvent(QWaylandSurface *surface, ulong timestamp, const QPointF &localPos,
813 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button,
814 Qt::KeyboardModifiers modifiers)
817 {
818 }
819};
820
823{
825 if (!surface) {
826 // We destroyed the pointer focus surface, but the server didn't get the message yet...
827 // or the server didn't send an enter event first. In either case, ignore the event.
828 return;
829 }
830
832
833 // translate from kernel (input.h) 'button' to corresponding Qt:MouseButton.
834 // The range of mouse values is 0x110 <= mouse_button < 0x120, the first Joystick button.
835 switch (button) {
836 case 0x110: qt_button = Qt::LeftButton; break; // kernel BTN_LEFT
837 case 0x111: qt_button = Qt::RightButton; break;
838 case 0x112: qt_button = Qt::MiddleButton; break;
839 case 0x113: qt_button = Qt::ExtraButton1; break; // AKA Qt::BackButton
840 case 0x114: qt_button = Qt::ExtraButton2; break; // AKA Qt::ForwardButton
841 case 0x115: qt_button = Qt::ExtraButton3; break; // AKA Qt::TaskButton
842 case 0x116: qt_button = Qt::ExtraButton4; break;
843 case 0x117: qt_button = Qt::ExtraButton5; break;
844 case 0x118: qt_button = Qt::ExtraButton6; break;
845 case 0x119: qt_button = Qt::ExtraButton7; break;
846 case 0x11a: qt_button = Qt::ExtraButton8; break;
847 case 0x11b: qt_button = Qt::ExtraButton9; break;
848 case 0x11c: qt_button = Qt::ExtraButton10; break;
849 case 0x11d: qt_button = Qt::ExtraButton11; break;
850 case 0x11e: qt_button = Qt::ExtraButton12; break;
851 case 0x11f: qt_button = Qt::ExtraButton13; break;
852 default: return; // invalid button number (as far as Qt is concerned)
853 }
854
856 const bool firstPress = state && mButtons == Qt::NoButton;
857
858 if (state)
860 else
862
863 mParent->mTime = time;
865 if (state)
867
869
872 if (grab && grab->waylandSurface() != focusSurface()) {
873 pos = QPointF(-1, -1);
875
877 }
878
879 if (firstPress)
881
882 if (state) {
883 if (auto *window = surface->waylandWindow())
886 } else {
888 if (mButtons == Qt::NoButton)
890 }
891}
892
894{
895 if (mFocus) {
897 mFocus = nullptr;
898 }
899 mEnterSerial = 0;
900}
901
912
923
925{
926public:
927 WheelEvent(QWaylandSurface *surface, Qt::ScrollPhase phase, ulong timestamp, const QPointF &local,
928 const QPointF &global, const QPoint &pixelDelta, const QPoint &angleDelta,
929 Qt::MouseEventSource source, Qt::KeyboardModifiers modifiers, bool inverted)
934 {
935 }
936};
937
939{
940 if (!focusSurface()) {
941 // We destroyed the pointer focus surface, but the server didn't get the message yet...
942 // or the server didn't send an enter event first. In either case, ignore the event.
943 return;
944 }
945
946 // Get the delta and convert it into the expected range
947 switch (axis) {
950 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis vertical:" << mFrameData.delta.y();
951 break;
954 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis horizontal:" << mFrameData.delta.x();
955 break;
956 default:
957 //TODO: is this really needed?
958 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis: Unknown axis:" << axis;
959 return;
960 }
961
962 mParent->mTime = time;
963
965}
966
968{
970 qCDebug(lcQpaWaylandInput) << "compressed pointer_frame event";
971 return;
972 }
973
975}
976
978{
979 switch (source) {
981 qCDebug(lcQpaWaylandInput) << "Axis source wheel";
982 break;
984 qCDebug(lcQpaWaylandInput) << "Axis source finger";
985 break;
987 qCDebug(lcQpaWaylandInput) << "Axis source continuous";
988 break;
990 qCDebug(lcQpaWaylandInput) << "Axis source wheel tilt";
991 }
993}
994
996{
997 if (!focusSurface())
998 return;
999
1000 mParent->mTime = time;
1001 switch (axis) {
1003 qCDebug(lcQpaWaylandInput) << "Received vertical wl_pointer.axis_stop";
1004 break;
1006 qCDebug(lcQpaWaylandInput) << "Received horizontal wl_pointer.axis_stop";
1007 break;
1008 default:
1009 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_stop: Unknown axis: " << axis
1010 << "This is most likely a compositor bug";
1011 return;
1012 }
1013
1014 mScrollEnd = true;
1015}
1016
1018{
1019 if (!focusSurface())
1020 return;
1021
1022 const int32_t delta120 = value * 15 * 8;
1023
1024 switch (axis) {
1026 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete vertical:" << value;
1028 break;
1030 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete horizontal:" << value;
1032 break;
1033 default:
1034 //TODO: is this really needed?
1035 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_discrete: Unknown axis:" << axis;
1036 return;
1037 }
1038}
1039
1041{
1042 if (!focusSurface())
1043 return;
1044
1045 switch (axis) {
1047 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_value120 vertical:" << value;
1049 break;
1051 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_value120 horizontal:" << value;
1053 break;
1054 default:
1055 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_value120: Unknown axis:" << axis;
1056 return;
1057 }
1058}
1059
1061{
1063 switch (axis) {
1066 break;
1069 break;
1070 default:
1071 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_relative_direction: Unknown axis:" << axis;
1072 }
1073}
1074
1076{
1078 qCDebug(lcQpaWaylandInput) << "Flushing new event; no frame event in this version";
1079 pointer_frame();
1080 }
1081}
1082
1084{
1085 qCDebug(lcQpaWaylandInput) << "Setting frame event " << event->type;
1087 qCDebug(lcQpaWaylandInput) << "Flushing; previous was " << mFrameData.event->type;
1089 }
1090
1092
1094}
1095
1104
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 && QWaylandWindow::mouseGrab())
1163 if (!target)
1164 target = focusSurface();
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";
1174 if (auto *ww = target->waylandWindow())
1178 mParent->modifiers(), false));
1179 mScrollBeginSent = true;
1182 }
1183
1186
1187 qCDebug(lcQpaWaylandInput) << "Flushing scroll event" << phase << pixelDelta << angleDelta;
1188 if (auto *ww = target->waylandWindow())
1191 }
1192
1193 if (mScrollEnd) {
1194 if (mScrollBeginSent) {
1195 if (auto target = mScrollTarget.get()) {
1196 qCDebug(lcQpaWaylandInput) << "Flushing scroll end event";
1197 if (auto *ww = target->waylandWindow())
1200 }
1201 mScrollBeginSent = false;
1203 } else {
1204 // May receive axis_stop for events we haven't sent a ScrollBegin for because
1205 // most axis_sources do not mandate an axis_stop event to be sent.
1206
1207 // TODO: For now, we just ignore these events, but we could perhaps take this as an
1208 // indication that this compositor will in fact send axis_stop events for these sources
1209 // and send a ScrollBegin the next time an axis_source event with this type is encountered.
1210 }
1211 mScrollEnd = false;
1213 }
1214
1216}
1217
1219{
1221
1222 if (auto *event = mFrameData.event.get()) {
1223 if (auto surface = event->surface) {
1224 if (auto *decoration = surface->decoration())
1226 if (auto *ww = surface->waylandWindow())
1228 } else if (mFrameData.event->type == QEvent::MouseButtonRelease) {
1229 // If the window has been destroyed, we still need to report an up event, but it can't
1230 // be handled by the destroyed window (obviously), so send the event here instead.
1232 nullptr, event->timestamp,
1235 event->modifiers); // , Qt::MouseEventSource source =
1236 // Qt::MouseEventNotSynthesized);
1237 }
1239 }
1240
1241 //TODO: do modifiers get passed correctly here?
1243}
1244
1246{
1247 return source == axis_source_finger;
1248}
1249
1251{
1252 const auto fdCloser = qScopeGuard([fd] { close(fd); });
1254#if QT_CONFIG(xkbcommon)
1256 return;
1257
1259 qCWarning(lcQpaWayland) << "unknown keymap format:" << format;
1260 return;
1261 }
1262
1263 char *map_str = static_cast<char *>(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0));
1264 if (map_str == MAP_FAILED)
1265 return;
1266
1267 // We need to be able to cope with a non 0-terminated map_str, but we also need to make
1268 // sure that map_str does not contain any 0-bytes at all, as older libxkbcommon cannot
1269 // deal with those and fail (e.g. RHEL 9.6 and Ubuntu 22.04)
1271 map_str,
1275
1276 if (mXkbKeymap) {
1278 } else {
1279 qCDebug(lcQpaWaylandInput) << "failed to compile a keymap, size =" << size
1280 << ", keymap string:\n"
1282 }
1283
1285
1286 if (mXkbKeymap)
1288 else
1289 mXkbState.reset(nullptr);
1290#else
1291 Q_UNUSED(size);
1292#endif
1293}
1294
1296{
1298 Q_UNUSED(keys);
1299
1300 if (!surface) {
1301 // Ignoring wl_keyboard.enter event with null surface. This is either a compositor bug,
1302 // or it's a race with a wl_surface.destroy request. In either case, ignore the event.
1303 return;
1304 }
1305
1307 if (!window)
1308 return;
1309
1310 if (mFocus) {
1311 qCWarning(lcQpaWayland()) << "Unexpected wl_keyboard.enter event. Keyboard already has focus";
1313 }
1314
1317
1319}
1320
1322{
1324
1325 if (!surface) {
1326 // Either a compositor bug, or a race condition with wl_surface.destroy, ignore the event.
1327 return;
1328 }
1329
1331 if (!qtSurface)
1332 return;
1334 if (!window)
1335 return;
1336
1337 if (window->waylandSurface() != mFocus) {
1338 qCWarning(lcQpaWayland) << "Ignoring unexpected wl_keyboard.leave event."
1339 << "wl_surface argument does not match the current focus"
1340 << "This is most likely a compositor bug";
1341 return;
1342 }
1345}
1346
1350 const QString &text, bool autorepeat, ushort count)
1351{
1353 bool filtered = false;
1354
1355 if (inputContext) {
1360 }
1361
1362 if (!filtered) {
1363 QWindow *window = nullptr;
1366 } else if (auto *surface = focusSurface()) {
1367 if (auto *ww = surface->waylandWindow())
1368 window = ww->window();
1369 }
1370 if (!window)
1371 return;
1372
1373 if (type == QEvent::KeyPress && key == Qt::Key_Menu) {
1374 auto cursor = window->screen()->handle()->cursor();
1375 if (cursor) {
1376 const QPoint globalPos = cursor->pos();
1379 }
1380 }
1381
1384 }
1385}
1386
1388{
1390 qCWarning(lcQpaWayland) << Q_FUNC_INFO << "unknown keymap format:" << mKeymapFormat;
1391 return;
1392 }
1393
1394 auto *surface = focusSurface();
1395 if (!surface) {
1396 // We destroyed the keyboard focus surface, but the server didn't get the message yet...
1397 // or the server didn't send an enter event first. In either case, ignore the event.
1398 return;
1399 }
1400
1402
1404 if (isDown)
1406
1408#if QT_CONFIG(xkbcommon)
1409 if ((!mXkbKeymap || !mXkbState) && !createDefaultKeymap())
1410 return;
1411
1412 auto code = key + 8; // map to wl_keyboard::keymap_format::keymap_format_xkb_v1
1413
1416
1419
1422 if (isAutoRepeat && mRepeatRate > 0) {
1423 qCWarning(lcQpaWayland, "received key repeat event while repeat rate is non-zero");
1424 return;
1425 }
1427 qCWarning(lcQpaWayland, "received key repeat event for a key that should not be repeated");
1428 return;
1429 }
1430 if (isAutoRepeat)
1432
1434
1443 } else if (mRepeatKey.code == code) {
1445 }
1446#else
1447 Q_UNUSED(time);
1448 Q_UNUSED(key);
1449 qCWarning(lcQpaWayland, "xkbcommon not available on this build, not performing key mapping");
1450 return;
1451#endif
1453 // raw scan code
1454 return;
1455 }
1456}
1457
1459{
1461}
1462
1464{
1465 mFocus = nullptr;
1468}
1469
1490
1496
1498 uint32_t time,
1499 struct wl_surface *surface,
1500 int32_t id,
1501 wl_fixed_t x,
1502 wl_fixed_t y)
1503{
1504 if (!surface)
1505 return;
1506
1508 if (!window)
1509 return; // Ignore foreign surfaces
1510
1511 mParent->mTime = time;
1513 mFocus = window;
1517}
1518
1520{
1522 mParent->mTime = time;
1524
1525 if (allTouchPointsReleased()) {
1526 mFocus = nullptr;
1527
1528 // As of Weston 7.0.0 there is no touch_frame after the last touch_up
1529 // (i.e. when the last finger is released). To accommodate for this, issue a
1530 // touch_frame. This cannot hurt since it is safe to call the touch_frame
1531 // handler multiple times when there are no points left.
1532 // See: https://gitlab.freedesktop.org/wayland/weston/issues/44
1533 // TODO: change logging category to lcQpaWaylandInput in newer versions.
1534 qCDebug(lcQpaWayland, "Generating fake frame event to work around Weston bug");
1535 touch_frame();
1536 }
1537}
1538
1545
1553
1555{
1557 auto it = std::find_if(mTouch->mPendingTouchPoints.begin(), end, [id](const QWindowSystemInterface::TouchPoint &tp){ return tp.id == id; });
1558 if (it == end) {
1560 it->id = id;
1561 }
1562 // If the touch points were up and down in same frame, send out frame right away
1563 else if ((it->state == QEventPoint::Pressed && state == QEventPoint::Released)
1567 it->id = id;
1568 }
1569
1571
1572 // Only moved and pressed needs to update/set position
1574 // We need a global (screen) position.
1576
1577 //is it possible that mTouchFocus is null;
1578 if (!surface && mPointer)
1580 if (!surface && mKeyboard)
1582 if (!surface)
1583 return;
1584
1585 auto *ww = surface->waylandWindow();
1586 if (!ww || !ww->window())
1587 return;
1588
1589 tp.area = QRectF(0, 0, 8, 8);
1592 }
1593
1594 // If the touch point was pressed earlier this frame, we don't want to overwrite its state.
1595 if (tp.state != QEventPoint::Pressed)
1597
1598 tp.pressure = tp.state == QEventPoint::Released ? 0 : 1;
1599}
1600
1602{
1603 for (const auto &tp : std::as_const(mPendingTouchPoints)) {
1604 if (tp.state != QEventPoint::Released)
1605 return false;
1606 }
1607 return true;
1608}
1609
1620
1622{
1623 // TODO: early return if no events?
1624
1625 QWindow *window = nullptr;
1626 if (mFocus) {
1627 if (auto *ww = mFocus->waylandWindow())
1628 window = ww->window();
1629 }
1630
1631 if (mFocus) {
1632 // Returns a reference to the last item in the list. The list must not be empty.
1633 // If the list can be empty, call isEmpty() before calling this function.
1634 // See: https://doc.qt.io/qt-5.15/qlist.html#last
1636 return;
1637 }
1638
1640
1641 // Prepare state for next frame
1644 for (const auto &prevPoint: prevTouchPoints) {
1645 // All non-released touch points should be part of the next touch event
1648 tp.state = QEventPoint::Stationary; // ... as stationary (unless proven otherwise)
1650 }
1651 }
1652
1653}
1654
1655}
1656
1657QT_END_NAMESPACE
1658
1659#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)