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
androidjniinput.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
3// Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5// Qt-Security score:significant reason:default
6
7#include <QtGui/qtguiglobal.h>
8
10#include "androidjnimenu.h"
11#include "androidjnimain.h"
13
14#include <qpa/qplatformwindow.h>
15#include <qpa/qwindowsysteminterface.h>
16#include <QTouchEvent>
17#include <QPointer>
18
19#include <QGuiApplication>
20#include <QtMath>
21
23
24Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods");
25
26using namespace QtAndroid;
27
28Q_DECLARE_JNI_CLASS(QtInputInterface, "org/qtproject/qt/android/QtInputInterface")
29
31{
32 static bool m_ignoreMouseEvents = false;
34
36
38
39 static QPointer<QWindow> m_mouseGrabber;
40 static bool m_backKeyPressAccepted = false;
41 static bool m_menuKeyPressAccepted = false;
42
43
44 void updateSelection(int selStart, int selEnd, int candidatesStart, int candidatesEnd)
45 {
46 qCDebug(lcQpaInputMethods) << ">>> UPDATESELECTION" << selStart << selEnd << candidatesStart << candidatesEnd;
48 reg->callInterface<QtJniTypes::QtInputInterface, void>("updateSelection", selStart, selEnd,
49 candidatesStart, candidatesEnd);
50 }
51
52 void showSoftwareKeyboard(int left, int top, int width, int height, int inputHints, int enterKeyType)
53 {
55 reg->callInterface<QtJniTypes::QtInputInterface, void>(
56 "showSoftwareKeyboard", QtAndroidPrivate::activity(),
57 left, top, width, height, inputHints,
58 enterKeyType);
59 qCDebug(lcQpaInputMethods) << "@@@ SHOWSOFTWAREKEYBOARD" << left << top << width << height << inputHints << enterKeyType;
60 }
61
63 {
65 reg->callInterface<QtJniTypes::QtInputInterface>("resetSoftwareKeyboard");
66 qCDebug(lcQpaInputMethods) << "@@@ RESETSOFTWAREKEYBOARD";
67 }
68
70 {
72 reg->callInterface<QtJniTypes::QtInputInterface>("hideSoftwareKeyboard");
73 qCDebug(lcQpaInputMethods) << "@@@ HIDESOFTWAREKEYBOARD";
74 }
75
77 {
79 return reg->callInterface<QtJniTypes::QtInputInterface, jboolean>(
80 "isSoftwareKeyboardVisible");
81 }
82
84 {
85 return m_softwareKeyboardRect;
86 }
87
89 {
91 return reg->callInterface<QtJniTypes::QtInputInterface, jint>("getSelectionHandleWidth");
92 }
93
94 void updateHandles(int mode, QPoint editMenuPos, uint32_t editButtons, QPoint cursor, QPoint anchor, bool rtl)
95 {
97 reg->callInterface<QtJniTypes::QtInputInterface, void>(
98 "updateHandles", mode, editMenuPos.x(), editMenuPos.y(),
99 editButtons, cursor.x(), cursor.y(), anchor.x(), anchor.y(), rtl);
100 }
101
102 // from https://developer.android.com/reference/android/view/MotionEvent#getButtonState()
104 BUTTON_PRIMARY = 0x00000001,
105 BUTTON_SECONDARY = 0x00000002,
106 BUTTON_TERTIARY = 0x00000004,
107 BUTTON_BACK = 0x00000008,
108 BUTTON_FORWARD = 0x00000010,
111 };
112 Q_DECLARE_FLAGS(AndroidMouseButtons, AndroidMouseButton)
113
114 static Qt::MouseButtons toMouseButtons(jint j_buttons)
115 {
116 const auto buttons = static_cast<AndroidMouseButtons>(j_buttons);
117 Qt::MouseButtons mouseButtons;
118 if (buttons.testFlag(BUTTON_PRIMARY))
119 mouseButtons.setFlag(Qt::LeftButton);
120
121 if (buttons.testFlag(BUTTON_SECONDARY))
122 mouseButtons.setFlag(Qt::RightButton);
123
124 if (buttons.testFlag(BUTTON_TERTIARY))
125 mouseButtons.setFlag(Qt::MiddleButton);
126
127 if (buttons.testFlag(BUTTON_BACK))
128 mouseButtons.setFlag(Qt::BackButton);
129
130 if (buttons.testFlag(BUTTON_FORWARD))
131 mouseButtons.setFlag(Qt::ForwardButton);
132
133 if (buttons.testFlag(BUTTON_STYLUS_PRIMARY))
134 mouseButtons.setFlag(Qt::LeftButton);
135
136 if (buttons.testFlag(BUTTON_STYLUS_SECONDARY))
137 mouseButtons.setFlag(Qt::RightButton);
138
139 // Fall back to left button
140 if (Q_UNLIKELY(buttons != 0 && mouseButtons == Qt::NoButton)) {
141 qWarning() << "Unhandled button value:" << buttons << "Falling back to Qt::LeftButton";
142 mouseButtons = Qt::LeftButton;
143 }
144 return mouseButtons;
145 }
146
147 static void sendMouseButtonEvents(QWindow *topLevel, QPoint localPos, QPoint globalPos,
148 jint mouseButtonState, QEvent::Type type)
149 {
150 const Qt::MouseButtons qtButtons = toMouseButtons(mouseButtonState);
151 const bool mouseReleased = type == QEvent::MouseButtonRelease && qtButtons == Qt::NoButton;
152 const Qt::MouseButtons eventButtons = mouseReleased ? m_lastSeenButtons : qtButtons;
153 m_lastSeenButtons = qtButtons;
154
155 static_assert (sizeof(eventButtons) <= sizeof(uint), "Qt::MouseButtons size changed. Adapt code.");
156
157 if (eventButtons == Qt::NoButton)
158 return;
159
160 for (uint buttonInt = 0x1; static_cast<uint>(eventButtons) >= buttonInt; buttonInt <<= 1) {
161 const auto button = static_cast<Qt::MouseButton>(buttonInt);
162 if (eventButtons.testFlag(button)) {
163 QWindowSystemInterface::handleMouseEvent(topLevel, localPos, globalPos,
164 qtButtons, button, type);
165 }
166 }
167 }
168
169 static void mouseDown(JNIEnv */*env*/, jobject /*thiz*/, jint winId, jint x, jint y, jint mouseButtonState)
170 {
172 return;
173
174 const QPoint localPos(x,y);
175 QWindow *window = windowFromId(winId);
176 m_mouseGrabber = window;
177 const QPoint globalPos = window && window->handle() ?
178 window->handle()->mapToGlobal(localPos) : localPos;
179 sendMouseButtonEvents(window, localPos, globalPos, mouseButtonState, QEvent::MouseButtonPress);
180 }
181
182 static void mouseUp(JNIEnv */*env*/, jobject /*thiz*/, jint winId, jint x, jint y, jint mouseButtonState)
183 {
184 const QPoint localPos(x,y);
185 QWindow *window = m_mouseGrabber.data();
186 if (!window)
187 window = windowFromId(winId);
188
189 const QPoint globalPos = window && window->handle() ?
190 window->handle()->mapToGlobal(localPos) : localPos;
191
192 sendMouseButtonEvents(window, localPos, globalPos, mouseButtonState, QEvent::MouseButtonRelease);
193 m_ignoreMouseEvents = false;
194 m_mouseGrabber.clear();
195 }
196
197 static void mouseMove(JNIEnv */*env*/, jobject /*thiz*/, jint winId, jint x, jint y, jint mouseButtonState)
198 {
200 return;
201
202 const QPoint localPos(x,y);
203 QWindow *window = m_mouseGrabber.data();
204 if (!window)
205 window = windowFromId(winId);
206 const QPoint globalPos = window && window->handle() ?
207 window->handle()->mapToGlobal(localPos) : localPos;
208 const Qt::MouseButtons qtButtons = toMouseButtons(mouseButtonState);
209 m_lastSeenButtons = qtButtons;
210 QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos,
211 qtButtons, Qt::NoButton, QEvent::MouseMove);
212 }
213
214 static void mouseWheel(JNIEnv */*env*/, jobject /*thiz*/, jint winId, jint x, jint y, jfloat hdelta, jfloat vdelta)
215 {
217 return;
218
219 const QPoint localPos(x,y);
220 QWindow *window = m_mouseGrabber.data();
221 if (!window)
222 window = windowFromId(winId);
223 const QPoint globalPos = window && window->handle() ?
224 window->handle()->mapToGlobal(localPos) : localPos;
225 const QPoint angleDelta(hdelta * 120, vdelta * 120);
226
227 QWindowSystemInterface::handleWheelEvent(window,
228 localPos,
229 globalPos,
230 QPoint(),
231 angleDelta);
232 }
233
234 static void longPress(JNIEnv */*env*/, jobject /*thiz*/, jint winId, jint x, jint y)
235 {
237
238 const QPoint localPos(x,y);
239 QWindow *window = windowFromId(winId);
240 const QPoint globalPos = window && window->handle() ?
241 window->handle()->mapToGlobal(localPos) : localPos;
242
243 if (inputContext && qGuiApp)
244 QMetaObject::invokeMethod(inputContext, "longPress", Q_ARG(int, globalPos.x()), Q_ARG(int, globalPos.y()));
245
246 //### TODO: add proper API for Qt 5.2
247 static bool rightMouseFromLongPress = qEnvironmentVariableIntValue("QT_ANDROID_ENABLE_RIGHT_MOUSE_FROM_LONG_PRESS");
248 if (!rightMouseFromLongPress)
249 return;
250 m_ignoreMouseEvents = true;
251
252
253 // Click right button if no other button is already pressed.
254 if (!m_mouseGrabber) {
255 QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos,
256 Qt::MouseButtons(Qt::RightButton), Qt::RightButton,
257 QEvent::MouseButtonPress);
258 QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos,
259 Qt::MouseButtons(Qt::NoButton), Qt::RightButton,
260 QEvent::MouseButtonRelease);
261 }
262 }
263
264 static void touchBegin(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/)
265 {
266 m_touchPoints.clear();
267 }
268
269 static void touchAdd(JNIEnv */*env*/, jobject /*thiz*/, jint winId, jint id, jint action, jboolean /*primary*/, jint x, jint y,
270 jfloat major, jfloat minor, jfloat rotation, jfloat pressure)
271 {
272 QEventPoint::State state = QEventPoint::State::Stationary;
273 switch (action) {
274 case 0:
275 state = QEventPoint::State::Pressed;
276 break;
277 case 1:
278 state = QEventPoint::State::Updated;
279 break;
280 case 2:
281 state = QEventPoint::State::Stationary;
282 break;
283 case 3:
284 state = QEventPoint::State::Released;
285 break;
286 }
287
288
289 QSize availableSize;
290 if (auto *platformIntegration = QtAndroid::androidPlatformIntegration())
291 availableSize = platformIntegration->screen()->availableGeometry().size();
292 else
294
295 QWindow *window = QtAndroid::windowFromId(winId);
296 if (!window) {
297 qCWarning(lcQpaInputMethods, "Touch event received for non-existing window %d", winId);
298 return;
299 }
300
301 QPointF mappedTouchPoint;
302 if (window->handle())
303 mappedTouchPoint = window->handle()->mapToGlobalF(QPointF(x, y));
304 else
305 mappedTouchPoint = window->mapToGlobal(QPointF(x, y));
306
307 QWindowSystemInterface::TouchPoint touchPoint;
308 // Start numbering touch points from 1
309 touchPoint.id = id + 1;
310 touchPoint.pressure = pressure;
311 touchPoint.rotation = qRadiansToDegrees(rotation);
312 touchPoint.normalPosition = QPointF((mappedTouchPoint.x() / availableSize.width()),
313 (mappedTouchPoint.y() / availableSize.height()));
314 touchPoint.state = state;
315 touchPoint.area = QRectF(mappedTouchPoint.x() - double(minor * 0.5f),
316 mappedTouchPoint.y() - double(major * 0.5f),
317 double(minor),
318 double(major));
319
320 m_touchPoints.push_back(touchPoint);
321 if (state == QEventPoint::State::Pressed) {
323 if (inputContext && qGuiApp)
324 QMetaObject::invokeMethod(inputContext, "touchDown", Q_ARG(int, mappedTouchPoint.x()), Q_ARG(int, mappedTouchPoint.y()));
325 }
326 }
327
329 {
331 if (!platformIntegration)
332 return nullptr;
333
334 QPointingDevice *touchDevice = platformIntegration->touchDevice();
335 if (!touchDevice) {
336 touchDevice = new QPointingDevice("Android touchscreen", 1,
337 QInputDevice::DeviceType::TouchScreen,
338 QPointingDevice::PointerType::Finger,
339 QPointingDevice::Capability::Position
340 | QPointingDevice::Capability::Area
341 | QPointingDevice::Capability::Pressure
342 | QPointingDevice::Capability::NormalizedPosition,
343 10, 0);
344 QWindowSystemInterface::registerInputDevice(touchDevice);
345 platformIntegration->setTouchDevice(touchDevice);
346 }
347
348 return touchDevice;
349 }
350
351 static void touchEnd(JNIEnv * /*env*/, jobject /*thiz*/, jint winId, jint /*action*/)
352 {
353 if (m_touchPoints.isEmpty())
354 return;
355
356 QMutexLocker lock(QtAndroid::platformInterfaceMutex());
357 const QPointingDevice *touchDevice = getTouchDevice();
358 if (!touchDevice)
359 return;
360
361 QWindow *window = QtAndroid::windowFromId(winId);
362 if (!window)
363 return;
364 QWindowSystemInterface::handleTouchEvent(window, touchDevice, m_touchPoints);
365 }
366
367 static void touchCancel(JNIEnv * /*env*/, jobject /*thiz*/, jint winId)
368 {
369 if (m_touchPoints.isEmpty())
370 return;
371
372 QMutexLocker lock(QtAndroid::platformInterfaceMutex());
373 const QPointingDevice *touchDevice = getTouchDevice();
374 if (!touchDevice)
375 return;
376
377 QWindow *window = QtAndroid::windowFromId(winId);
378 if (!window)
379 return;
380 QWindowSystemInterface::handleTouchCancelEvent(window, touchDevice);
381 }
382
383 static bool isTabletEventSupported(JNIEnv */*env*/, jobject /*thiz*/)
384 {
385#if QT_CONFIG(tabletevent)
386 return true;
387#else
388 return false;
389#endif // QT_CONFIG(tabletevent)
390 }
391
392 static void tabletEvent(JNIEnv */*env*/, jobject /*thiz*/, jint winId, jint deviceId, jlong time, jint action,
393 jint pointerType, jint buttonState, jfloat x, jfloat y, jfloat pressure)
394 {
395#if QT_CONFIG(tabletevent)
396 const QPointF localPos(x, y);
397 QWindow *window = windowFromId(winId);
398 const QPointF globalPosF = window && window->handle() ?
399 window->handle()->mapFromGlobalF(localPos) : localPos;
400
401 // Galaxy Note with plain Android:
402 // 0 1 0 stylus press
403 // 2 1 0 stylus drag
404 // 1 1 0 stylus release
405 // 0 1 2 stylus press with side-button held
406 // 2 1 2 stylus drag with side-button held
407 // 1 1 2 stylus release with side-button held
408 // Galaxy Note 4 with Samsung firmware:
409 // 0 1 0 stylus press
410 // 2 1 0 stylus drag
411 // 1 1 0 stylus release
412 // 211 1 2 stylus press with side-button held
413 // 213 1 2 stylus drag with side-button held
414 // 212 1 2 stylus release with side-button held
415 // when action == ACTION_UP (1) it's a release; otherwise we say which button is pressed
416 Qt::MouseButtons buttons = Qt::NoButton;
417 switch (action) {
418 case 1: // ACTION_UP
419 case 6: // ACTION_POINTER_UP, happens if stylus is not the primary pointer
420 case 212: // stylus release while side-button held on Galaxy Note 4
421 buttons = Qt::NoButton;
422 break;
423 default: // action is press or drag
424 if (buttonState == 0)
425 buttons = Qt::LeftButton;
426 else // 2 means RightButton
427 buttons = Qt::MouseButtons(buttonState);
428 break;
429 }
430
431 qCDebug(lcQpaInputMethods) << action << pointerType << buttonState << '@' << x << y << "pressure" << pressure << ": buttons" << buttons;
432
433 QWindowSystemInterface::handleTabletEvent(window, ulong(time),
434 localPos, globalPosF, int(QInputDevice::DeviceType::Stylus), pointerType,
435 buttons, pressure, 0, 0, 0., 0., 0, deviceId, Qt::NoModifier);
436#endif // QT_CONFIG(tabletevent)
437 }
438
440 {
441 // 0--9 0x00000007 -- 0x00000010
442 if (key >= 0x00000007 && key <= 0x00000010)
443 return QKeyCombination::fromCombined(Qt::Key_0 + key - 0x00000007);
444
445 // A--Z 0x0000001d -- 0x00000036
446 if (key >= 0x0000001d && key <= 0x00000036)
447 return QKeyCombination::fromCombined(Qt::Key_A + key - 0x0000001d);
448
449 // F1--F12 0x00000083 -- 0x0000008e
450 if (key >= 0x00000083 && key <= 0x0000008e)
451 return QKeyCombination::fromCombined(Qt::Key_F1 + key - 0x00000083);
452
453 // NUMPAD_0--NUMPAD_9 0x00000090 -- 0x00000099
454 if (key >= 0x00000090 && key <= 0x00000099)
455 return QKeyCombination::fromCombined(Qt::KeypadModifier | Qt::Key_0 + key - 0x00000090);
456
457 // BUTTON_1--KEYCODE_BUTTON_16 0x000000bc -- 0x000000cb
458
459 switch (key) {
460 case 0x00000000: // KEYCODE_UNKNOWN
461 return Qt::Key_unknown;
462
463 case 0x00000001: // KEYCODE_SOFT_LEFT
464 return Qt::Key_Left;
465
466 case 0x00000002: // KEYCODE_SOFT_RIGHT
467 return Qt::Key_Right;
468
469 // 0x00000003: // KEYCODE_HOME is never delivered to applications.
470
471 case 0x00000004: // KEYCODE_BACK
472 return Qt::Key_Back;
473
474 case 0x00000005: // KEYCODE_CALL
475 return Qt::Key_Call;
476
477 case 0x00000006: // KEYCODE_ENDCALL
478 return Qt::Key_Hangup;
479
480 // 0--9 0x00000007 -- 0x00000010
481
482 case 0x00000011: // KEYCODE_STAR
483 return Qt::Key_Asterisk;
484
485 case 0x00000012: // KEYCODE_POUND
486 return Qt::Key_NumberSign;
487
488 case 0x00000013: //KEYCODE_DPAD_UP
489 return Qt::Key_Up;
490
491 case 0x00000014: // KEYCODE_DPAD_DOWN
492 return Qt::Key_Down;
493
494 case 0x00000015: //KEYCODE_DPAD_LEFT
495 return Qt::Key_Left;
496
497 case 0x00000016: //KEYCODE_DPAD_RIGHT
498 return Qt::Key_Right;
499
500 case 0x00000017: // KEYCODE_DPAD_CENTER
501 return Qt::Key_Enter;
502
503 case 0x00000018: // KEYCODE_VOLUME_UP
504 return Qt::Key_VolumeUp;
505
506 case 0x00000019: // KEYCODE_VOLUME_DOWN
507 return Qt::Key_VolumeDown;
508
509 case 0x0000001a:
510 return Qt::Key_PowerOff;
511
512 case 0x0000001b: // KEYCODE_CAMERA
513 return Qt::Key_Camera;
514
515 case 0x0000001c: // KEYCODE_CLEAR
516 return Qt::Key_Clear;
517
518 // A--Z 0x0000001d -- 0x00000036
519
520 case 0x00000037: // KEYCODE_COMMA
521 return Qt::Key_Comma;
522
523 case 0x00000038: // KEYCODE_PERIOD
524 return Qt::Key_Period;
525
526 case 0x00000039: // KEYCODE_ALT_LEFT
527 case 0x0000003a: // KEYCODE_ALT_RIGHT
528 return Qt::Key_Alt;
529
530 case 0x0000003b: // KEYCODE_SHIFT_LEFT
531 case 0x0000003c: // KEYCODE_SHIFT_RIGHT
532 return Qt::Key_Shift;
533
534 case 0x0000003d: // KEYCODE_TAB
535 return Qt::Key_Tab;
536
537 case 0x0000003e: // KEYCODE_SPACE
538 return Qt::Key_Space;
539
540 case 0x0000003f: // KEYCODE_SYM
541 return Qt::Key_Meta;
542
543 case 0x00000040: // KEYCODE_EXPLORER
544 return Qt::Key_Explorer;
545
546 case 0x00000041: //KEYCODE_ENVELOPE
547 return Qt::Key_LaunchMail;
548
549 case 0x00000042: // KEYCODE_ENTER
550 return Qt::Key_Return;
551
552 case 0x00000043: // KEYCODE_DEL
553 return Qt::Key_Backspace;
554
555 case 0x00000044: // KEYCODE_GRAVE
556 return Qt::Key_QuoteLeft;
557
558 case 0x00000045: // KEYCODE_MINUS
559 return Qt::Key_Minus;
560
561 case 0x00000046: // KEYCODE_EQUALS
562 return Qt::Key_Equal;
563
564 case 0x00000047: // KEYCODE_LEFT_BRACKET
565 return Qt::Key_BracketLeft;
566
567 case 0x00000048: // KEYCODE_RIGHT_BRACKET
568 return Qt::Key_BracketRight;
569
570 case 0x00000049: // KEYCODE_BACKSLASH
571 return Qt::Key_Backslash;
572
573 case 0x0000004a: // KEYCODE_SEMICOLON
574 return Qt::Key_Semicolon;
575
576 case 0x0000004b: // KEYCODE_APOSTROPHE
577 return Qt::Key_Apostrophe;
578
579 case 0x0000004c: // KEYCODE_SLASH
580 return Qt::Key_Slash;
581
582 case 0x0000004d: // KEYCODE_AT
583 return Qt::Key_At;
584
585 case 0x0000004e: // KEYCODE_NUM
586 return Qt::Key_Alt;
587
588 case 0x0000004f: // KEYCODE_HEADSETHOOK
589 return QKeyCombination::fromCombined(0);
590
591 case 0x00000050: // KEYCODE_FOCUS
592 return Qt::Key_CameraFocus;
593
594 case 0x00000051: // KEYCODE_PLUS
595 return Qt::Key_Plus;
596
597 case 0x00000052: // KEYCODE_MENU
598 return Qt::Key_Menu;
599
600 case 0x00000053: // KEYCODE_NOTIFICATION
601 return QKeyCombination::fromCombined(0);
602
603 case 0x00000054: // KEYCODE_SEARCH
604 return Qt::Key_Search;
605
606 case 0x00000055: // KEYCODE_MEDIA_PLAY_PAUSE
607 return Qt::Key_MediaTogglePlayPause;
608
609 case 0x00000056: // KEYCODE_MEDIA_STOP
610 return Qt::Key_MediaStop;
611
612 case 0x00000057: // KEYCODE_MEDIA_NEXT
613 return Qt::Key_MediaNext;
614
615 case 0x00000058: // KEYCODE_MEDIA_PREVIOUS
616 return Qt::Key_MediaPrevious;
617
618 case 0x00000059: // KEYCODE_MEDIA_REWIND
619 return Qt::Key_AudioRewind;
620
621 case 0x0000005a: // KEYCODE_MEDIA_FAST_FORWARD
622 return Qt::Key_AudioForward;
623
624 case 0x0000005b: // KEYCODE_MUTE
625 return Qt::Key_MicMute;
626
627 case 0x0000005c: // KEYCODE_PAGE_UP
628 return Qt::Key_PageUp;
629
630 case 0x0000005d: // KEYCODE_PAGE_DOWN
631 return Qt::Key_PageDown;
632
633 case 0x0000005e: // KEYCODE_PICTSYMBOLS
634 return QKeyCombination::fromCombined(0);
635
636 case 0x00000060: // KEYCODE_BUTTON_A
637 case 0x00000061: // KEYCODE_BUTTON_B
638 case 0x00000062: // KEYCODE_BUTTON_B
639 case 0x00000063: // KEYCODE_BUTTON_X
640 case 0x00000064: // KEYCODE_BUTTON_Y
641 case 0x00000065: // KEYCODE_BUTTON_Z
642 case 0x00000066: // KEYCODE_BUTTON_L1
643 case 0x00000067: // KEYCODE_BUTTON_R1
644 case 0x00000068: // KEYCODE_BUTTON_L2
645 case 0x00000069: // KEYCODE_BUTTON_R2
646 case 0x0000006a: // KEYCODE_BUTTON_THUMBL
647 case 0x0000006b: // KEYCODE_BUTTON_THUMBR
648 case 0x0000006c: // KEYCODE_BUTTON_START
649 case 0x0000006d: // KEYCODE_BUTTON_SELECT
650 case 0x0000006e: // KEYCODE_BUTTON_MODE
651 return QKeyCombination::fromCombined(0);
652
653 case 0x0000006f: // KEYCODE_ESCAPE
654 return Qt::Key_Escape;
655
656 case 0x00000070: // KEYCODE_FORWARD_DEL
657 return Qt::Key_Delete;
658
659 case 0x00000071: // KEYCODE_CTRL_LEFT
660 case 0x00000072: // KEYCODE_CTRL_RIGHT
661 return Qt::Key_Control;
662
663 case 0x00000073: // KEYCODE_CAPS_LOCK
664 return Qt::Key_CapsLock;
665
666 case 0x00000074: // KEYCODE_SCROLL_LOCK
667 return Qt::Key_ScrollLock;
668
669 case 0x00000075: // KEYCODE_META_LEFT
670 case 0x00000076: // KEYCODE_META_RIGHT
671 return Qt::Key_Meta;
672
673 case 0x00000077: // KEYCODE_FUNCTION
674 return QKeyCombination::fromCombined(0);
675
676 case 0x00000078: // KEYCODE_SYSRQ
677 return Qt::Key_Print;
678
679 case 0x00000079: // KEYCODE_BREAK
680 return Qt::Key_Pause;
681
682 case 0x0000007a: // KEYCODE_MOVE_HOME
683 return Qt::Key_Home;
684
685 case 0x0000007b: // KEYCODE_MOVE_END
686 return Qt::Key_End;
687
688 case 0x0000007c: // KEYCODE_MOVE_INSERT
689 return Qt::Key_Insert;
690
691 case 0x0000007d: // KEYCODE_FORWARD
692 return Qt::Key_Forward;
693
694 case 0x0000007e: // KEYCODE_MEDIA_PLAY
695 return Qt::Key_MediaPlay;
696
697 case 0x0000007f: // KEYCODE_MEDIA_PAUSE
698 return Qt::Key_MediaPause;
699
700 case 0x00000080: // KEYCODE_MEDIA_CLOSE
701 case 0x00000081: // KEYCODE_MEDIA_EJECT
702 return Qt::Key_Eject;
703
704 case 0x00000082: // KEYCODE_MEDIA_RECORD
705 return Qt::Key_MediaRecord;
706
707 // F1--F12 0x00000083 -- 0x0000008e
708
709 case 0x0000008f: // KEYCODE_NUM_LOCK
710 return Qt::Key_NumLock;
711
712 // NUMPAD_0--NUMPAD_9 0x00000090 -- 0x00000099
713
714 case 0x0000009a: // KEYCODE_NUMPAD_DIVIDE
715 return Qt::KeypadModifier | Qt::Key_Slash;
716
717 case 0x0000009b: // KEYCODE_NUMPAD_MULTIPLY
718 return Qt::KeypadModifier | Qt::Key_Asterisk;
719
720 case 0x0000009c: // KEYCODE_NUMPAD_SUBTRACT
721 return Qt::KeypadModifier | Qt::Key_Minus;
722
723 case 0x0000009d: // KEYCODE_NUMPAD_ADD
724 return Qt::KeypadModifier | Qt::Key_Plus;
725
726 case 0x0000009e: // KEYCODE_NUMPAD_DOT
727 return Qt::KeypadModifier | Qt::Key_Period;
728
729 case 0x0000009f: // KEYCODE_NUMPAD_COMMA
730 return Qt::KeypadModifier | Qt::Key_Comma;
731
732 case 0x000000a0: // KEYCODE_NUMPAD_ENTER
733 return Qt::Key_Enter;
734
735 case 0x000000a1: // KEYCODE_NUMPAD_EQUALS
736 return Qt::KeypadModifier | Qt::Key_Equal;
737
738 case 0x000000a2: // KEYCODE_NUMPAD_LEFT_PAREN
739 return Qt::Key_ParenLeft;
740
741 case 0x000000a3: // KEYCODE_NUMPAD_RIGHT_PAREN
742 return Qt::Key_ParenRight;
743
744 case 0x000000a4: // KEYCODE_VOLUME_MUTE
745 return Qt::Key_VolumeMute;
746
747 case 0x000000a5: // KEYCODE_INFO
748 return Qt::Key_Info;
749
750 case 0x000000a6: // KEYCODE_CHANNEL_UP
751 return Qt::Key_ChannelUp;
752
753 case 0x000000a7: // KEYCODE_CHANNEL_DOWN
754 return Qt::Key_ChannelDown;
755
756 case 0x000000a8: // KEYCODE_ZOOM_IN
757 return Qt::Key_ZoomIn;
758
759 case 0x000000a9: // KEYCODE_ZOOM_OUT
760 return Qt::Key_ZoomOut;
761
762 case 0x000000aa: // KEYCODE_TV
763 case 0x000000ab: // KEYCODE_WINDOW
764 return QKeyCombination::fromCombined(0);
765
766 case 0x000000ac: // KEYCODE_GUIDE
767 return Qt::Key_Guide;
768
769 case 0x000000ad: // KEYCODE_DVR
770 return QKeyCombination::fromCombined(0);
771
772 case 0x000000ae: // KEYCODE_BOOKMARK
773 return Qt::Key_AddFavorite;
774
775 case 0x000000af: // KEYCODE_CAPTIONS
776 return Qt::Key_Subtitle;
777
778 case 0x000000b0: // KEYCODE_SETTINGS
779 return Qt::Key_Settings;
780
781 case 0x000000b1: // KEYCODE_TV_POWER
782 case 0x000000b2: // KEYCODE_TV_INPUT
783 case 0x000000b3: // KEYCODE_STB_POWER
784 case 0x000000b4: // KEYCODE_STB_INPUT
785 case 0x000000b5: // KEYCODE_AVR_POWER
786 case 0x000000b6: // KEYCODE_AVR_INPUT
787 return QKeyCombination::fromCombined(0);
788
789 case 0x000000b7: // KEYCODE_PROG_RED
790 return Qt::Key_Red;
791
792 case 0x000000b8: // KEYCODE_PROG_GREEN
793 return Qt::Key_Green;
794
795 case 0x000000b9: // KEYCODE_PROG_YELLOW
796 return Qt::Key_Yellow;
797
798 case 0x000000ba: // KEYCODE_PROG_BLUE
799 return Qt::Key_Blue;
800
801 // 0x000000bb: // KEYCODE_APP_SWITCH is not sent by the Android O.S.
802
803 // BUTTON_1--KEYCODE_BUTTON_16 0x000000bc -- 0x000000cb
804
805 case 0x000000cc: // KEYCODE_LANGUAGE_SWITCH
806 case 0x000000cd: // KEYCODE_MANNER_MODE do we need such a thing?
807 case 0x000000ce: // KEYCODE_3D_MODE
808 case 0x000000cf: // KEYCODE_CONTACTS
809 return QKeyCombination::fromCombined(0);
810
811 case 0x000000d0: // KEYCODE_CALENDAR
812 return Qt::Key_Calendar;
813
814 case 0x000000d1: // KEYCODE_MUSIC
815 return Qt::Key_Music;
816
817 case 0x000000d2: // KEYCODE_CALCULATOR
818 return Qt::Key_Calculator;
819
820 // 0x000000d3 -- 0x000000da some japanese specific keys, someone who understand what is about should check !
821
822 // 0x000000db: // KEYCODE_ASSIST not delivered to applications.
823
824 case 0x000000dc: // KEYCODE_BRIGHTNESS_DOWN
825 return Qt::Key_KeyboardBrightnessDown;
826
827 case 0x000000dd: // KEYCODE_BRIGHTNESS_UP
828 return Qt::Key_KeyboardBrightnessUp;
829
830 case 0x000000de: // KEYCODE_MEDIA_AUDIO_TRACK
831 return Qt::Key_AudioCycleTrack;
832
833 default:
834 qWarning() << "Unhandled key code " << key << '!';
835 return QKeyCombination::fromCombined(0);
836 }
837 }
838
840 {
841 Qt::KeyboardModifiers qmodifiers;
842
843 if (modifiers & 0x00000001) // META_SHIFT_ON
844 qmodifiers |= Qt::ShiftModifier;
845
846 if (modifiers & 0x00000002) // META_ALT_ON
847 qmodifiers |= Qt::AltModifier;
848
849 if (modifiers & 0x00000004) // META_SYM_ON
850 qmodifiers |= Qt::MetaModifier;
851
852 if (modifiers & 0x00001000) // META_CTRL_ON
853 qmodifiers |= Qt::ControlModifier;
854
855 return qmodifiers;
856 }
857
858 // maps 0 to the empty string, and anything else to a single-character string
859 static inline QString toString(jint unicode)
860 {
861 return unicode ? QString(QChar(unicode)) : QString();
862 }
863
864 static void keyDown(JNIEnv */*env*/, jobject /*thiz*/, jint key, jint unicode, jint modifier, jboolean autoRepeat)
865 {
866 const int qtKeyCombination = mapAndroidKey(key).toCombined();
867 const auto qtModifiers = mapAndroidModifiers(modifier);
868 const auto isBackKey = qtKeyCombination == Qt::Key_Back;
869 const auto isMenuKey = qtKeyCombination == Qt::Key_Menu;
870 const auto text = toString(unicode);
871
872 bool accepted = false;
873 if (isBackKey || isMenuKey) {
874 accepted = QWindowSystemInterface::handleKeyEvent<
875 QWindowSystemInterface::SynchronousDelivery>(
876 nullptr, QEvent::KeyPress, qtKeyCombination, qtModifiers, text, autoRepeat);
877 } else {
878 QWindowSystemInterface::handleKeyEvent(
879 nullptr, QEvent::KeyPress, qtKeyCombination, qtModifiers, text, autoRepeat);
880 }
881 m_backKeyPressAccepted = isBackKey && accepted;
882 m_menuKeyPressAccepted = isMenuKey && accepted;
883 }
884
886 auto iface = qGuiApp->nativeInterface<QNativeInterface::QAndroidApplication>();
887 iface->runOnAndroidMainThread([iface]() {
888 if (!iface->isActivityContext())
889 return;
890 iface->context().callMethod<jboolean>("moveTaskToBack", true);
891 });
892 }
893
894 static void keyUp(JNIEnv */*env*/, jobject /*thiz*/, jint key, jint unicode, jint modifier, jboolean autoRepeat)
895 {
896 const int qtKeyCombination = mapAndroidKey(key).toCombined();
897 const auto qtModifiers = mapAndroidModifiers(modifier);
898 const auto isBackKey = qtKeyCombination == Qt::Key_Back;
899 const auto isMenuKey = qtKeyCombination == Qt::Key_Menu;
900 const auto text = toString(unicode);
901
902 if (!isBackKey && !isMenuKey) {
903 QWindowSystemInterface::handleKeyEvent(
904 nullptr, QEvent::KeyRelease, qtKeyCombination, qtModifiers, text, autoRepeat);
905 return;
906 }
907
908 const bool accepted =
909 QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
910 nullptr, QEvent::KeyRelease, qtKeyCombination, qtModifiers, text, autoRepeat);
911
912 if (isBackKey && !m_backKeyPressAccepted && !accepted)
914 else if (isMenuKey && !m_menuKeyPressAccepted && !accepted)
916 }
917
918 static void keyboardVisibilityChanged(JNIEnv */*env*/, jobject /*thiz*/, jboolean visibility)
919 {
920 if (!visibility)
921 m_softwareKeyboardRect = QRect();
922
924 if (inputContext && qGuiApp) {
925 inputContext->emitInputPanelVisibleChanged();
926 if (!visibility) {
927 inputContext->emitKeyboardRectChanged();
928 QMetaObject::invokeMethod(inputContext, "hideSelectionHandles", Qt::QueuedConnection);
929 }
930 }
931 qCDebug(lcQpaInputMethods) << "@@@ KEYBOARDVISIBILITYCHANGED" << inputContext;
932 }
933
934 static void keyboardGeometryChanged(JNIEnv */*env*/, jobject /*thiz*/, jint x, jint y, jint w, jint h)
935 {
936 QRect r = QRect(x, y, w, h);
937 if (r == m_softwareKeyboardRect)
938 return;
941 if (inputContext && qGuiApp)
942 inputContext->emitKeyboardRectChanged();
943
944 qCDebug(lcQpaInputMethods) << "@@@ KEYBOARDRECTCHANGED" << m_softwareKeyboardRect;
945 }
946
947 static void handleLocationChanged(JNIEnv */*env*/, jobject /*thiz*/, int id, int x, int y)
948 {
949 qCDebug(lcQpaInputMethods) << "@@@ handleLocationChanged" << id << x << y;
951 if (inputContext && qGuiApp)
952 QMetaObject::invokeMethod(inputContext, "handleLocationChanged", Qt::BlockingQueuedConnection,
953 Q_ARG(int, id), Q_ARG(int, x), Q_ARG(int, y));
954
955 }
956
957
958 static const JNINativeMethod methods[] = {
959 {"touchBegin","(I)V",(void*)touchBegin},
960 {"touchAdd","(IIIZIIFFFF)V",(void*)touchAdd},
961 {"touchEnd","(II)V",(void*)touchEnd},
962 {"touchCancel", "(I)V", (void *)touchCancel},
963 {"mouseDown", "(IIII)V", (void *)mouseDown},
964 {"mouseUp", "(IIII)V", (void *)mouseUp},
965 {"mouseMove", "(IIII)V", (void *)mouseMove},
966 {"mouseWheel", "(IIIFF)V", (void *)mouseWheel},
967 {"longPress", "(III)V", (void *)longPress},
968 {"isTabletEventSupported", "()Z", (void *)isTabletEventSupported},
969 {"tabletEvent", "(IIJIIIFFF)V", (void *)tabletEvent},
970 {"keyDown", "(IIIZ)V", (void *)keyDown},
971 {"keyUp", "(IIIZ)V", (void *)keyUp},
972 {"keyboardVisibilityChanged", "(Z)V", (void *)keyboardVisibilityChanged},
973 {"keyboardGeometryChanged", "(IIII)V", (void *)keyboardGeometryChanged},
974 {"handleLocationChanged", "(III)V", (void *)handleLocationChanged},
975 };
976
977 bool registerNatives(QJniEnvironment &env)
978 {
979 if (!env.registerNativeMethods(QtJniTypes::Traits<QtJniTypes::QtInputDelegate>::className(),
980 methods, sizeof(methods) / sizeof(methods[0]))) {
981 __android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed");
982 return false;
983 }
984
985 return true;
986 }
987}
988
989QT_END_NAMESPACE
static QAndroidInputContext * androidInputContext()
static QRect & defaultAvailableGeometry()
\inmodule QtCore\reentrant
Definition qpoint.h:30
Combined button and popup list for selecting options.
static QPointer< QWindow > m_mouseGrabber
static void touchCancel(JNIEnv *, jobject, jint winId)
static void mouseWheel(JNIEnv *, jobject, jint winId, jint x, jint y, jfloat hdelta, jfloat vdelta)
static bool isTabletEventSupported(JNIEnv *, jobject)
void updateSelection(int selStart, int selEnd, int candidatesStart, int candidatesEnd)
static void touchBegin(JNIEnv *, jobject, jint)
bool registerNatives(QJniEnvironment &env)
static QString toString(jint unicode)
static void sendMouseButtonEvents(QWindow *topLevel, QPoint localPos, QPoint globalPos, jint mouseButtonState, QEvent::Type type)
static bool m_ignoreMouseEvents
static QKeyCombination mapAndroidKey(int key)
static void mouseDown(JNIEnv *, jobject, jint winId, jint x, jint y, jint mouseButtonState)
static void moveActivityToBackground()
static void keyUp(JNIEnv *, jobject, jint key, jint unicode, jint modifier, jboolean autoRepeat)
void showSoftwareKeyboard(int left, int top, int width, int height, int inputHints, int enterKeyType)
static QPointingDevice * getTouchDevice()
static Qt::MouseButtons m_lastSeenButtons
static Qt::KeyboardModifiers mapAndroidModifiers(jint modifiers)
bool isSoftwareKeyboardVisible()
static void touchAdd(JNIEnv *, jobject, jint winId, jint id, jint action, jboolean, jint x, jint y, jfloat major, jfloat minor, jfloat rotation, jfloat pressure)
static void keyboardGeometryChanged(JNIEnv *, jobject, jint x, jint y, jint w, jint h)
static QRect m_softwareKeyboardRect
static void keyboardVisibilityChanged(JNIEnv *, jobject, jboolean visibility)
static void tabletEvent(JNIEnv *, jobject, jint winId, jint deviceId, jlong time, jint action, jint pointerType, jint buttonState, jfloat x, jfloat y, jfloat pressure)
static void keyDown(JNIEnv *, jobject, jint key, jint unicode, jint modifier, jboolean autoRepeat)
static void mouseMove(JNIEnv *, jobject, jint winId, jint x, jint y, jint mouseButtonState)
void updateHandles(int mode, QPoint editMenuPos, uint32_t editButtons, QPoint cursor, QPoint anchor, bool rtl)
static void handleLocationChanged(JNIEnv *, jobject, int id, int x, int y)
QRect softwareKeyboardRect()
static void longPress(JNIEnv *, jobject, jint winId, jint x, jint y)
static QList< QWindowSystemInterface::TouchPoint > m_touchPoints
static Qt::MouseButtons toMouseButtons(jint j_buttons)
static void mouseUp(JNIEnv *, jobject, jint winId, jint x, jint y, jint mouseButtonState)
static const JNINativeMethod methods[]
static bool m_menuKeyPressAccepted
static bool m_backKeyPressAccepted
static void touchEnd(JNIEnv *, jobject, jint winId, jint)
QBasicMutex * platformInterfaceMutex()
QAndroidPlatformIntegration * androidPlatformIntegration()
AndroidBackendRegister * backendRegister()
QWindow * windowFromId(int windowId)
Q_DECLARE_JNI_CLASS(MotionEvent, "android/view/MotionEvent")