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
73QInputDevice::DeviceType getTouchDeviceType(::OH_NativeXComponent *component, std::int32_t pointId)
74{
75 ::OH_NativeXComponent_EventSourceType sourceType = ::OH_NATIVEXCOMPONENT_SOURCE_TYPE_UNKNOWN;
76 std::int32_t res = ::OH_NativeXComponent_GetTouchEventSourceType(component, pointId, &sourceType);
77 if (res != ::OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
78 qOhosCritical(QtForOhos)
79 << "OH_NativeXComponent_GetTouchEventSourceType() failed,"
80 << "pointId:" << pointId << "result:" << res;
81 return QInputDevice::DeviceType::TouchPad;
82 }
83
84 return sourceType == ::OH_NATIVEXCOMPONENT_SOURCE_TYPE_TOUCHSCREEN
85 ? QInputDevice::DeviceType::TouchScreen
86 : QInputDevice::DeviceType::TouchPad;
87}
88
89bool isModifierKey(OH_NativeXComponent_KeyCode keyCode)
90{
91 switch (keyCode) {
92 case OH_NativeXComponent_KeyCode::KEY_SHIFT_LEFT:
93 case OH_NativeXComponent_KeyCode::KEY_SHIFT_RIGHT:
94 case OH_NativeXComponent_KeyCode::KEY_ALT_LEFT:
95 case OH_NativeXComponent_KeyCode::KEY_ALT_RIGHT:
96 case OH_NativeXComponent_KeyCode::KEY_CTRL_LEFT:
97 case OH_NativeXComponent_KeyCode::KEY_CTRL_RIGHT:
98 case OH_NativeXComponent_KeyCode::KEY_META_LEFT:
99 case OH_NativeXComponent_KeyCode::KEY_META_RIGHT:
100 case OH_NativeXComponent_KeyCode::KEY_CAPS_LOCK:
101 case OH_NativeXComponent_KeyCode::KEY_NUM_LOCK:
102 return true;
103 default:
104 return false;
105 }
106}
107
108QOhosOptional<QEvent::Type> tryMapXComponentMouseEventActionToQEventType(::OH_NativeXComponent_MouseEventAction action)
109{
110 switch (action) {
111 case OH_NATIVEXCOMPONENT_MOUSE_PRESS:
112 return makeQOhosOptional(QEvent::MouseButtonPress);
113 case OH_NATIVEXCOMPONENT_MOUSE_RELEASE:
114 return makeQOhosOptional(QEvent::MouseButtonRelease);
115 case OH_NATIVEXCOMPONENT_MOUSE_MOVE:
116 return makeQOhosOptional(QEvent::MouseMove);
117 case OH_NATIVEXCOMPONENT_MOUSE_NONE:
118 case OH_NATIVEXCOMPONENT_MOUSE_CANCEL:
119 break;
120 }
122}
123
124QOhosOptional<Qt::MouseButton> tryMapXComponentMouseButtonToQt(::OH_NativeXComponent_MouseEventButton button)
125{
126 switch (button) {
127 case OH_NATIVEXCOMPONENT_LEFT_BUTTON:
128 return makeQOhosOptional(Qt::LeftButton);
129 case OH_NATIVEXCOMPONENT_MIDDLE_BUTTON:
130 return makeQOhosOptional(Qt::MiddleButton);
131 case OH_NATIVEXCOMPONENT_RIGHT_BUTTON:
132 return makeQOhosOptional(Qt::RightButton);
133 case OH_NATIVEXCOMPONENT_BACK_BUTTON:
134 return makeQOhosOptional(Qt::BackButton);
135 case OH_NATIVEXCOMPONENT_FORWARD_BUTTON:
136 return makeQOhosOptional(Qt::ForwardButton);
137 case OH_NATIVEXCOMPONENT_NONE_BUTTON:
138 break;
139 }
141}
142
143
144}
145
146QOhosNativeXComponentInputHandler::QOhosNativeXComponentInputHandler(
147 QXComponentRender xcomponent,
148 QtOhos::QThreadSafeRef<QWindow> qWindowRef,
149 QtOhos::QThreadSafeRef<QOhosInputMethodEventHandler> imEventHandlerRef)
153{
154}
155
156void QOhosNativeXComponentInputHandler::handleTouchEvent(void *window)
157{
159 return;
160
161 OH_NativeXComponent_TouchEvent touchEvent;
162 int32_t retcode = OH_NativeXComponent_GetTouchEvent(m_xComponent.handle(), window, &touchEvent);
163 if (retcode != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
164 qOhosCritical(QtForOhos) << "Unable to obtain TouchEvent from XComponent";
165 return;
166 }
167
168 std::vector<QOhosTouchEventTouchPointData> validTouchPoints;
169
170 ::OH_NativeXComponent_EventSourceType tool = ::OH_NATIVEXCOMPONENT_SOURCE_TYPE_UNKNOWN;
171 std::int32_t res = ::OH_NativeXComponent_GetTouchEventSourceType(m_xComponent.handle(), touchEvent.id, &tool);
172 if (res != ::OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
173 qOhosCritical(QtForOhos)
174 << "OH_NativeXComponent_GetTouchEventSourceType() failed,"
175 << "touchEvent id:" << touchEvent.id << "result:" << res;
176 tool = ::OH_NATIVEXCOMPONENT_SOURCE_TYPE_UNKNOWN;
177 }
178
179 bool isMouseOrUnknownTouchType =
180 tool == OH_NATIVEXCOMPONENT_SOURCE_TYPE_UNKNOWN
181 || tool == OH_NATIVEXCOMPONENT_SOURCE_TYPE_MOUSE;
182 if (isMouseOrUnknownTouchType)
183 return;
184
185 for (std::uint32_t i = 0; i < touchEvent.numPoints; ++i) {
186 auto touchDisplayPosition = tryGetTouchPointDisplayPosition(m_xComponent, i);
187 if (!touchDisplayPosition.hasValue())
188 continue;
189
190 validTouchPoints.push_back(QOhosTouchEventTouchPointData{
191 .touchPoint = touchEvent.touchPoints[i],
192 .displayPosition = touchDisplayPosition.value(),
193 });
194 }
195
196 if (validTouchPoints.empty())
197 return;
198
199 if (!m_optTouchEventsHandler) {
200 auto weakSelf = sharedFromThis().toWeakRef();
201 m_optTouchEventsHandler = makeQtOhosSimpleBatchingQtRequestsHandler<TouchEvent>(
202 m_imEventHandlerRef.toQObjectThreadSafeRef(),
203 [weakSelf](std::vector<TouchEvent> &&batch) {
204 auto sharedSelf = weakSelf.toStrongRef();
205 if (!sharedSelf.isNull())
206 sharedSelf->processTouchEventsInQtThread(std::move(batch));
207 });
208 }
209
210 m_optTouchEventsHandler(
211 TouchEvent{
212 .timestamp = std::chrono::steady_clock::now(),
213 .touchTimeStamp = std::chrono::nanoseconds(touchEvent.timeStamp),
214 .touchPoints = validTouchPoints,
215 .deviceType = getTouchDeviceType(m_xComponent.handle(), touchEvent.id),
216 });
217}
218
219void QOhosNativeXComponentInputHandler::handleMouseEvent(void *window)
220{
222 return;
223 OH_NativeXComponent_MouseEvent ev;
224 const std::int32_t retcode = OH_NativeXComponent_GetMouseEvent(m_xComponent.handle(), window, &ev);
225 if (retcode != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
226 qOhosCritical(QtForOhos) << "Unable to retrieve MouseEvent from XComponent";
227 return;
228 }
229
230 if (!m_optMouseEventsHandler) {
231 auto weakSelf = sharedFromThis().toWeakRef();
232 m_optMouseEventsHandler = makeQtOhosBatchingQtRequestsHandler<std::vector<MouseEvent>>(
233 m_imEventHandlerRef.toQObjectThreadSafeRef(),
234 [weakSelf](std::vector<MouseEvent> &&batch) {
235 auto sharedSelf = weakSelf.toStrongRef();
236 if (!sharedSelf.isNull())
237 sharedSelf->processMouseEventsInQtThread(std::move(batch));
238 });
239 }
240
241 m_optMouseEventsHandler(
242 [&](std::vector<MouseEvent> &batch) {
243 auto now = std::chrono::steady_clock::now();
244 MouseEvent newEvent{now, ev};
245 if (!batch.empty() && mayDropMouseEvent(now, batch.back(), newEvent))
246 batch.pop_back();
247 batch.push_back(newEvent);
248 });
249}
250
251void QOhosNativeXComponentInputHandler::handleHoverEvent(void *nativeWindow, bool isHover)
252{
254 return;
255 auto __dbg = make_QCScopedDebug("QOhosNativeXComponentInputDispatcher::dispatchHoverEvent");
256
257 QPointF localPoint;
258 QPointF globalPoint;
259 std::tie(localPoint, globalPoint) = getLocalAndGlobalPointsOrDefault(m_xComponent.handle(), nativeWindow);
260
261 invokeMethodLaterIfSelfExists(
262 m_imEventHandlerRef.toQObjectThreadSafeRef(),
263 [isHover, localPoint, globalPoint](QOhosNativeXComponentInputHandler &self) {
264 self.m_imEventHandlerRef.data()->onHoverEvent(isHover, localPoint, globalPoint, self.m_qWindowRef.data());
265 });
266}
267
268void QOhosNativeXComponentInputHandler::processMouseEventsInQtThread(std::vector<MouseEvent> &&batch)
269{
270 auto now = std::chrono::steady_clock::now();
271
272 QWindow *associatedWindowHandle = m_qWindowRef.data();
273 for (std::size_t i = 0; i < batch.size(); ++i) {
274 if (i + 1 < batch.size() && mayDropMouseEvent(now, batch[i], batch[i + 1]))
275 continue;
276
277 auto event = batch[i].event;
278 auto optButton = tryMapXComponentMouseButtonToQt(event.button);
279 if (!optButton.hasValue())
280 qOhosWarning(QtForOhos) << "Unexpected mouse button!";
281
282 // HACK / FIXME
283 // Temporary globalPos set to in OH window po - as we still live in single window world, so the
284 // window is our screen...
285 QPointF globalPos(event.screenX, event.screenY);
286 QPointF localPos(event.x, event.y);
287
288 m_imEventHandlerRef.data()->onMouseEvent(
289 {
290 .targetWindow = associatedWindowHandle,
291 .timestampMs = std::chrono::duration_cast<std::chrono::milliseconds>(
292 std::chrono::nanoseconds(event.timestamp)),
293 .localPosition = localPos,
294 .globalPosition = globalPos,
295 .button = optButton.valueOr(Qt::NoButton),
296 .eventType = tryMapXComponentMouseEventActionToQEventType(event.action).valueOr(QEvent::None),
297 .modifiers = readKeyModifiersFromKeyState(
298 QSpan(keysToModifiers.data(), keysToModifiers.size())),
299 });
300 }
301}
302
303void QOhosNativeXComponentInputHandler::processTouchEventsInQtThread(std::vector<TouchEvent> &&batch)
304{
305 QWindow *window = m_qWindowRef.data();
306 auto now = std::chrono::steady_clock::now();
307 for (auto &event : batch) {
308 if (now - event.timestamp >= touchEventMinAgeForDrop) {
309 event.touchPoints.erase(
310 QtOhos::removeMatchingWithLookahead(
311 event.touchPoints.begin(), event.touchPoints.end(),
312 [](const auto &touchPointData, const auto &nextTouchPointData) {
313 return
314 touchPointData.touchPoint.type == OH_NATIVEXCOMPONENT_MOVE
315 && nextTouchPointData.touchPoint.type == OH_NATIVEXCOMPONENT_MOVE;
316 }),
317 event.touchPoints.end());
318 }
319 m_imEventHandlerRef.data()->onTouchEventFromXComponent(
320 window, event.touchTimeStamp, event.touchPoints, event.deviceType);
321 }
322}
323
324void QOhosNativeXComponentInputHandler::invokeMethodLaterIfSelfExists(
325 QtOhos::QObjectThreadSafeRef ctxRef, std::function<void(QOhosNativeXComponentInputHandler &)> callback)
326{
327 auto weakSelf = sharedFromThis().toWeakRef();
328 ctxRef.visitInQtThreadIfAlive(
329 [weakSelf, callback = std::move(callback)](auto &) {
330 auto sharedSelf = weakSelf.toStrongRef();
331 if (!sharedSelf.isNull()) {
332 callback(*sharedSelf);
333 };
334 });
335}
336
337bool QOhosNativeXComponentInputHandler::mayDropMouseEvent(
338 std::chrono::steady_clock::time_point now,
339 const MouseEvent &event, const MouseEvent &nextEvent)
340{
341 return
342 now - event.timestamp >= mouseMotionEventMinAgeForDrop
343 && event.event.action == OH_NATIVEXCOMPONENT_MOUSE_MOVE
344 && nextEvent.event.action == OH_NATIVEXCOMPONENT_MOUSE_MOVE;
345}
346
347void QOhosNativeXComponentInputHandler::handleKeyEvent()
348{
350 return;
351 OH_NativeXComponent_KeyEvent *keyEvent = nullptr;
352 OH_NativeXComponent_KeyAction keyAction = {};
353 OH_NativeXComponent_KeyCode keyCode = {};
354
355 if (OH_NativeXComponent_GetKeyEvent(m_xComponent.handle(), &keyEvent) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
356 qOhosCritical(QtForOhos) << "Could not obtain correct KeyEvent!";
357 return;
358 }
359
360 if (OH_NativeXComponent_GetKeyEventAction(keyEvent, &keyAction) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
361 qOhosCritical(QtForOhos) << "Could not obtain correct Action for given KeyEvent!";
362 return;
363 }
364
365 if (OH_NativeXComponent_GetKeyEventCode(keyEvent, &keyCode) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
366 qOhosCritical(QtForOhos) << "Could not obtain correct KeyCode for given KeyEvent!";
367 return;
368 }
369
370 const auto keyModifiers = readKeyModifiersFromKeyState(
371 QSpan(keysToModifiers.data(), keysToModifiers.size()));
372
373 auto ohosKeyEvent = makeQOhosXComponentKeyEvent(keyAction, keyCode, keyModifiers);
374
375 if (isModifierKey(keyCode) && m_lastKeyEvent && m_lastKeyEvent->equals(*ohosKeyEvent))
376 return;
377 m_lastKeyEvent = ohosKeyEvent;
378
379 invokeMethodLaterIfSelfExists(
380 m_imEventHandlerRef.toQObjectThreadSafeRef(),
381 [ohosKeyEvent](QOhosNativeXComponentInputHandler &self) {
382 self.m_imEventHandlerRef.data()->onKeyEvent(*ohosKeyEvent, self.m_qWindowRef.data());
383 });
384}
385
386QT_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)
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)
QOhosOptional< void > makeEmptyQOhosOptional()
QXComponent< QXComponentType::Render > QXComponentRender
Definition qxcomponent.h:43