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#include <QtCore/qbytearrayview.h>
46#include <QtCore/qscopeguard.h>
47
48#include <unistd.h>
49#include <fcntl.h>
50#include <sys/mman.h>
51
52#if QT_CONFIG(cursor)
53#include <wayland-cursor.h>
54#endif
55
56#include <QtGui/QGuiApplication>
57#include <QtGui/QPointingDevice>
58
59QT_BEGIN_NAMESPACE
60
61namespace QtWaylandClient {
62
63Q_LOGGING_CATEGORY(lcQpaWaylandInput, "qt.qpa.wayland.input");
64
65// The maximum number of concurrent touchpoints is not exposed in wayland, so we assume a
66// reasonable number of them. As of 2021 most touchscreen panels support 10 concurrent touchpoints.
67static const int MaxTouchPoints = 10;
68
70{
71 timeElapsed.start();
72 delayTimer.setSingleShot(true);
73}
74
76{
77 using namespace std::chrono_literals;
78
79 if (!QCoreApplication::testAttribute(Qt::AA_CompressHighFrequencyEvents))
80 return false;
81
82 const auto elapsed = timeElapsed.durationElapsed();
83 timeElapsed.start();
84 if (elapsed < 100us || delayTimer.isActive())
85 {
86 // The highest USB HID polling rate is 8 kHz (125 μs). Most mice use lowe polling rate [125 Hz - 1000 Hz].
87 // Reject all events faster than 100 μs, because it definitely means the application main thread is
88 // freezed by long operation and events are delivered one after another from the queue. Since now we rely
89 // on the 0 ms timer to deliver the last pending event when application main thread is no longer freezed.
90 delayTimer.start(0);
91 return true;
92 }
93
94 return false;
95}
96
98 : mParent(p)
99{
100 init(p->get_keyboard());
101 mRepeatTimer.callOnTimeout(this, [&]() {
102 if (!focusSurface()) {
103 // We destroyed the keyboard focus surface, but the server didn't get the message yet...
104 // or the server didn't send an enter event first.
105 return;
106 }
111 mRepeatKey.text, true);
114 mRepeatKey.text, true);
115 });
116}
117
118#if QT_CONFIG(xkbcommon)
120{
122 if (!ctx)
123 return false;
124
125 struct xkb_rule_names names;
126 names.rules = "evdev";
127 names.model = "pc105";
128 names.layout = "us";
129 names.variant = "";
130 names.options = "";
131
133 if (mXkbKeymap)
135
136 if (!mXkbKeymap || !mXkbState) {
137 qCWarning(lcQpaWayland, "failed to create default keymap");
138 return false;
139 }
140
141 return true;
142}
143#endif
144
154
159
177
179{
180 if (version() >= 3)
182 else
184}
185
190
191#if QT_CONFIG(cursor)
192
194{
195 if (seat()->mQDisplay->compositor()->version() < 3) {
196 return 1;
197 }
198
199 if (auto *s = mCursor.surface.get()) {
200 if (s->outputScale() > 0)
201 return s->outputScale();
202 }
203
205}
206
208{
211
215 }
216
218 cursorThemeName = QStringLiteral("default");
219 if (cursorSize.isEmpty())
220 cursorSize = QSize(24, 24);
221
222 int scale = idealCursorScale();
224 auto *display = seat()->mQDisplay;
226
227 if (!mCursor.theme)
228 return; // A warning has already been printed in loadCursorTheme
229
230 if (auto *arrow = mCursor.theme->cursor(Qt::ArrowCursor)) {
231 int arrowPixelSize = qMax(arrow->images[0]->width, arrow->images[0]->height); // Not all cursor themes are square
232 while (scale > 1 && arrowPixelSize / scale < cursorSize.width())
233 --scale;
234 } else {
235 qCWarning(lcQpaWayland) << "Cursor theme does not support the arrow cursor";
236 }
238}
239
241{
242 if (mEnterSerial == 0)
243 return;
244
245 auto shape = seat()->mCursor.shape;
246
247 if (shape == Qt::BlankCursor) {
248 if (mCursor.surface)
250 set_cursor(mEnterSerial, nullptr, 0, 0);
251 return;
252 }
253
254 if (shape == Qt::BitmapCursor) {
256 if (!buffer) {
257 qCWarning(lcQpaWayland) << "No buffer for bitmap cursor, can't set cursor";
258 return;
259 }
260 auto hotspot = seat()->mCursor.hotspot;
263 return;
264 }
265
266 if (mCursor.shape) {
267 if (mCursor.surface) {
269 }
271 return;
272 }
273
276
277 if (!mCursor.theme)
278 return;
279
280 // Set from shape using theme
282 const uint time = timer.isValid() ? timer.elapsed() : 0;
283
284 if (struct ::wl_cursor *waylandCursor = mCursor.theme->cursor(shape)) {
285 uint duration = 0;
288
290 if (!buffer) {
291 qCWarning(lcQpaWayland) << "Could not find buffer for cursor" << shape;
292 return;
293 }
297 bool animated = duration > 0;
298 if (animated) {
302 }
304 return;
305 }
306
307 qCWarning(lcQpaWayland) << "Unable to change to cursor" << shape;
308}
309
312{
313 if (!mCursor.surface)
315 return mCursor.surface.get();
316}
317
319{
322 updateCursor();
323 }
324}
325
327{
330 updateCursor();
331 }
332}
333
334#endif // QT_CONFIG(cursor)
335
341
343{
344 if (version() >= 3)
346 else
348}
349
354 , mId(id)
355{
356
357#if QT_CONFIG(clipboard)
360 }
361#endif
362
363#if QT_CONFIG(wayland_datadevice)
366 }
367#endif
368
369#if QT_CONFIG(wayland_client_primary_selection)
370 // TODO: Could probably decouple this more if there was a signal for new seat added
373#endif
374
379 }
380
383
386
389
390#if QT_CONFIG(tabletevent)
391 if (auto *tm = mQDisplay->tabletManager())
393#endif
394}
395
403
405{
406 mCaps = caps;
407
410 } else if (!(mCaps & WL_SEAT_CAPABILITY_KEYBOARD) && mKeyboard) {
412 }
413
416
418 if (pointerGestures) {
419 // NOTE: The name of the device and its system ID are not exposed on Wayland.
427 }
428 } else if (!(mCaps & WL_SEAT_CAPABILITY_POINTER) && mPointer) {
429 mPointer.reset();
432 }
433
435 mTouch.reset(createTouch(this));
436
437 if (!mTouchDevice) {
438 // TODO number of touchpoints, actual name and ID
444 }
445 } else if (!(mCaps & WL_SEAT_CAPABILITY_TOUCH) && mTouch) {
446 mTouch.reset();
447 }
448}
449
454
459
464
469
474
479
484
489
491{
492 return mTouch.get();
493}
494
502
508
509#if QT_CONFIG(wayland_datadevice)
511{
513}
514
516{
517 return mDataDevice;
518}
519#endif
520
521#if QT_CONFIG(clipboard)
523{
525}
526
528{
529 return mDataControlDevice.get();
530}
531#endif
532
533#if QT_CONFIG(wayland_client_primary_selection)
535{
537}
538
540{
542}
543#endif
544
549
550#if QT_CONFIG(tabletevent)
552{
554}
555
557{
558 return mTabletSeat.get();
559}
560#endif
561
566
571
576
582
584{
585 //TODO GUARD!
586 return mPointer && mPointer->focusSurface() ? mPointer->focusSurface()->waylandWindow() : nullptr;
587}
588
590{
591 //GUARD!
592
594}
595
597{
598 //GUARD!
599
600 return mTouch && mTouch->mFocus ? mTouch->mFocus->waylandWindow() : nullptr;
601}
602
607
609{
610#if QT_CONFIG(xkbcommon)
613#else
615#endif
616 return {};
617}
618
620{
621 if (!mKeyboard)
622 return Qt::NoModifier;
623
624 return mKeyboard->modifiers();
625}
626
628{
630
631#if QT_CONFIG(xkbcommon)
632 if (!mXkbState)
633 return ret;
634
636#endif
637
638 return ret;
639}
640
641#if QT_CONFIG(cursor)
643{
645 mCursor = CursorState(); // Clear any previous state
650
651 if (mCursor.shape == Qt::BitmapCursor) {
654 mCursor.bitmapScale = int(dpr); // Wayland doesn't support fractional buffer scale
655 // If there was a fractional part of the dpr, we need to scale the hotspot accordingly
656 if (mCursor.bitmapScale < dpr)
658 }
659
660 // Return early if setCursor was called redundantly (mostly happens from decorations)
665 return;
666 }
667
668 if (mPointer)
670
671#if QT_CONFIG(tabletevent)
672 if (mTabletSeat)
674#endif
675}
676#endif
677
679{
680public:
681 EnterEvent(QWaylandSurface *surface, const QPointF &local, const QPointF &global)
684 {}
685};
686
689{
690 if (!wlSurface)
691 return;
692
694
695 if (!surface)
696 return; // Ignore foreign surfaces
697
698 if (mFocus) {
699 qCWarning(lcQpaWayland) << "The compositor sent a wl_pointer.enter event before sending a"
700 << "leave event first, this is not allowed by the wayland protocol"
701 << "attempting to work around it by invalidating the current focus";
703 }
704 mFocus = surface;
706
708 if (auto *ww = surface->waylandWindow())
710 else
711 return;
712
715
718 }
719
720#if QT_CONFIG(cursor)
721 // Depends on mEnterSerial being updated
722 updateCursor();
723#endif
724
726 if (!grab)
728}
729
731{
732public:
733 LeaveEvent(QWaylandSurface *surface, const QPointF &localPos, const QPointF &globalPos)
736 {}
737};
738
740{
742
745
746 // The event may arrive after destroying the window, indicated by
747 // a null surface.
748 if (!wlSurface)
749 return;
750
752 if (!surface)
753 return; // Ignore foreign surfaces
754
757}
758
760{
761public:
762 MotionEvent(QWaylandSurface *surface, ulong timestamp, const QPointF &localPos,
763 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
766 {
767 }
768};
769
771{
773 if (!surface) {
774 // We destroyed the pointer focus surface, but the server didn't get the message yet...
775 // or the server didn't send an enter event first. In either case, ignore the event.
776 return;
777 }
778
781 if (auto *ww = surface->waylandWindow())
783 else
784 return;
785
788 mParent->mTime = time;
789
791 if (grab && grab->waylandSurface() != surface) {
792 // We can't know the true position since we're getting events for another surface,
793 // so we just set it outside of the window boundaries.
794 pos = QPointF(-1, -1);
797 }
799}
800
802{
803public:
804 PressEvent(QWaylandSurface *surface, ulong timestamp, const QPointF &localPos,
805 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button,
806 Qt::KeyboardModifiers modifiers)
809 {
810 }
811};
812
814{
815public:
816 ReleaseEvent(QWaylandSurface *surface, ulong timestamp, const QPointF &localPos,
817 const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button,
818 Qt::KeyboardModifiers modifiers)
821 {
822 }
823};
824
827{
829 if (!surface) {
830 // We destroyed the pointer focus surface, but the server didn't get the message yet...
831 // or the server didn't send an enter event first. In either case, ignore the event.
832 return;
833 }
834
836
837 // translate from kernel (input.h) 'button' to corresponding Qt:MouseButton.
838 // The range of mouse values is 0x110 <= mouse_button < 0x120, the first Joystick button.
839 switch (button) {
840 case 0x110: qt_button = Qt::LeftButton; break; // kernel BTN_LEFT
841 case 0x111: qt_button = Qt::RightButton; break;
842 case 0x112: qt_button = Qt::MiddleButton; break;
843 case 0x113: qt_button = Qt::ExtraButton1; break; // AKA Qt::BackButton
844 case 0x114: qt_button = Qt::ExtraButton2; break; // AKA Qt::ForwardButton
845 case 0x115: qt_button = Qt::ExtraButton3; break; // AKA Qt::TaskButton
846 case 0x116: qt_button = Qt::ExtraButton4; break;
847 case 0x117: qt_button = Qt::ExtraButton5; break;
848 case 0x118: qt_button = Qt::ExtraButton6; break;
849 case 0x119: qt_button = Qt::ExtraButton7; break;
850 case 0x11a: qt_button = Qt::ExtraButton8; break;
851 case 0x11b: qt_button = Qt::ExtraButton9; break;
852 case 0x11c: qt_button = Qt::ExtraButton10; break;
853 case 0x11d: qt_button = Qt::ExtraButton11; break;
854 case 0x11e: qt_button = Qt::ExtraButton12; break;
855 case 0x11f: qt_button = Qt::ExtraButton13; break;
856 default: return; // invalid button number (as far as Qt is concerned)
857 }
858
860 const bool firstPress = state && mButtons == Qt::NoButton;
861
862 if (state)
864 else
866
867 mParent->mTime = time;
869 if (state)
871
873
876 if (grab && grab->waylandSurface() != focusSurface()) {
877 pos = QPointF(-1, -1);
879
881 }
882
883 if (firstPress)
885
886 if (state) {
887 if (auto *window = surface->waylandWindow())
890 } else {
892 if (mButtons == Qt::NoButton)
894 }
895}
896
898{
899 if (mFocus) {
901 mFocus = nullptr;
902 }
903 mEnterSerial = 0;
904}
905
916
918{
919 if (auto *surface = focusSurface()) {
921 if (auto *ww = surface->waylandWindow())
923 }
924}
925
927{
928public:
929 WheelEvent(QWaylandSurface *surface, Qt::ScrollPhase phase, ulong timestamp, const QPointF &local,
930 const QPointF &global, const QPoint &pixelDelta, const QPoint &angleDelta,
931 Qt::MouseEventSource source, Qt::KeyboardModifiers modifiers, bool inverted)
936 {
937 }
938};
939
941{
942 if (!focusSurface()) {
943 // We destroyed the pointer focus surface, but the server didn't get the message yet...
944 // or the server didn't send an enter event first. In either case, ignore the event.
945 return;
946 }
947
948 // Get the delta and convert it into the expected range
949 switch (axis) {
952 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis vertical:" << mFrameData.delta.y();
953 break;
956 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis horizontal:" << mFrameData.delta.x();
957 break;
958 default:
959 //TODO: is this really needed?
960 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis: Unknown axis:" << axis;
961 return;
962 }
963
964 mParent->mTime = time;
965
967}
968
970{
972 qCDebug(lcQpaWaylandInput) << "compressed pointer_frame event";
973 return;
974 }
975
977}
978
980{
981 switch (source) {
983 qCDebug(lcQpaWaylandInput) << "Axis source wheel";
984 break;
986 qCDebug(lcQpaWaylandInput) << "Axis source finger";
987 break;
989 qCDebug(lcQpaWaylandInput) << "Axis source continuous";
990 break;
992 qCDebug(lcQpaWaylandInput) << "Axis source wheel tilt";
993 }
995}
996
998{
999 if (!focusSurface())
1000 return;
1001
1002 mParent->mTime = time;
1003 switch (axis) {
1005 qCDebug(lcQpaWaylandInput) << "Received vertical wl_pointer.axis_stop";
1006 break;
1008 qCDebug(lcQpaWaylandInput) << "Received horizontal wl_pointer.axis_stop";
1009 break;
1010 default:
1011 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_stop: Unknown axis: " << axis
1012 << "This is most likely a compositor bug";
1013 return;
1014 }
1015
1016 mScrollEnd = true;
1017}
1018
1020{
1021 if (!focusSurface())
1022 return;
1023
1024 const int32_t delta120 = value * 15 * 8;
1025
1026 switch (axis) {
1028 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete vertical:" << value;
1030 break;
1032 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete horizontal:" << value;
1034 break;
1035 default:
1036 //TODO: is this really needed?
1037 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_discrete: Unknown axis:" << axis;
1038 return;
1039 }
1040}
1041
1043{
1044 if (!focusSurface())
1045 return;
1046
1047 switch (axis) {
1049 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_value120 vertical:" << value;
1051 break;
1053 qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_value120 horizontal:" << value;
1055 break;
1056 default:
1057 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_value120: Unknown axis:" << axis;
1058 return;
1059 }
1060}
1061
1063{
1065 switch (axis) {
1068 break;
1071 break;
1072 default:
1073 qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_relative_direction: Unknown axis:" << axis;
1074 }
1075}
1076
1078{
1080 qCDebug(lcQpaWaylandInput) << "Flushing new event; no frame event in this version";
1081 pointer_frame();
1082 }
1083}
1084
1086{
1087 qCDebug(lcQpaWaylandInput) << "Setting frame event " << event->type;
1089 qCDebug(lcQpaWaylandInput) << "Flushing; previous was " << mFrameData.event->type;
1091 }
1092
1094
1096}
1097
1106
1108{
1110 // Add accumulated rounding error before rounding again
1113 Q_ASSERT(qAbs(accumulatedError->x()) < 1.0);
1114 Q_ASSERT(qAbs(accumulatedError->y()) < 1.0);
1115
1116 // for continuous scroll events things should be
1117 // in the same direction
1118 // i.e converted so downwards surface co-ordinates (positive axis_value)
1119 // goes to downwards in wheel event (negative value)
1120 pixelDelta *= -1;
1121 return pixelDelta;
1122}
1123
1125{
1126 if (delta120.isNull()) {
1127 // If we didn't get any discrete events, then we need to fall back to
1128 // the continuous information.
1129 return (delta * -12).toPoint(); //TODO: why multiply by 12?
1130 }
1131
1132 // The angle delta is in eights of degrees, and our docs says most mice have
1133 // 1 click = 15 degrees, i.e. 120 is one click. It's also in the opposite
1134 // direction of surface space.
1135 return -delta120;
1136}
1137
1139{
1140 switch (axisSource) {
1141 case axis_source_wheel_tilt: // sideways tilt of the wheel
1142 case axis_source_wheel:
1144 case axis_source_finger:
1146 default: // Whatever other sources might be added are probably not mouse wheels
1148 }
1149}
1150
1152{
1154
1155 // The wayland protocol has separate horizontal and vertical axes, Qt has just the one inverted flag
1156 // Pragmatically it should't come up
1158
1159 // Angle delta is required for Qt wheel events, so don't try to send events if it's zero
1160 if (!angleDelta.isNull()) {
1162 if (!mScrollBeginSent) {
1163 if (!target && QWaylandWindow::mouseGrab())
1165 if (!target)
1166 target = focusSurface();
1167 }
1168 if (!target) {
1169 qCDebug(lcQpaWaylandInput) << "Flushing scroll event aborted - no scroll target";
1171 return;
1172 }
1173
1175 qCDebug(lcQpaWaylandInput) << "Flushing scroll event sending ScrollBegin";
1176 if (auto *ww = target->waylandWindow())
1180 mParent->modifiers(), false));
1181 mScrollBeginSent = true;
1184 }
1185
1188
1189 qCDebug(lcQpaWaylandInput) << "Flushing scroll event" << phase << pixelDelta << angleDelta;
1190 if (auto *ww = target->waylandWindow())
1193 }
1194
1195 if (mScrollEnd) {
1196 if (mScrollBeginSent) {
1197 if (auto target = mScrollTarget.get()) {
1198 qCDebug(lcQpaWaylandInput) << "Flushing scroll end event";
1199 if (auto *ww = target->waylandWindow())
1202 }
1203 mScrollBeginSent = false;
1205 } else {
1206 // May receive axis_stop for events we haven't sent a ScrollBegin for because
1207 // most axis_sources do not mandate an axis_stop event to be sent.
1208
1209 // TODO: For now, we just ignore these events, but we could perhaps take this as an
1210 // indication that this compositor will in fact send axis_stop events for these sources
1211 // and send a ScrollBegin the next time an axis_source event with this type is encountered.
1212 }
1213 mScrollEnd = false;
1215 }
1216
1218}
1219
1221{
1223
1224 if (auto *event = mFrameData.event.get()) {
1225 if (auto surface = event->surface) {
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 (!window)
1332 return;
1333
1334 if (window->waylandSurface() != mFocus) {
1335 qCWarning(lcQpaWayland) << "Ignoring unexpected wl_keyboard.leave event."
1336 << "wl_surface argument does not match the current focus"
1337 << "This is most likely a compositor bug";
1338 return;
1339 }
1342}
1343
1347 const QString &text, bool autorepeat, ushort count)
1348{
1350 bool filtered = false;
1351
1352 if (inputContext) {
1357 }
1358
1359 if (!filtered) {
1360 QWindow *window = nullptr;
1363 } else if (auto *surface = focusSurface()) {
1364 if (auto *ww = surface->waylandWindow())
1365 window = ww->window();
1366 }
1367 if (!window)
1368 return;
1369
1370 if (type == QEvent::KeyPress && key == Qt::Key_Menu) {
1371 auto cursor = window->screen()->handle()->cursor();
1372 if (cursor) {
1373 const QPoint globalPos = cursor->pos();
1376 }
1377 }
1378
1381 }
1382}
1383
1385{
1387 qCWarning(lcQpaWayland) << Q_FUNC_INFO << "unknown keymap format:" << mKeymapFormat;
1388 return;
1389 }
1390
1391 auto *surface = focusSurface();
1392 if (!surface) {
1393 // We destroyed the keyboard focus surface, but the server didn't get the message yet...
1394 // or the server didn't send an enter event first. In either case, ignore the event.
1395 return;
1396 }
1397
1399
1401 if (isDown)
1403
1405#if QT_CONFIG(xkbcommon)
1406 if ((!mXkbKeymap || !mXkbState) && !createDefaultKeymap())
1407 return;
1408
1409 auto code = key + 8; // map to wl_keyboard::keymap_format::keymap_format_xkb_v1
1410
1413
1416
1419 if (isAutoRepeat && mRepeatRate > 0) {
1420 qCWarning(lcQpaWayland, "received key repeat event while repeat rate is non-zero");
1421 return;
1422 }
1424 qCWarning(lcQpaWayland, "received key repeat event for a key that should not be repeated");
1425 return;
1426 }
1427 if (isAutoRepeat)
1429
1431
1440 } else if (mRepeatKey.code == code) {
1442 }
1443#else
1444 Q_UNUSED(time);
1445 Q_UNUSED(key);
1446 qCWarning(lcQpaWayland, "xkbcommon not available on this build, not performing key mapping");
1447 return;
1448#endif
1450 // raw scan code
1451 return;
1452 }
1453}
1454
1456{
1458}
1459
1461{
1462 mFocus = nullptr;
1465}
1466
1487
1493
1495 uint32_t time,
1496 struct wl_surface *surface,
1497 int32_t id,
1498 wl_fixed_t x,
1499 wl_fixed_t y)
1500{
1501 if (!surface)
1502 return;
1503
1505 if (!window)
1506 return; // Ignore foreign surfaces
1507
1508 mParent->mTime = time;
1510 mFocus = window;
1514}
1515
1517{
1519 mParent->mTime = time;
1521
1522 if (allTouchPointsReleased()) {
1523 mFocus = nullptr;
1524
1525 // As of Weston 7.0.0 there is no touch_frame after the last touch_up
1526 // (i.e. when the last finger is released). To accommodate for this, issue a
1527 // touch_frame. This cannot hurt since it is safe to call the touch_frame
1528 // handler multiple times when there are no points left.
1529 // See: https://gitlab.freedesktop.org/wayland/weston/issues/44
1530 // TODO: change logging category to lcQpaWaylandInput in newer versions.
1531 qCDebug(lcQpaWayland, "Generating fake frame event to work around Weston bug");
1532 touch_frame();
1533 }
1534}
1535
1542
1550
1552{
1554 auto it = std::find_if(mTouch->mPendingTouchPoints.begin(), end, [id](const QWindowSystemInterface::TouchPoint &tp){ return tp.id == id; });
1555 if (it == end) {
1557 it->id = id;
1558 }
1559 // If the touch points were up and down in same frame, send out frame right away
1560 else if ((it->state == QEventPoint::Pressed && state == QEventPoint::Released)
1564 it->id = id;
1565 }
1566
1568
1569 // Only moved and pressed needs to update/set position
1571 // We need a global (screen) position.
1573
1574 //is it possible that mTouchFocus is null;
1575 if (!surface && mPointer)
1577 if (!surface && mKeyboard)
1579 if (!surface)
1580 return;
1581
1582 auto *ww = surface->waylandWindow();
1583 if (!ww || !ww->window())
1584 return;
1585
1586 tp.area = QRectF(0, 0, 8, 8);
1590 }
1591
1592 // If the touch point was pressed earlier this frame, we don't want to overwrite its state.
1593 if (tp.state != QEventPoint::Pressed)
1595
1596 tp.pressure = tp.state == QEventPoint::Released ? 0 : 1;
1597}
1598
1600{
1601 for (const auto &tp : std::as_const(mPendingTouchPoints)) {
1602 if (tp.state != QEventPoint::Released)
1603 return false;
1604 }
1605 return true;
1606}
1607
1618
1620{
1621 // TODO: early return if no events?
1622
1623 QWindow *window = nullptr;
1624 if (mFocus) {
1625 if (auto *ww = mFocus->waylandWindow())
1626 window = ww->window();
1627 }
1628
1629 if (mFocus) {
1630 // Returns a reference to the last item in the list. The list must not be empty.
1631 // If the list can be empty, call isEmpty() before calling this function.
1632 // See: https://doc.qt.io/qt-5.15/qlist.html#last
1634 return;
1636 // When the touch event is received, the global pos is calculated with the margins
1637 // in mind. Now we need to adjust again to get the correct local pos back.
1638 auto *ww = mFocus->waylandWindow();
1639 if (!ww)
1640 return;
1642 QPoint p = tp.area.center().toPoint();
1645 return;
1646 }
1647
1649
1650 // Prepare state for next frame
1653 for (const auto &prevPoint: prevTouchPoints) {
1654 // All non-released touch points should be part of the next touch event
1657 tp.state = QEventPoint::Stationary; // ... as stationary (unless proven otherwise)
1659 }
1660 }
1661
1662}
1663
1664}
1665
1666QT_END_NAMESPACE
1667
1668#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)