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
qohosnativexcomponentinputhandler.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
4#include <ace/xcomponent/native_interface_xcomponent.h>
5#include <info/application_target_sdk_version.h>
6#include <multimodalinput/oh_input_manager.h>
7#include <render/qohosnativexcomponentinputhandler.h>
8
9#include <QtCore/QCoreApplication>
10#include <QtCore/qspan.h>
11#include <native_window/external_window.h>
12#include <qohosinputmethodeventhandler.h>
13#include <qohosjsmain.h>
14#include <qohosxcomponentkeyevent.h>
15#include <qohosplatformintegration.h>
16#include <qohosplatformtheme.h>
17#include <qohosutils.h>
18#include <render/qohosbatchingrequestshandler.h>
19#include <render/qohosview.h>
20#include <algorithm>
21#include <cstdint>
22#include <memory>
23#include <vector>
24
25QT_BEGIN_NAMESPACE
26
27namespace
28{
29
30constexpr auto mouseMotionEventMinAgeForDrop = std::chrono::milliseconds(20);
31
32constexpr auto touchEventMinAgeForDrop = std::chrono::milliseconds(20);
33
35 {OhosKeyboardModifier::CTRL, {::KEYCODE_CTRL_LEFT, ::KEYCODE_CTRL_RIGHT}, &::OH_Input_GetKeyPressed, ::KEY_PRESSED},
36 {OhosKeyboardModifier::ALT, {::KEYCODE_ALT_LEFT, ::KEYCODE_ALT_RIGHT}, &::OH_Input_GetKeyPressed, ::KEY_PRESSED},
37 {OhosKeyboardModifier::SHIFT, {::KEYCODE_SHIFT_LEFT, ::KEYCODE_SHIFT_RIGHT}, &::OH_Input_GetKeyPressed, ::KEY_PRESSED},
38 {OhosKeyboardModifier::LOGO, {::KEYCODE_META_LEFT, ::KEYCODE_META_RIGHT}, &::OH_Input_GetKeyPressed, ::KEY_PRESSED},
39 {OhosKeyboardModifier::CAPS_LOCK, {::KEYCODE_CAPS_LOCK}, &::OH_Input_GetKeySwitch, ::KEY_SWITCH_ON},
40 {OhosKeyboardModifier::NUM_LOCK, {::KEYCODE_NUM_LOCK}, &::OH_Input_GetKeySwitch, ::KEY_SWITCH_ON},
41}};
42
44{
45 // FIXME: make sure we use thread-safe check here
46 return QOhosPlatformIntegration::instance() != nullptr
47 && !QCoreApplication::startingUp()
48 && !QCoreApplication::closingDown();
49}
50
51std::tuple<QPointF, QPointF> getLocalAndGlobalPointsOrDefault(OH_NativeXComponent *component, void *window)
52{
53 OH_NativeXComponent_MouseEvent event;
54 return OH_NativeXComponent_GetMouseEvent(component, window, &event) == OH_NATIVEXCOMPONENT_RESULT_SUCCESS
55 ? std::tuple<QPointF, QPointF>({{event.x, event.y}, {event.screenX, event.screenY}})
56 : std::tuple<QPointF, QPointF>({{}, {}});
57}
58
60{
61 float x;
62 float y;
63
64 return
65 ::OH_NativeXComponent_GetTouchPointDisplayX(xComponent.handle(), touchPointIndex, &x)
66 == ::OH_NATIVEXCOMPONENT_RESULT_SUCCESS
67 && ::OH_NativeXComponent_GetTouchPointDisplayY(xComponent.handle(), touchPointIndex, &y)
68 == ::OH_NATIVEXCOMPONENT_RESULT_SUCCESS
69 ? makeQOhosOptional(QPointF{x, y})
70 : makeEmptyQOhosOptional();
71}
72
74 QXComponentRender xComponent, const OH_NativeXComponent_TouchEvent &touchEvent,
75 std::uint32_t pointIndex)
76{
77 auto touchDisplayPosition = tryGetTouchPointDisplayPosition(xComponent, pointIndex);
78 if (!touchDisplayPosition.has_value())
80
81 ::OH_NativeXComponent_TouchPointToolType toolType = ::OH_NATIVEXCOMPONENT_TOOL_TYPE_UNKNOWN;
82 std::int32_t resToolType = ::OH_NativeXComponent_GetTouchPointToolType(xComponent.handle(), pointIndex, &toolType);
83 if (resToolType != ::OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
84 qOhosCritical(QtForOhos)
85 << "OH_NativeXComponent_GetTouchPointToolType() failed,"
86 << "touchPoint id:" << touchEvent.touchPoints[pointIndex].id << "result:" << resToolType;
87 toolType = ::OH_NATIVEXCOMPONENT_TOOL_TYPE_UNKNOWN;
88 }
89
90 float tiltX = 0;
91 float tiltY = 0;
92 if (toolType == ::OH_NATIVEXCOMPONENT_TOOL_TYPE_PEN) {
93 std::int32_t resTiltX = ::OH_NativeXComponent_GetTouchPointTiltX(xComponent.handle(), pointIndex, &tiltX);
94 if (resTiltX != ::OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
95 qOhosCritical(QtForOhos)
96 << "OH_NativeXComponent_GetTouchPointTiltX() failed,"
97 << "touchPoint id:" << touchEvent.touchPoints[pointIndex].id << "result:" << resTiltX;
98 tiltX = 0;
99 }
100
101 std::int32_t resTiltY = ::OH_NativeXComponent_GetTouchPointTiltY(xComponent.handle(), pointIndex, &tiltY);
102 if (resTiltY != ::OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
103 qOhosCritical(QtForOhos)
104 << "OH_NativeXComponent_GetTouchPointTiltY() failed,"
105 << "touchPoint id:" << touchEvent.touchPoints[pointIndex].id << "result:" << resTiltY;
106 tiltY = 0;
107 }
108 }
109
110 return makeQOhosOptional(
112 .touchPoint = touchEvent.touchPoints[pointIndex],
113 .toolType = toolType,
114 .displayPosition = touchDisplayPosition.value(),
115 .tiltX = tiltX,
116 .tiltY = tiltY,
117 });
118}
119
120QInputDevice::DeviceType getTouchDeviceType(::OH_NativeXComponent *component, std::int32_t pointId)
121{
122 ::OH_NativeXComponent_EventSourceType sourceType = ::OH_NATIVEXCOMPONENT_SOURCE_TYPE_UNKNOWN;
123 std::int32_t res = ::OH_NativeXComponent_GetTouchEventSourceType(component, pointId, &sourceType);
124 if (res != ::OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
125 qOhosCritical(QtForOhos)
126 << "OH_NativeXComponent_GetTouchEventSourceType() failed,"
127 << "pointId:" << pointId << "result:" << res;
128 return QInputDevice::DeviceType::TouchPad;
129 }
130
131 return sourceType == ::OH_NATIVEXCOMPONENT_SOURCE_TYPE_TOUCHSCREEN
132 ? QInputDevice::DeviceType::TouchScreen
133 : QInputDevice::DeviceType::TouchPad;
134}
135
136bool isModifierKey(OH_NativeXComponent_KeyCode keyCode)
137{
138 switch (keyCode) {
139 case OH_NativeXComponent_KeyCode::KEY_SHIFT_LEFT:
140 case OH_NativeXComponent_KeyCode::KEY_SHIFT_RIGHT:
141 case OH_NativeXComponent_KeyCode::KEY_ALT_LEFT:
142 case OH_NativeXComponent_KeyCode::KEY_ALT_RIGHT:
143 case OH_NativeXComponent_KeyCode::KEY_CTRL_LEFT:
144 case OH_NativeXComponent_KeyCode::KEY_CTRL_RIGHT:
145 case OH_NativeXComponent_KeyCode::KEY_META_LEFT:
146 case OH_NativeXComponent_KeyCode::KEY_META_RIGHT:
147 case OH_NativeXComponent_KeyCode::KEY_CAPS_LOCK:
148 case OH_NativeXComponent_KeyCode::KEY_NUM_LOCK:
149 return true;
150 default:
151 return false;
152 }
153}
154
155QOhosOptional<QEvent::Type> tryMapXComponentMouseEventActionToQEventType(::OH_NativeXComponent_MouseEventAction action)
156{
157 switch (action) {
158 case OH_NATIVEXCOMPONENT_MOUSE_PRESS:
159 return makeQOhosOptional(QEvent::MouseButtonPress);
160 case OH_NATIVEXCOMPONENT_MOUSE_RELEASE:
161 return makeQOhosOptional(QEvent::MouseButtonRelease);
162 case OH_NATIVEXCOMPONENT_MOUSE_MOVE:
163 return makeQOhosOptional(QEvent::MouseMove);
164 case OH_NATIVEXCOMPONENT_MOUSE_NONE:
165 case OH_NATIVEXCOMPONENT_MOUSE_CANCEL:
166 break;
167 }
169}
170
171QOhosOptional<Qt::MouseButton> tryMapXComponentMouseButtonToQt(::OH_NativeXComponent_MouseEventButton button)
172{
173 switch (button) {
174 case OH_NATIVEXCOMPONENT_LEFT_BUTTON:
175 return makeQOhosOptional(Qt::LeftButton);
176 case OH_NATIVEXCOMPONENT_MIDDLE_BUTTON:
177 return makeQOhosOptional(Qt::MiddleButton);
178 case OH_NATIVEXCOMPONENT_RIGHT_BUTTON:
179 return makeQOhosOptional(Qt::RightButton);
180 case OH_NATIVEXCOMPONENT_BACK_BUTTON:
181 return makeQOhosOptional(Qt::BackButton);
182 case OH_NATIVEXCOMPONENT_FORWARD_BUTTON:
183 return makeQOhosOptional(Qt::ForwardButton);
184 case OH_NATIVEXCOMPONENT_NONE_BUTTON:
185 break;
186 }
188}
189
190
191}
192
193QOhosNativeXComponentInputHandler::QOhosNativeXComponentInputHandler(
194 QXComponentRender xcomponent,
195 QtOhos::QThreadSafeRef<QWindow> qWindowRef,
196 QtOhos::QThreadSafeRef<QOhosInputMethodEventHandler> imEventHandlerRef)
200{
201}
202
203void QOhosNativeXComponentInputHandler::handleTouchEvent(void *window)
204{
206 return;
207
208 OH_NativeXComponent_TouchEvent touchEvent;
209 int32_t retcode = OH_NativeXComponent_GetTouchEvent(m_xComponent.handle(), window, &touchEvent);
210 if (retcode != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
211 qOhosCritical(QtForOhos) << "Unable to obtain TouchEvent from XComponent";
212 return;
213 }
214
215 std::vector<QOhosTouchEventTouchPointData> validTouchPoints;
216
217 ::OH_NativeXComponent_EventSourceType sourceType = ::OH_NATIVEXCOMPONENT_SOURCE_TYPE_UNKNOWN;
218 std::int32_t resSourceType = ::OH_NativeXComponent_GetTouchEventSourceType(m_xComponent.handle(), touchEvent.id, &sourceType);
219 if (resSourceType != ::OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
220 qOhosCritical(QtForOhos)
221 << "OH_NativeXComponent_GetTouchEventSourceType() failed,"
222 << "touchEvent id:" << touchEvent.id << "result:" << resSourceType;
223 sourceType = ::OH_NATIVEXCOMPONENT_SOURCE_TYPE_UNKNOWN;
224 }
225
226 bool isMouseOrUnknownTouchType =
227 sourceType == OH_NATIVEXCOMPONENT_SOURCE_TYPE_UNKNOWN
228 || sourceType == OH_NATIVEXCOMPONENT_SOURCE_TYPE_MOUSE;
229 if (isMouseOrUnknownTouchType)
230 return;
231
232 for (std::uint32_t pointIndex = 0; pointIndex < touchEvent.numPoints; ++pointIndex) {
233 auto pointData = tryMakeTouchEventPointData(m_xComponent, touchEvent, pointIndex);
234 if (pointData.has_value())
235 validTouchPoints.push_back(pointData.value());
236 }
237
238 if (validTouchPoints.empty())
239 return;
240
241 if (!m_optTouchEventsHandler) {
242 auto weakSelf = sharedFromThis().toWeakRef();
243 m_optTouchEventsHandler = makeQtOhosSimpleBatchingQtRequestsHandler<TouchEvent>(
244 m_imEventHandlerRef.toQObjectThreadSafeRef(),
245 [weakSelf](std::vector<TouchEvent> &&batch) {
246 auto sharedSelf = weakSelf.toStrongRef();
247 if (!sharedSelf.isNull())
248 sharedSelf->processTouchEventsInQtThread(std::move(batch));
249 });
250 }
251
252 m_optTouchEventsHandler(
253 TouchEvent{
254 .timestamp = std::chrono::steady_clock::now(),
255 .touchTimeStamp = std::chrono::nanoseconds(touchEvent.timeStamp),
256 .touchPoints = validTouchPoints,
257 .deviceType = getTouchDeviceType(m_xComponent.handle(), touchEvent.id),
258 .modifiers = readKeyModifiersFromKeyState(
259 QSpan(keysToModifiers.data(), keysToModifiers.size())),
260 });
261}
262
263void QOhosNativeXComponentInputHandler::handleMouseEvent(void *window)
264{
266 return;
267 OH_NativeXComponent_MouseEvent ev;
268 const std::int32_t retcode = OH_NativeXComponent_GetMouseEvent(m_xComponent.handle(), window, &ev);
269 if (retcode != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
270 qOhosCritical(QtForOhos) << "Unable to retrieve MouseEvent from XComponent";
271 return;
272 }
273
274 if (!m_optMouseEventsHandler) {
275 auto weakSelf = sharedFromThis().toWeakRef();
276 m_optMouseEventsHandler = makeQtOhosBatchingQtRequestsHandler<std::vector<MouseEvent>>(
277 m_imEventHandlerRef.toQObjectThreadSafeRef(),
278 [weakSelf](std::vector<MouseEvent> &&batch) {
279 auto sharedSelf = weakSelf.toStrongRef();
280 if (!sharedSelf.isNull())
281 sharedSelf->processMouseEventsInQtThread(std::move(batch));
282 });
283 }
284
285 m_optMouseEventsHandler(
286 [&](std::vector<MouseEvent> &batch) {
287 auto now = std::chrono::steady_clock::now();
288 MouseEvent newEvent{now, ev};
289 if (!batch.empty() && mayDropMouseEvent(now, batch.back(), newEvent))
290 batch.pop_back();
291 batch.push_back(newEvent);
292 });
293}
294
295void QOhosNativeXComponentInputHandler::handleHoverEvent(void *nativeWindow, bool isHover)
296{
298 return;
299 auto __dbg = make_QCScopedDebug("QOhosNativeXComponentInputDispatcher::dispatchHoverEvent");
300
301 QPointF localPoint;
302 QPointF globalPoint;
303 std::tie(localPoint, globalPoint) = getLocalAndGlobalPointsOrDefault(m_xComponent.handle(), nativeWindow);
304
305 invokeMethodLaterIfSelfExists(
306 m_imEventHandlerRef.toQObjectThreadSafeRef(),
307 [isHover, localPoint, globalPoint](QOhosNativeXComponentInputHandler &self) {
308 self.m_imEventHandlerRef.data()->onHoverEvent(
309 QOhosHoverEvent {
310 .targetWindow = self.m_qWindowRef.data(),
311 .localPosition = localPoint,
312 .globalPosition = globalPoint,
313 .isHover = isHover,
314 });
315 });
316}
317
318void QOhosNativeXComponentInputHandler::processMouseEventsInQtThread(std::vector<MouseEvent> &&batch)
319{
320 auto now = std::chrono::steady_clock::now();
321
322 QWindow *associatedWindowHandle = m_qWindowRef.data();
323 for (std::size_t i = 0; i < batch.size(); ++i) {
324 if (i + 1 < batch.size() && mayDropMouseEvent(now, batch[i], batch[i + 1]))
325 continue;
326
327 auto event = batch[i].event;
328
329 auto eventType = tryMapXComponentMouseEventActionToQEventType(event.action);
330 if (!eventType.has_value()) {
331 qOhosPrintfDebug(
332 "%s: got unsupported action in mouse event (%d), ignoring",
333 Q_FUNC_INFO, event.action);
334 continue;
335 }
336
337 auto optButton = tryMapXComponentMouseButtonToQt(event.button);
338 if (!optButton.has_value())
339 qOhosWarning(QtForOhos) << "Unexpected mouse button!";
340
341 // HACK / FIXME
342 // Temporary globalPos set to in OH window po - as we still live in single window world, so the
343 // window is our screen...
344 QPointF globalPos(event.screenX, event.screenY);
345 QPointF localPos(event.x, event.y);
346
347 m_imEventHandlerRef.data()->onMouseEvent(
348 {
349 .targetWindow = associatedWindowHandle,
350 .timestampMs = std::chrono::duration_cast<std::chrono::milliseconds>(
351 std::chrono::nanoseconds(event.timestamp)),
352 .localPosition = localPos,
353 .globalPosition = globalPos,
354 .button = optButton.value_or(Qt::NoButton),
355 .eventType = eventType.value(),
356 .modifiers = readKeyModifiersFromKeyState(
357 QSpan(keysToModifiers.data(), keysToModifiers.size())),
358 });
359 }
360}
361
362void QOhosNativeXComponentInputHandler::processTouchEventsInQtThread(std::vector<TouchEvent> &&batch)
363{
364 QWindow *window = m_qWindowRef.data();
365 auto now = std::chrono::steady_clock::now();
366 for (auto &event : batch) {
367 if (now - event.timestamp >= touchEventMinAgeForDrop) {
368 event.touchPoints.erase(
369 QtOhos::removeMatchingWithLookahead(
370 event.touchPoints.begin(), event.touchPoints.end(),
371 [](const auto &touchPointData, const auto &nextTouchPointData) {
372 return
373 touchPointData.touchPoint.type == OH_NATIVEXCOMPONENT_MOVE
374 && nextTouchPointData.touchPoint.type == OH_NATIVEXCOMPONENT_MOVE;
375 }),
376 event.touchPoints.end());
377 }
378 m_imEventHandlerRef.data()->onTouchEventFromXComponent(
379 window, event.touchTimeStamp, event.touchPoints, event.deviceType, event.modifiers);
380 }
381}
382
383void QOhosNativeXComponentInputHandler::invokeMethodLaterIfSelfExists(
384 QtOhos::QObjectThreadSafeRef ctxRef, std::function<void(QOhosNativeXComponentInputHandler &)> callback)
385{
386 auto weakSelf = sharedFromThis().toWeakRef();
387 ctxRef.visitInQtThreadIfAlive(
388 [weakSelf, callback = std::move(callback)](auto &) {
389 auto sharedSelf = weakSelf.toStrongRef();
390 if (!sharedSelf.isNull()) {
391 callback(*sharedSelf);
392 };
393 });
394}
395
396bool QOhosNativeXComponentInputHandler::mayDropMouseEvent(
397 std::chrono::steady_clock::time_point now,
398 const MouseEvent &event, const MouseEvent &nextEvent)
399{
400 return
401 now - event.timestamp >= mouseMotionEventMinAgeForDrop
402 && event.event.action == OH_NATIVEXCOMPONENT_MOUSE_MOVE
403 && nextEvent.event.action == OH_NATIVEXCOMPONENT_MOUSE_MOVE;
404}
405
406void QOhosNativeXComponentInputHandler::handleKeyEvent()
407{
409 return;
410 OH_NativeXComponent_KeyEvent *keyEvent = nullptr;
411 OH_NativeXComponent_KeyAction keyAction = {};
412 OH_NativeXComponent_KeyCode keyCode = {};
413
414 if (OH_NativeXComponent_GetKeyEvent(m_xComponent.handle(), &keyEvent) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
415 qOhosCritical(QtForOhos) << "Could not obtain correct KeyEvent!";
416 return;
417 }
418
419 if (OH_NativeXComponent_GetKeyEventAction(keyEvent, &keyAction) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
420 qOhosCritical(QtForOhos) << "Could not obtain correct Action for given KeyEvent!";
421 return;
422 }
423
424 if (OH_NativeXComponent_GetKeyEventCode(keyEvent, &keyCode) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
425 qOhosCritical(QtForOhos) << "Could not obtain correct KeyCode for given KeyEvent!";
426 return;
427 }
428
429 const auto keyModifiers = readKeyModifiersFromKeyState(
430 QSpan(keysToModifiers.data(), keysToModifiers.size()));
431
432 auto ohosKeyEvent = makeQOhosXComponentKeyEvent(keyAction, keyCode, keyModifiers);
433
434 if (isModifierKey(keyCode) && m_lastKeyEvent && m_lastKeyEvent->equals(*ohosKeyEvent))
435 return;
436 m_lastKeyEvent = ohosKeyEvent;
437
438 invokeMethodLaterIfSelfExists(
439 m_imEventHandlerRef.toQObjectThreadSafeRef(),
440 [ohosKeyEvent](QOhosNativeXComponentInputHandler &self) {
441 self.m_imEventHandlerRef.data()->onKeyEvent(*ohosKeyEvent, self.m_qWindowRef.data());
442 });
443}
444
445QT_END_NAMESPACE
QOhosNativeXComponentInputHandler(QXComponentRender xcomponent, QtOhos::QThreadSafeRef< QWindow > qWindowRef, QtOhos::QThreadSafeRef< QOhosInputMethodEventHandler > imEventHandlerRef)
void handleHoverEvent(void *nativeWindow, bool isHover)
QOhosOptional< QEvent::Type > tryMapXComponentMouseEventActionToQEventType(::OH_NativeXComponent_MouseEventAction action)
QOhosOptional< QPointF > tryGetTouchPointDisplayPosition(QXComponentRender xComponent, std::int32_t touchPointIndex)
std::tuple< QPointF, QPointF > getLocalAndGlobalPointsOrDefault(OH_NativeXComponent *component, void *window)
QOhosOptional< QOhosTouchEventTouchPointData > tryMakeTouchEventPointData(QXComponentRender xComponent, const OH_NativeXComponent_TouchEvent &touchEvent, std::uint32_t pointIndex)
const std::array< OhosKeyToModifier, 6 > keysToModifiers
QOhosOptional< Qt::MouseButton > tryMapXComponentMouseButtonToQt(::OH_NativeXComponent_MouseEventButton button)
bool isModifierKey(OH_NativeXComponent_KeyCode keyCode)
QInputDevice::DeviceType getTouchDeviceType(::OH_NativeXComponent *component, std::int32_t pointId)
std::nullopt_t makeEmptyQOhosOptional()
QXComponent< QXComponentType::Render > QXComponentRender
Definition qxcomponent.h:43