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