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
qohosinputmethodeventhandler.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6#include "qohosjsmain.h"
9#include <QtCore/private/qohoslogger_p.h>
10#include <QtCore/qmap.h>
11#include <QtGui/private/qguiapplication_p.h>
12#include <QtGui/private/qhighdpiscaling_p.h>
13#include <QtGui/private/qwindow_p.h>
14#include <QtCore/qmath.h>
15#include <algorithm>
16#include <arkui/ui_input_event.h>
17#include <chrono>
18#include <render/qohosview.h>
19#include <typeinfo>
20
21using namespace Qt::Literals::StringLiterals;
22
23namespace ch = std::chrono;
24
25QT_BEGIN_NAMESPACE
26
27namespace {
28
29constexpr double fingerAreaWidth = 50.0;
30constexpr double fingerAreaHeight = 50.0;
31
32QInputDevice *registerPointingDevice(std::unique_ptr<QPointingDevice> device)
33{
34 auto *deviceRaw = device.get();
35 QWindowSystemInterface::registerInputDevice(device.release());
36 return deviceRaw;
37}
38
39QInputDevice *createPointingDevice(QInputDevice::DeviceType deviceType)
40{
41 qOhosDebug(QtForOhos) << "Creating pointing device! type:" << deviceType;
42
43 // QInputDevice::systemId() is expected to be unique per device; reusing
44 // the DeviceType flag value keeps that true across the devices created
45 // here without needing a separate ID scheme.
46 const qint64 systemId = static_cast<qint64>(deviceType);
47
48 if (deviceType == QInputDevice::DeviceType::Mouse) {
49 return registerPointingDevice(std::make_unique<QPointingDevice>(
50 "OHOS mouse device"_L1, systemId, deviceType, QPointingDevice::PointerType::Generic,
51 QInputDevice::Capability::Position, 1, 3));
52 }
53
54 return registerPointingDevice(std::make_unique<QPointingDevice>(
55 "OHOS touch device"_L1, systemId, deviceType, QPointingDevice::PointerType::Finger,
56 QInputDevice::Capability::Position
57 | QInputDevice::Capability::Area
58 | QInputDevice::Capability::Pressure
59 | QInputDevice::Capability::NormalizedPosition,
60 10, 0));
61}
62
64 QObject *object, QObject *context, std::function<void()> signalHandler)
65{
66 auto objDestroyedConnection = QObject::connect(object, &QObject::destroyed, context, std::move(signalHandler));
67 return QtOhos::makeDestroyNotifier(
68 [objDestroyedConnection = std::move(objDestroyedConnection)] () mutable {
69 QObject::disconnect(objDestroyedConnection);
70 });
71}
72
73std::optional<QEventPoint::State> tryMapXComponentTouchEventTypeToQt(::OH_NativeXComponent_TouchEventType eventType)
74{
75 switch (eventType) {
76 case OH_NATIVEXCOMPONENT_DOWN:
77 return QEventPoint::State::Pressed;
78 case OH_NATIVEXCOMPONENT_UP:
79 return QEventPoint::State::Released;
80 case OH_NATIVEXCOMPONENT_MOVE:
81 return QEventPoint::State::Updated;
82 case OH_NATIVEXCOMPONENT_CANCEL:
83 case OH_NATIVEXCOMPONENT_UNKNOWN:
84 break;
85 }
86 return {};
87}
88
89QPointF calculateTouchPointNormalPosition(QWindow *targetWindow, const QPointF &clickPoint)
90{
91 auto *platformScreen = static_cast<QOhosPlatformScreen *>(targetWindow->screen()->handle());
92
93 QSize screenSize = platformScreen->geometry().size();
94
95 QPointF clickPointNormalized(
96 clickPoint.x() / screenSize.width(),
97 clickPoint.y() / screenSize.height());
98
99 return clickPointNormalized;
100}
101
102QRectF calculateTouchPointArea(const QPointF &clickPoint)
103{
104 return QRectF(
105 clickPoint.x() - static_cast<double>(fingerAreaWidth/2),
106 clickPoint.y() - static_cast<double>(fingerAreaHeight/2),
109}
110
112{
113 auto *screen = qWindow != nullptr
114 ? qWindow->screen()
115 : QGuiApplication::primaryScreen();
116
117 return screen != nullptr
118 ? screen->handle()->geometry().topLeft()
119 : QPoint();
120}
121
122QPoint makeWindowLocalPosition(const QPoint &globalPosition, QWindow *qWindow)
123{
124 auto *platformWindow = QOhosPlatformWindow::fromQWindowOrNull(qWindow);
125 auto platformWindowGeometry = platformWindow != nullptr
126 ? platformWindow->geometry()
127 : QHighDpi::toNativePixels(qWindow->geometry(), qWindow);
128
129 return globalPosition - platformWindowGeometry.topLeft();
130}
131
132// Whether a press on qWindow should make it the focus window. Based on the
133// check in QXcbWindow::handleButtonPressEvent(), with the window types and
134// states that OHOS must also skip.
135bool windowShouldTakeFocusOnPress(QWindow *qWindow)
136{
137 if (qWindow == nullptr || qWindow == QGuiApplication::focusWindow())
138 return false;
139
140 if (QGuiApplicationPrivate::activePopupWindow() != nullptr)
141 return false;
142
143 if (QOhosPlatformWindow::fromQWindowOrNull(qWindow) == nullptr)
144 return false;
145
146 const auto flags = QOhosPlatformWindow::platformWindowFlagsForQWindow(qWindow);
147 if (flags.testFlag(Qt::WindowDoesNotAcceptFocus)
148 || flags.testFlag(Qt::WindowTransparentForInput)
149 || flags.testFlag(Qt::BypassWindowManagerHint)) {
150 return false;
151 }
152
153 switch (qWindow->type()) {
154 case Qt::ToolTip:
155 case Qt::Popup:
156 case Qt::SplashScreen:
157 return false;
158 default:
159 break;
160 }
161
162 return !QGuiApplicationPrivate::instance()->isWindowBlocked(qWindow);
163}
164
165}
166
167QOhosInputMethodEventHandler::QOhosInputMethodEventHandler(
168 const std::set<QInputDevice::DeviceType> &deviceTypes)
169{
170 for (const auto &deviceType : deviceTypes)
171 m_pointingDevices.emplace(deviceType, createPointingDevice(deviceType));
172
173 // Activation is asynchronous, so a request is latched until focus changes.
174 connect(qGuiApp, &QGuiApplication::focusWindowChanged,
175 this, [this](QWindow *) { m_pendingActivationWindow.clear(); });
176}
177
178void QOhosInputMethodEventHandler::requestActivationOnPressIfNeeded(QWindow *pressTargetWindow)
179{
180 if (!m_currentMouseGrabbingWindow.isNull())
181 return;
182
183 if (pressTargetWindow == nullptr)
184 return;
185
186 // Activate the event receiver, not the hit window: a native child
187 // QWidgetWindow must not become the focus window on its own.
188 auto *targetWindow =
189 static_cast<QWindowPrivate *>(QObjectPrivate::get(pressTargetWindow))->eventReceiver();
190
191 if (!windowShouldTakeFocusOnPress(targetWindow))
192 return;
193
194 auto *currentFocusWindow = QGuiApplication::focusWindow();
195 const bool embeddedWindowInvolved =
196 QOhosPlatformWindow::isEmbeddedWindow(targetWindow)
197 || (currentFocusWindow != nullptr
198 && QOhosPlatformWindow::isEmbeddedWindow(currentFocusWindow));
199 if (!embeddedWindowInvolved)
200 return;
201
202 if (m_pendingActivationWindow == targetWindow)
203 return;
204
205 m_pendingActivationWindow = targetWindow;
206 targetWindow->requestActivate();
207}
208
210
212 QWindow *targetWindow, ch::nanoseconds timeStamp,
213 const std::vector<QOhosTouchEventTouchPointData> &touchPoints,
214 QInputDevice::DeviceType deviceType, QFlags<OhosKeyboardModifier> modifiers)
215{
216 auto *touchDevice = getPointingDeviceOrCreate(deviceType);
217
218 QList<QWindowSystemInterface::TouchPoint> wsiTouchPoints;
219
220 auto timeStampMs = ch::duration_cast<ch::milliseconds>(timeStamp);
221
222 std::vector<QPoint> activeTouchPointDisplayPositions;
223
224 auto displayOffset = targetWindow != nullptr
225 ? determineScreenGlobalDisplayOffset(targetWindow)
226 : QPoint(0, 0);
227
228 for (const auto &touchPointData : touchPoints) {
229 const auto &touchPoint = touchPointData.touchPoint;
230 QPointF clickPoint = touchPointData.displayPosition;
231
232 switch (touchPointData.toolType) {
233 case ::OH_NATIVEXCOMPONENT_TOOL_TYPE_FINGER: {
234 QEventPoint::State state =
235 tryMapXComponentTouchEventTypeToQt(touchPoint.type)
236 .value_or(QEventPoint::State::Stationary);
237
238 if (state != QEventPoint::State::Released)
239 activeTouchPointDisplayPositions.push_back(touchPointData.displayPosition.toPoint());
240
241 QWindowSystemInterface::TouchPoint qwsiTouchPoint;
242 qwsiTouchPoint.id = touchPoint.id;
243 qwsiTouchPoint.pressure = touchPoint.force;
244 qwsiTouchPoint.normalPosition =
245 calculateTouchPointNormalPosition(targetWindow, clickPoint);
246 qwsiTouchPoint.state = state;
247 qwsiTouchPoint.area = calculateTouchPointArea(clickPoint + displayOffset);
248 wsiTouchPoints.push_back(qwsiTouchPoint);
249 break;
250 }
251 case ::OH_NATIVEXCOMPONENT_TOOL_TYPE_PEN: {
252 Qt::MouseButtons buttons = Qt::NoButton;
253 switch (touchPoint.type) {
254 case OH_NATIVEXCOMPONENT_DOWN:
255 requestActivationOnPressIfNeeded(targetWindow);
256 buttons = Qt::LeftButton;
257 break;
258 case OH_NATIVEXCOMPONENT_MOVE:
259 buttons = Qt::LeftButton;
260 break;
261 case OH_NATIVEXCOMPONENT_UP:
262 case OH_NATIVEXCOMPONENT_CANCEL:
263 case OH_NATIVEXCOMPONENT_UNKNOWN:
264 buttons = Qt::NoButton;
265 break;
266 }
267 constexpr float tiltDegreesMin = -60.0f;
268 constexpr float tiltDegreesMax = 60.0f;
269 const int xTilt = qRound(qBound(tiltDegreesMin, touchPointData.tiltX, tiltDegreesMax));
270 const int yTilt = qRound(qBound(tiltDegreesMin, touchPointData.tiltY, tiltDegreesMax));
271 constexpr qreal tangentialPressure = 0;
272 constexpr qreal rotation = 0;
273 constexpr int z = 0;
274 QWindowSystemInterface::handleTabletEvent(
275 targetWindow, timeStampMs.count(), {touchPoint.x, touchPoint.y},
276 clickPoint,
277 static_cast<int>(QInputDevice::DeviceType::Stylus),
278 static_cast<int>(QPointingDevice::PointerType::Pen),
279 buttons, touchPoint.force, xTilt, yTilt, tangentialPressure, rotation,
280 z, touchPoint.id, convertOhosToQtKeyboardModifiers(modifiers));
281 break;
282 }
283 case ::OH_NATIVEXCOMPONENT_TOOL_TYPE_RUBBER:
284 case ::OH_NATIVEXCOMPONENT_TOOL_TYPE_BRUSH:
285 case ::OH_NATIVEXCOMPONENT_TOOL_TYPE_PENCIL:
286 case ::OH_NATIVEXCOMPONENT_TOOL_TYPE_AIRBRUSH:
287 case ::OH_NATIVEXCOMPONENT_TOOL_TYPE_LENS:
288 case ::OH_NATIVEXCOMPONENT_TOOL_TYPE_UNKNOWN:
289 qOhosWarning(QtForOhos) << "Skipping unsupported tool type =" << touchPointData.toolType;
290 break;
291 case ::OH_NATIVEXCOMPONENT_TOOL_TYPE_MOUSE:
292 qOhosWarning(QtForOhos) << "Skipping mouse tool type in touch event.";
293 break;
294 }
295 }
296
297 if (!wsiTouchPoints.isEmpty()) {
298 auto singleActiveTouchEventGlobalPosition = activeTouchPointDisplayPositions.size() == 1
299 ? std::optional(displayOffset + activeTouchPointDisplayPositions.front())
300 : std::nullopt;
301
302 QWindowSystemInterfaceTouchEvent touchEvent = {
303 .targetWindow = targetWindow,
304 .touchPoints = wsiTouchPoints,
305 .touchDevice = touchDevice,
306 .timestampMs = timeStampMs,
307 .modifiers = modifiers,
308 .singleTouchPointEventGlobalPosition = singleActiveTouchEventGlobalPosition,
309 };
310
311 handleTouchEvent(touchEvent);
312 }
313}
314
316{
317 auto *pointingDevice = getPointingDeviceOrCreate(gestureEvent.deviceType);
318
319 // handleGestureEventWithRealValue() takes native positions and applies QHighDpi internally.
320 QWindowSystemInterface::handleGestureEventWithRealValue(
321 gestureEvent.targetWindow,
322 gestureEvent.timestamp,
323 static_cast<const QPointingDevice *>(pointingDevice),
324 gestureEvent.gestureType,
325 gestureEvent.value,
326 gestureEvent.localPosition,
327 gestureEvent.globalPosition);
328}
329
330void QOhosInputMethodEventHandler::onKeyEvent(const QOhosKeyEvent &keyEvent, QWindow *targetWindow)
331{
332 const auto optQOhosQtKeyEvent = keyEvent.tryConvertToQOhosQtKeyEvent();
333 if (!optQOhosQtKeyEvent.has_value())
334 return;
335 const auto qOhosQtKeyEvent = optQOhosQtKeyEvent.value();
336
337 constexpr quint32 nativeScanCode = 0;
338 constexpr quint32 nativeModifiers = 0;
339
340 auto *ohosInputContext = qobject_cast<QOhosInputContext *>(QOhosPlatformIntegration::instance()->inputContext());
341 if (ohosInputContext != nullptr)
342 ohosInputContext->setLastInputTypeToTriggerSoftKeyboard(QOhosInputContext::RequestKeyboardReason::NONE);
343
344 if (qOhosQtKeyEvent.keyAction == QEvent::KeyPress) {
345 if (m_autoRepeatCountMap[qOhosQtKeyEvent.keyCode] < std::numeric_limits<ushort>::max()) {
346 ++m_autoRepeatCountMap[qOhosQtKeyEvent.keyCode];
347 }
348 } else {
349 m_autoRepeatCountMap.remove(qOhosQtKeyEvent.keyCode);
350 }
351 const auto count = m_autoRepeatCountMap.value(qOhosQtKeyEvent.keyCode, 1);
352
353 QWindowSystemInterface::handleExtendedKeyEvent(
354 !m_currentKeyboardGrabbingWindow.isNull()
355 ? m_currentKeyboardGrabbingWindow.data()
356 : targetWindow,
357 qOhosQtKeyEvent.keyAction, qOhosQtKeyEvent.keyCode,
358 qOhosQtKeyEvent.guiApplicationKeyboardModifiers, nativeScanCode,
359 qOhosQtKeyEvent.nativeKeyCode, nativeModifiers, qOhosQtKeyEvent.keyText, count > 1, count);
360}
361
363{
364 Qt::MouseButton button = Qt::NoButton;
365
366 if (mouseEvent.eventType == QEvent::MouseButtonPress || mouseEvent.eventType == QEvent::MouseButtonRelease) {
367 button = mouseEvent.button;
368
369 // HACK
370 // Destructing QOhosView means loosing QNativeNode, JsStateData and QXComponentCallbackReceiver.
371 // It means no more events will come from the destroyed window.
372 // There is a case when closing window was done via mouse double-click event and second release
373 // button event is not caught due to QNativeNode destruction. It causes issues in
374 // QOhosInputMethodEventHandler state machine after switching to different window.
375 // This workaround allows to clear buttons state.
376 registerOnWindowCloseToResetMouseButtonsState(mouseEvent.targetWindow);
377 }
378
379 QOhosMouseEvent wsiEvent {
380 .targetWindow = mouseEvent.targetWindow,
381 .timestampMs = mouseEvent.timestampMs,
382 .localPosition = mouseEvent.localPosition,
383 .globalPosition = mouseEvent.globalPosition,
384 .button = button,
385 .eventType = mouseEvent.eventType,
386 .modifiers = mouseEvent.modifiers,
387 .deviceType = mouseEvent.deviceType,
388 };
389
390 handleMouseEvent(wsiEvent);
391}
392
394{
395 bool isHover = hoverEvent.isHover;
396 auto local = hoverEvent.localPosition;
397 auto global = hoverEvent.globalPosition;
398 QWindow *window = hoverEvent.targetWindow;
399 if (!m_currentMouseGrabbingWindow.isNull() && m_currentMouseGrabbingWindow != window)
400 return;
401
402 if (isHover)
403 QWindowSystemInterface::handleEnterEvent(window, local, global);
404 else
405 QWindowSystemInterface::handleLeaveEvent(window);
406}
407
409{
410 constexpr int angleXMin = -120;
411 constexpr int angleXMax = 120;
412 constexpr int xAxisValueMultiplier = -10;
413
414 constexpr int angleYMin = angleXMin;
415 constexpr int angleYMax = angleXMax;
416 constexpr int yAxisValueMultiplier = xAxisValueMultiplier;
417
418 constexpr double wheelStepDegree = 15.0;
419 constexpr double wheelStepPixel = 21.0;
420
421 constexpr double angleBaseValue = 8.0;
422 constexpr double directionMultiplier = -1.0;
423
424 QPoint pixelDelta;
425 QPoint angleDelta;
426
427 static_cast<QOhosPlatformTheme *>(QGuiApplicationPrivate::platformTheme())->setWheelScrollLines(
428 static_cast<int>(event.wheelScrollLines));
429
430 auto *platformScreen = static_cast<QOhosPlatformScreen *>(window->screen()->handle());
431
432 if (event.eventToolType == UI_INPUT_EVENT_TOOL_TYPE_MOUSE) {
433 angleDelta.setX(qBound(angleXMin, static_cast<int>(event.horizontalValue * xAxisValueMultiplier), angleXMax));
434 angleDelta.setY(qBound(angleYMin, static_cast<int>(event.verticalValue * yAxisValueMultiplier), angleYMax));
435
436 auto mousePixelDeltaMultiplier = wheelStepPixel / wheelStepDegree / angleBaseValue;
437 pixelDelta.setX(
438 qRound(angleDelta.x() * mousePixelDeltaMultiplier * platformScreen->pixelScalingCoefficient()));
439 pixelDelta.setY(
440 qRound(angleDelta.y() * mousePixelDeltaMultiplier * platformScreen->pixelScalingCoefficient()));
441 } else if (event.eventToolType == UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD) {
442 auto touchpadAngleDeltaMultiplier =
443 wheelStepDegree / wheelStepPixel / platformScreen->pixelScalingCoefficient() * angleBaseValue * directionMultiplier / event.wheelScrollLines;
444 angleDelta.setX(qRound(event.horizontalValue * touchpadAngleDeltaMultiplier));
445 angleDelta.setY(qRound(event.verticalValue * touchpadAngleDeltaMultiplier));
446
447 pixelDelta.setX(qRound(event.horizontalValue * directionMultiplier));
448 pixelDelta.setY(qRound(event.verticalValue * directionMultiplier));
449 } else {
450 qOhosWarning(QtForOhos)
451 << Q_FUNC_INFO
452 << "Received unsupported input event tool type =" << event.eventToolType << "skipping...";
453 return;
454 }
455
456 Qt::MouseEventSource source = event.eventToolType == ::UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD
457 ? Qt::MouseEventSynthesizedBySystem
458 : Qt::MouseEventNotSynthesized;
459 bool inverted = false;
460
461 const QInputDevice::DeviceType wheelDeviceType = event.eventToolType == UI_INPUT_EVENT_TOOL_TYPE_MOUSE
462 ? QInputDevice::DeviceType::Mouse
463 : QInputDevice::DeviceType::TouchPad;
464 const auto *wheelDevice =
465 static_cast<const QPointingDevice *>(getPointingDeviceOrCreate(wheelDeviceType));
466
467 QWindowSystemInterface::handleWheelEvent(
468 window,
469 event.timestamp,
470 wheelDevice,
471 event.localPoint,
472 event.globalPoint,
473 pixelDelta,
474 angleDelta,
475 convertOhosToQtKeyboardModifiers(event.modifiers),
476 event.scrollPhase,
477 source,
478 inverted);
479}
480
482 QWindow *targetWindow, std::vector<QOhosWindowProxy::NonClientAreaMouseEvent> eventBatch)
483{
484 using NonClientAreaMouseEvent = QOhosWindowProxy::NonClientAreaMouseEvent;
485
486 eventBatch.erase(
487 QtOhos::removeMatchingWithLookahead(
488 eventBatch.begin(), eventBatch.end(),
489 [](const NonClientAreaMouseEvent &event, const NonClientAreaMouseEvent &nextEvent) {
490 return event.action == QEvent::NonClientAreaMouseMove
491 && nextEvent.action == QEvent::NonClientAreaMouseMove;
492 }),
493 eventBatch.end());
494
495 for (const auto &mouseEvent : eventBatch) {
496 // Input_MouseEvent (unlike ArkUI_UIInputEvent) exposes no source type, so
497 // we cannot tell a real mouse from a touchpad here. TouchPad is the safe
498 // guess: if getPointingDeviceOrCreate() has to lazily register a device
499 // for it, that doesn't change what QPointingDevice::primaryPointingDevice()
500 // resolves to elsewhere, since TouchPad is already its fallback in the
501 // absence of a real Mouse device. Guessing Mouse instead could register a
502 // phantom Mouse device and make primaryPointingDevice() prefer it process-wide.
503 QOhosMouseEvent qtMouseEvent = {
504 .targetWindow = targetWindow,
505 .timestampMs = mouseEvent.timestamp,
506 .localPosition = mouseEvent.localPosition,
507 .globalPosition = mouseEvent.displayPosition,
508 .button = mouseEvent.button,
509 .eventType = mouseEvent.action,
510 .modifiers = {},
511 .deviceType = QInputDevice::DeviceType::TouchPad,
512 };
513
514 // HACK
515 // When a window is being closed by clicking close button on title bar we should receive two events:
516 // NonClientAreaMouseButtonPress and NonClientAreaMouseButtonRelease, but sometimes we receive only
517 // NonClientAreaMouseButtonPress event without NonClientAreaMouseButtonRelease, because the window
518 // sending this event is alredy destroyed. It leaves us with invalid mouse state with a button
519 // that has not been released.
520 // This workaround resets mouse buttons state when a window is closed.
521 if (mouseEvent.action == QEvent::NonClientAreaMouseButtonPress)
522 registerOnWindowCloseToResetMouseButtonsState(targetWindow);
523
524 handleMouseEvent(qtMouseEvent);
525 }
526}
527
529 QWindow *targetWindow, std::vector<QOhosWindowProxy::NonClientAreaTouchEvent> eventBatch)
530{
531 using NonClientAreaTouchEvent = QOhosWindowProxy::NonClientAreaTouchEvent;
532
533 eventBatch.erase(
534 QtOhos::removeMatchingWithLookahead(
535 eventBatch.begin(), eventBatch.end(),
536 [](const NonClientAreaTouchEvent &event, const NonClientAreaTouchEvent &nextEvent) {
537 return event.action == QEvent::NonClientAreaMouseMove
538 && nextEvent.action == QEvent::NonClientAreaMouseMove;
539 }),
540 eventBatch.end());
541
542 for (const auto &touchEvent : eventBatch) {
543 if (touchEvent.action == QEvent::NonClientAreaMouseButtonPress)
544 m_optNonClientAreaTouchPointerId = touchEvent.id;
545 else if (m_optNonClientAreaTouchPointerId != touchEvent.id)
546 continue;
547
548 if (touchEvent.action == QEvent::NonClientAreaMouseButtonRelease)
549 m_optNonClientAreaTouchPointerId.reset();
550
551 QOhosMouseEvent qtMouseEvent = {
552 .targetWindow = targetWindow,
553 .timestampMs = touchEvent.timestamp,
554 .localPosition = touchEvent.localPosition,
555 .globalPosition = touchEvent.displayPosition,
556 .button = Qt::LeftButton,
557 .eventType = touchEvent.action,
558 .modifiers = {},
559 .deviceType = QInputDevice::DeviceType::TouchScreen,
560 };
561
562 if (touchEvent.action == QEvent::NonClientAreaMouseButtonPress)
563 registerOnWindowCloseToResetMouseButtonsState(targetWindow);
564
565 handleMouseEvent(qtMouseEvent);
566 }
567}
568
570{
571 auto lastTouchedPair = getLastTouchedWindowWithSeqNoIfPresent();
572 return lastTouchedPair.has_value()
573 ? lastTouchedPair.value().first
574 : nullptr;
575}
576
577QInputDevice *QOhosInputMethodEventHandler::getPointingDeviceOrCreate(QInputDevice::DeviceType deviceType)
578{
579 auto pointingDeviceIter = m_pointingDevices.find(deviceType);
580 if (pointingDeviceIter == m_pointingDevices.end()) {
581 qOhosWarning(QtForOhos) << "Trying to get pointing device but it isn't registered. Creating and registering one now.";
582 std::tie(pointingDeviceIter, std::ignore) = m_pointingDevices.emplace(
583 deviceType, createPointingDevice(deviceType));
584 }
585 return pointingDeviceIter->second;
586}
587
588std::optional<std::pair<QWindow *, std::uint64_t>> QOhosInputMethodEventHandler::getLastTouchedWindowWithSeqNoIfPresent() const
589{
590 auto maxSeqNoEntryIter = std::max_element(
591 m_windowsUnderTouchPoints.begin(), m_windowsUnderTouchPoints.end(),
592 [](const auto &a, const auto &b) {
593 return a.second.second < b.second.second;
594 });
595
596 return maxSeqNoEntryIter != m_windowsUnderTouchPoints.end()
597 ? std::optional(
598 std::make_pair(maxSeqNoEntryIter->first, maxSeqNoEntryIter->second.second))
599 : std::nullopt;
600}
601
603{
604 qCDebug(QtForOhos) << Q_FUNC_INFO << "window:" << window;
605 m_currentMouseGrabbingWindow = window;
606}
607
609{
610 qCDebug(QtForOhos) << Q_FUNC_INFO << "window:" << window;
611 m_currentKeyboardGrabbingWindow = window;
612}
613
615{
616 if (!m_currentMouseGrabbingWindow.isNull() && m_lastWsiMouseEvent.has_value()) {
617 auto lastWsiMouseEventValue = m_lastWsiMouseEvent.value();
618 auto *previousCaptureWindow = m_currentMouseGrabbingWindow.data();
619 auto *optLastWindowUnderCursor = lastWsiMouseEventValue.targetWindow.data();
620 auto *optCurrentWindowUnderCursor = qGuiApp->topLevelAt(
621 QHighDpi::fromNativePixels(
622 lastWsiMouseEventValue.globalPosition.toPoint(),
623 lastWsiMouseEventValue.targetWindow.data()));
624
625 if (optLastWindowUnderCursor != nullptr
626 && optCurrentWindowUnderCursor != nullptr
627 && optLastWindowUnderCursor != previousCaptureWindow) {
628 QWindowSystemInterface::handleEnterEvent(
629 optLastWindowUnderCursor, lastWsiMouseEventValue.localPosition,
630 lastWsiMouseEventValue.globalPosition);
631 }
632 }
633 m_currentMouseGrabbingWindow.clear();
634}
635
637{
638 if (m_lastWsiMouseEvent.has_value())
639 return m_lastWsiMouseEvent.value().globalPosition.toPoint();
640
641 auto optLastTouchPosition = qAndThen(
642 m_lastWsiTouchEvent,
643 [](const QWindowSystemInterfaceTouchEvent &touchEvent) {
644 return touchEvent.singleTouchPointEventGlobalPosition;
645 });
646 if (optLastTouchPosition.has_value())
647 return optLastTouchPosition.value();
648
649 auto lastScaledPositionFromApp = QGuiApplicationPrivate::lastCursorPosition.toPoint();
650 auto *screen = qGuiApp->screenAt(lastScaledPositionFromApp);
651 return QHighDpi::toNativePixels(
652 lastScaledPositionFromApp,
653 screen != nullptr
654 ? screen
655 : QGuiApplication::primaryScreen());
656}
657
659{
660 m_currentKeyboardGrabbingWindow.clear();
661}
662
663void QOhosInputMethodEventHandler::handleMouseEvent(const QOhosMouseEvent &wsiEvent)
664{
665 static const QSet<QEvent::Type> mouseButtonPressEventTypes = {
666 QEvent::MouseButtonPress,
667 QEvent::NonClientAreaMouseButtonPress,
668 };
669 static const QSet<QEvent::Type> mouseButtonReleaseEventTypes = {
670 QEvent::MouseButtonRelease,
671 QEvent::NonClientAreaMouseButtonRelease,
672 };
673
674 bool eventTypeIsPress = mouseButtonPressEventTypes.contains(wsiEvent.eventType);
675 bool eventTypeIsRelease = mouseButtonReleaseEventTypes.contains(wsiEvent.eventType);
676
677 if (eventTypeIsPress || eventTypeIsRelease) {
678 m_mouseButtonsState.setFlag(wsiEvent.button, eventTypeIsPress);
679 auto *ohosInputContext = qobject_cast<QOhosInputContext *>(QOhosPlatformIntegration::instance()->inputContext());
680 if (ohosInputContext != nullptr)
681 ohosInputContext->setLastInputTypeToTriggerSoftKeyboard(QOhosInputContext::RequestKeyboardReason::MOUSE);
682 }
683
684 if (eventTypeIsPress)
685 requestActivationOnPressIfNeeded(wsiEvent.targetWindow.data());
686
687 QEvent::Type targetEventType;
688 QWindow *targetWindow;
689 QPointF localPosition;
690 if (!m_currentMouseGrabbingWindow.isNull()) {
691 targetWindow = m_currentMouseGrabbingWindow;
692 localPosition = makeWindowLocalPosition(wsiEvent.globalPosition.toPoint(), targetWindow);
693 switch (wsiEvent.eventType) {
694 case QEvent::NonClientAreaMouseButtonRelease:
695 targetEventType = QEvent::MouseButtonRelease;
696 break;
697 case QEvent::NonClientAreaMouseButtonPress:
698 targetEventType = QEvent::MouseButtonPress;
699 break;
700 case QEvent::NonClientAreaMouseMove:
701 targetEventType = QEvent::MouseMove;
702 break;
703 default:
704 targetEventType = wsiEvent.eventType;
705 break;
706 }
707 } else {
708 targetWindow = wsiEvent.targetWindow;
709 localPosition = wsiEvent.localPosition;
710 targetEventType = wsiEvent.eventType;
711 }
712
713 m_lastWsiMouseEvent = wsiEvent;
714
715 if (targetEventType == QEvent::None) {
716 qOhosPrintfDebug("%s: targetEventType is QEvent::None!", Q_FUNC_INFO);
717 return;
718 }
719
720 const auto *mouseDevice =
721 static_cast<const QPointingDevice *>(getPointingDeviceOrCreate(wsiEvent.deviceType));
722
723 QWindowSystemInterface::handleMouseEvent(
724 targetWindow,
725 wsiEvent.timestampMs.count(),
726 mouseDevice,
727 localPosition,
728 wsiEvent.globalPosition,
729 m_mouseButtonsState,
730 wsiEvent.button,
731 targetEventType,
732 convertOhosToQtKeyboardModifiers(wsiEvent.modifiers));
733}
734
735void QOhosInputMethodEventHandler::handleTouchEvent(const QWindowSystemInterfaceTouchEvent &touchEvent)
736{
737 if (touchEvent.touchPoints.isEmpty()) {
738 qOhosCritical(QtForOhos) << "TouchPoints list is empty, nothing to do";
739 return;
740 }
741
742 m_lastWsiMouseEvent.reset();
743
744 bool anyTouchPointPressed = false;
745 bool anyTouchPointReleased = false;
746 for (const auto &touchPoint : touchEvent.touchPoints) {
747 anyTouchPointPressed = anyTouchPointPressed || touchPoint.state == QEventPoint::State::Pressed;
748 anyTouchPointReleased = anyTouchPointReleased || touchPoint.state == QEventPoint::State::Released;
749 }
750
751 if (anyTouchPointPressed || anyTouchPointReleased) {
752 auto *ohosInputContext = qobject_cast<QOhosInputContext *>(QOhosPlatformIntegration::instance()->inputContext());
753 if (ohosInputContext != nullptr)
754 ohosInputContext->setLastInputTypeToTriggerSoftKeyboard(QOhosInputContext::RequestKeyboardReason::TOUCH);
755 }
756
757 if (anyTouchPointPressed)
758 requestActivationOnPressIfNeeded(touchEvent.targetWindow);
759
760 updateWindowsUnderTouchPoints(touchEvent);
761
762 m_lastWsiTouchEvent = touchEvent;
763 QWindowSystemInterface::handleTouchEvent(
764 touchEvent.targetWindow, touchEvent.timestampMs.count(),
765 static_cast<const QPointingDevice *>(touchEvent.touchDevice), touchEvent.touchPoints,
766 convertOhosToQtKeyboardModifiers(touchEvent.modifiers));
767}
768
769void QOhosInputMethodEventHandler::updateWindowsUnderTouchPoints(const QWindowSystemInterfaceTouchEvent &touchEvent)
770{
771 const auto &touchPoints = touchEvent.touchPoints;
772 auto *targetWindow = touchEvent.targetWindow;
773
774 const bool allTouchPointsUp = std::all_of(
775 touchPoints.begin(), touchPoints.end(),
776 [](const auto &touchPointData) {
777 return touchPointData.state == QEventPoint::State::Released;
778 });
779
780 const bool anyTouchPointDown = std::any_of(
781 touchPoints.begin(), touchPoints.end(),
782 [](const auto &touchPointData) {
783 return touchPointData.state == QEventPoint::State::Pressed;
784 });
785
786 if (allTouchPointsUp) {
787 std::ignore = m_windowsUnderTouchPoints.erase(targetWindow);
788 } else if (anyTouchPointDown) {
789 auto lastTouchedPair = getLastTouchedWindowWithSeqNoIfPresent();
790 auto nextSeqNo = lastTouchedPair.has_value()
791 ? lastTouchedPair.value().second + 1
792 : 0;
793 m_windowsUnderTouchPoints[targetWindow] = std::make_pair(
794 registerObjectDestroyedSignalHandler(
795 targetWindow, this,
796 [this, targetWindow]() {
797 std::ignore = m_windowsUnderTouchPoints.erase(targetWindow);
798 }),
799 nextSeqNo);
800 }
801}
802
803void QOhosInputMethodEventHandler::registerOnWindowCloseToResetMouseButtonsState(QWindow *window)
804{
805 auto *eventView = QOhosPlatformWindow::fromQWindow(window)->ownedViewOrNull();
806 if (eventView != nullptr) {
807 m_lastMouseEventViewLifetimeTrackerHandle = registerObjectDestroyedSignalHandler(
808 eventView, this,
809 [this]() {
810 m_mouseButtonsState = Qt::NoButton;
811 });
812 }
813}
814
815QT_END_NAMESPACE
void onMouseWheelEvent(const QOhosWheelEvent &event, QWindow *window)
void onNonClientAreaTouchEvents(QWindow *targetWindow, std::vector< QOhosWindowProxy::NonClientAreaTouchEvent > eventBatch)
void onHoverEvent(const QOhosHoverEvent &hoverEvent)
void onMouseEvent(const QOhosMouseEvent &mouseEvent)
void onGestureEventFromNativeNode(const QOhosGestureEvent &gestureEvent)
void onTouchEventFromXComponent(QWindow *targetWindow, std::chrono::nanoseconds timeStamp, const std::vector< QOhosTouchEventTouchPointData > &touchPoints, QInputDevice::DeviceType deviceType, QFlags< OhosKeyboardModifier > modifiers)
void onNonClientAreaMouseEvents(QWindow *targetWindow, std::vector< QOhosWindowProxy::NonClientAreaMouseEvent > eventBatch)
void onKeyEvent(const QOhosKeyEvent &keyEvent, QWindow *targetWindow)
virtual std::optional< QOhosQtKeyEvent > tryConvertToQOhosQtKeyEvent() const =0
static QOhosPlatformIntegration * instance()
QtOhos::enums::ohos::window::WindowEventType WindowEventType
QRectF calculateTouchPointArea(const QPointF &clickPoint)
bool windowShouldTakeFocusOnPress(QWindow *qWindow)
QPoint determineScreenGlobalDisplayOffset(QWindow *qWindow)
QPoint makeWindowLocalPosition(const QPoint &globalPosition, QWindow *qWindow)
QInputDevice * registerPointingDevice(std::unique_ptr< QPointingDevice > device)
QPointF calculateTouchPointNormalPosition(QWindow *targetWindow, const QPointF &clickPoint)
std::shared_ptr< void > registerObjectDestroyedSignalHandler(QObject *object, QObject *context, std::function< void()> signalHandler)
QInputDevice * createPointingDevice(QInputDevice::DeviceType deviceType)
std::optional< QEventPoint::State > tryMapXComponentTouchEventTypeToQt(::OH_NativeXComponent_TouchEventType eventType)