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