QEvent::TabletMove not firing on Android when pen moves quickly after touch
-
Hi, I am learning Qt to develop a note-taking app specifically for Android tablets that support pen input (e.g., Wacom MovinkPad, XP-Pen Magic Drawing Pad, Galaxy Tab). However, I’ve encountered an issue where pen events are not behaving as expected on Android.
Issue:
When I build the app for Windows (using Wacom Cintiq 24 + Pro Pen 3), the pen works perfectly.
However, on Android (Wacom Movink 11 + Pro Pen 3), if I move the pen too quickly immediately after it touches the screen, the QEvent::TabletMove event stops firing entirely.It seems like the initial quick movement prevents the tablet events from being recognized correctly by the application.
Is there any additional configuration or specific handling required for android to ensure stable QTabletEvent delivery?
any advice would be greatly appreciated!Example:
#include <QApplication> #include <QWidget> #include <QPainter> #include <QMouseEvent> #include <QTabletEvent> #include <QPixmap> #include <QDebug> static int counter = 0; class CanvasWidget : public QWidget { public: CanvasWidget(QWidget *parent = nullptr) : QWidget(parent) { canvas = QPixmap(this->width(), this->height()); canvas.fill(Qt::white); } protected: bool event(QEvent *event) override{ qDebug() << counter << ": "<< event->type(); counter++; return QWidget::event(event); } void paintEvent(QPaintEvent *event) override { QPainter painter(this); painter.drawPixmap(0, 0, canvas); painter.setPen(Qt::blue); painter.setFont(QFont("Monospace", 12)); QRect debugRect(0, 0, 200, 200); painter.drawText(debugRect, Qt::AlignLeft | Qt::AlignTop, debugText); } void tabletEvent(QTabletEvent *event) override { updateDebugString(event); switch (event->type()) { case QEvent::TabletPress: isDrawing = true; lastPoint = event->position(); break; case QEvent::TabletMove: if (isDrawing) { drawOnCanvas(event); lastPoint = event->position(); } break; case QEvent::TabletRelease: isDrawing = false; break; default: break; } event->accept(); update(); } private: QPixmap canvas; QPointF lastPoint; bool isDrawing = false; QString debugText = "Wait for Pen Input..."; void drawOnCanvas(QTabletEvent *event) { QPainter painter(&canvas); qreal width = event->pressure() * 10; QPen pen(Qt::black, width); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(pen); painter.drawLine(lastPoint, event->position()); lastPoint = event->position(); } void updateDebugString(QTabletEvent *event) { debugText = QString("Coords: %1, %2\nPressure: %3") .arg(event->position().x(), 0, 'f', 1) .arg(event->position().y(), 0, 'f', 1) .arg(event->pressure(), 0, 'f', 3); } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); CanvasWidget widget; widget.show(); return app.exec(); }Environment:
- Qt Version: 6.10.1
- device: Wacom MovinkPad 11 (DTHA116)
- pen: Wacom Pro Pen 3
- CPU architecture: arm64-v8a, armeabi-v7a, arneabi
- OS version: Android 14.0, SDK 34
-
This is a recording of an actual pen drawing.
https://youtube.com/shorts/S_pJpRJU_LM?feature=share -
Here is the application output when tracing the screen twice with the pen:
10:01:25: Android target "org.qtproject.example.MobileTablet" terminated. I/le.MobileTablet: Late-enabling -Xcheck:jni I/le.MobileTablet: Using CollectorTypeCMC GC. D/nativeloader: Load libframework-connectivity-tiramisu-jni.so using APEX ns com_android_tethering for caller /apex/com.android.tethering/javalib/framework-connectivity-t.jar: ok D/CompatibilityChangeReporter: Compat change id reported: 171979766; UID 10306; state: ENABLED D/CompatibilityChangeReporter: Compat change id reported: 242716250; UID 10306; state: ENABLED D/nativeloader: Configuring clns-6 for other apk /data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk. target_sdk_version=36, uses_libraries=, library_path=/data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/lib/arm64:/data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk!/lib/arm64-v8a, permitted_path=/data:/mnt/expand:/data/user/0/org.qtproject.example.MobileTablet D/nativeloader: Load libframework-connectivity-jni.so using APEX ns com_android_tethering for caller /apex/com.android.tethering/javalib/framework-connectivity.jar: ok V/GraphicsEnvironment: Currently set values for: V/GraphicsEnvironment: angle_gl_driver_selection_pkgs=[] V/GraphicsEnvironment: angle_gl_driver_selection_values=[] V/GraphicsEnvironment: ANGLE GameManagerService for org.qtproject.example.MobileTablet: false V/GraphicsEnvironment: org.qtproject.example.MobileTablet is not listed in per-application setting V/GraphicsEnvironment: App is not on the allowlist for updatable production driver. D/CompatibilityChangeReporter: Compat change id reported: 183155436; UID 10306; state: ENABLED D/MbrainDebugManagerImpl: getService failed D/libMEOW : meow new tls: 0xb400006efb5ff0b0 D/libMEOW : meow reload base cfg path: na D/libMEOW : meow reload overlay cfg path: na W/QT : qt_process_init() called E/QT : [QT]file does not exist W/QT : Support!! D/libMEOW : applied 0 plugin for [org.qtproject.example.MobileTablet]. D/libMEOW : rebuild call chain: 0xb400006efb5cf080 D/libMEOW : meow delete tls: 0xb400006efb5ff0b0 D/CompatibilityChangeReporter: Compat change id reported: 247079863; UID 10306; state: ENABLED W/le.MobileTablet: ClassLoaderContext parent mismatch. (PCL[] | PCL[];PCL[/data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk*3949881819]) D/nativeloader: Load /data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk!/lib/arm64-v8a/libc++_shared.so using class loader ns clns-6 (caller=/data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk): ok D/nativeloader: Load /data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk!/lib/arm64-v8a/libQt6Core_arm64-v8a.so using class loader ns clns-6 (caller=/data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk): ok I/QtCore : Start D/nativeloader: Load /data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk!/lib/arm64-v8a/libQt6Gui_arm64-v8a.so using class loader ns clns-6 (caller=/data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk): ok D/nativeloader: Load /data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk!/lib/arm64-v8a/libQt6Widgets_arm64-v8a.so using class loader ns clns-6 (caller=/data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk): ok D/nativeloader: Load /data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk!/lib/arm64-v8a/libQt6Svg_arm64-v8a.so using class loader ns clns-6 (caller=/data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk): ok D/nativeloader: Load /data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk!/lib/arm64-v8a/libQt6Network_arm64-v8a.so using class loader ns clns-6 (caller=/data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk): ok D/nativeloader: Load /data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk!/lib/arm64-v8a/libQt6OpenGL_arm64-v8a.so using class loader ns clns-6 (caller=/data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk): ok D/nativeloader: Load /data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk!/lib/arm64-v8a/libQt6Qml_arm64-v8a.so using class loader ns clns-6 (caller=/data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk): ok D/nativeloader: Load /data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk!/lib/arm64-v8a/libQt6QmlModels_arm64-v8a.so using class loader ns clns-6 (caller=/data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk): ok D/nativeloader: Load /data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk!/lib/arm64-v8a/libQt6QmlWorkerScript_arm64-v8a.so using class loader ns clns-6 (caller=/data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk): ok D/nativeloader: Load /data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk!/lib/arm64-v8a/libQt6QmlMeta_arm64-v8a.so using class loader ns clns-6 (caller=/data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk): ok D/nativeloader: Load /data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk!/lib/arm64-v8a/libQt6Quick_arm64-v8a.so using class loader ns clns-6 (caller=/data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk): ok D/nativeloader: Load /data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk!/lib/arm64-v8a/libQt6VirtualKeyboard_arm64-v8a.so using class loader ns clns-6 (caller=/data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk): ok D/nativeloader: Load /data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk!/lib/arm64-v8a/libplugins_platforms_qtforandroid_arm64-v8a.so using class loader ns clns-6 (caller=/data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk): ok I/Qt : Qt platform plugin started D/nativeloader: Load /data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk!/lib/arm64-v8a/libMobileTablet_arm64-v8a.so using class loader ns clns-6 (caller=/data/app/~~GOZeW_WaqPAmQ16ve5Oomw==/org.qtproject.example.MobileTablet-YoP76MFWqX6LEnzPk2LCzQ==/base.apk): ok I/M-ProMotion: M-ProMotion is disabled D/CompatibilityChangeReporter: Compat change id reported: 210923482; UID 10306; state: ENABLED I/SurfaceFactory: [static] sSurfaceFactory = com.mediatek.view.impl.SurfaceFactoryImpl@f0a7a7c D/CompatibilityChangeReporter: Compat change id reported: 237531167; UID 10306; state: DISABLED W/OpenGLRenderer: Unknown dataspace 0 D/VRI[QtActivity]: hardware acceleration = true, forceHwAccelerated = false D/nativeloader: Load libpowerhalwrap_jni.so using system ns (caller=/system_ext/framework/mediatek-framework.jar): ok I/PowerHalWrapper: PowerHalWrapper.getInstance D/libMEOW : meow new tls: 0xb400006efb5cf080 D/libMEOW : applied 0 plugin for [org.qtproject.example.MobileTablet]. D/libMEOW : rebuild call chain: 0xb400006efb5df090 D/InputTransport: Create ARC handle: 0xb400006f9b59f1e0 D/InputEventReceiver: Input log is disabled in InputEventReceiver. D/InputTransport: Input log is disabled in InputChannel. D/BufferQueueConsumer: [](id:58d000000000,api:0,p:-1,c:22736) connect: controlledByApp=false E/FBI : Can't load library: dlopen failed: library "libmagtsync.so" not found D/libMEOW : meow new tls: 0xb400006efb5ef0a0 D/libMEOW : applied 0 plugin for [org.qtproject.example.MobileTablet]. D/libMEOW : rebuild call chain: 0xb400006efb63f0f0 E/OpenGLRenderer: Unable to match the desired swap behavior. D/libMEOW : meow new tls: 0xb400006efb61f0d0 D/libMEOW : applied 0 plugin for [org.qtproject.example.MobileTablet]. D/libMEOW : rebuild call chain: 0xb400006efb64f100 D/BLASTBufferQueue: [VRI[QtActivity]#0](f:0,a:1) acquireNextBufferLocked size=2200x1440 mFrameNumber=1 applyTransaction=true mTimestamp=369794844350688(auto) mPendingTransactions.size=0 graphicBufferId=97650376441857 transform=7 D/default : 0 : QEvent::Polish D/default : 1 : QEvent::PlatformSurface D/default : 2 : QEvent::WinIdChange D/default : 3 : QEvent::WindowIconChange D/default : 4 : QEvent::WindowStateChange D/default : 5 : QEvent::Move D/default : 6 : QEvent::Resize D/default : 7 : QEvent::Show D/default : 8 : QEvent::Resize D/default : 9 : QEvent::Paint Art: Cleared App Profiles. D/BufferQueueConsumer: [](id:58d000000001,api:0,p:-1,c:22736) connect: controlledByApp=false D/BLASTBufferQueue: [SurfaceView[org.qtproject.example.MobileTablet/org.qtproject.qt.android.bindings.QtActivity]#1](f:0,a:1) acquireNextBufferLocked size=2200x1321 mFrameNumber=1 applyTransaction=true mTimestamp=369795041337150(auto) mPendingTransactions.size=0 graphicBufferId=97650376441866 transform=7 D/default : 10 : QEvent::WindowActivate D/default : 11 : QEvent::ActivationChange D/default : 12 : QEvent::InputMethodQuery D/default : 13 : QEvent::InputMethodQuery D/default : 14 : QEvent::InputMethodQuery D/default : 15 : QEvent::InputMethodQuery I/ImeTracker: org.qtproject.example.MobileTablet:c71a8ff8: onRequestHide at ORIGIN_CLIENT_HIDE_SOFT_INPUT reason HIDE_SOFT_INPUT_BY_INSETS_API I/ImeTracker: org.qtproject.example.MobileTablet:c71a8ff8: onCancelled at PHASE_CLIENT_APPLY_ANIMATION D/default : 16 : QEvent::ShowToParent D/default : 17 : QEvent::PolishRequest D/default : 18 : QEvent::UpdateLater D/default : 19 : QEvent::UpdateRequest D/default : 20 : QEvent::Paint Art: Compiled App Profiles. I/le.MobileTablet: Compiler allocated 6094KB to compile void android.view.ViewRootImpl.performTraversals() D/ProfileInstaller: Installing profile for org.qtproject.example.MobileTablet W/default : QObject: Cannot create children for a parent that is in a different thread. W/default : (Parent is QApplication(0x6e8347b6e0), parent's thread is QThread(0xb400006fdb56af30), current thread is QThread(0xb400006fdb582e90) W/default : QObject: Cannot create children for a parent that is in a different thread. W/default : (Parent is QApplication(0x6e8347b6e0), parent's thread is QThread(0xb400006fdb56af30), current thread is QThread(0xb400006fdb582e90) D/default : 21 : QEvent::TabletPress D/default : 22 : QEvent::UpdateRequest D/default : 23 : QEvent::Paint D/default : 24 : QEvent::TabletMove D/default : 25 : QEvent::InputMethodQuery D/default : 26 : QEvent::InputMethodQuery D/default : 27 : QEvent::InputMethodQuery D/default : 28 : QEvent::InputMethodQuery D/default : 29 : QEvent::UpdateRequest D/default : 30 : QEvent::Paint D/default : 31 : QEvent::InputMethodQuery D/default : 32 : QEvent::InputMethodQuery D/default : 33 : QEvent::InputMethodQuery D/default : 34 : QEvent::InputMethodQuery D/default : 35 : QEvent::InputMethodQuery D/default : 36 : QEvent::InputMethodQuery D/default : 37 : QEvent::InputMethodQuery D/default : 38 : QEvent::TabletRelease D/default : 39 : QEvent::UpdateRequest I/ScrollIdentify: on fling D/default : 40 : QEvent::Paint D/default : 41 : QEvent::ToolTip W/le.MobileTablet: userfaultfd: MOVE ioctl seems unsupported: Connection timed out D/default : 42 : QEvent::TabletPress D/default : 43 : QEvent::UpdateRequest D/default : 44 : QEvent::Paint D/default : 45 : QEvent::TabletMove D/default : 46 : QEvent::UpdateRequest D/default : 47 : QEvent::Paint D/default : 48 : QEvent::InputMethodQuery D/default : 49 : QEvent::InputMethodQuery D/default : 50 : QEvent::InputMethodQuery D/default : 51 : QEvent::InputMethodQuery D/default : 52 : QEvent::InputMethodQuery D/default : 53 : QEvent::InputMethodQuery D/default : 54 : QEvent::InputMethodQuery D/default : 55 : QEvent::TabletRelease D/default : 56 : QEvent::UpdateRequest I/ScrollIdentify: on fling D/default : 57 : QEvent::Paint D/default : 58 : QEvent::ToolTip