<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Tablet events behavior with stylus button click]]></title><description><![CDATA[<p dir="auto">Hello,</p>
<h2>Problem ?</h2>
<p dir="auto">I am working on tablet input and I found out that there is an odd behavior with the tablet when using the stylus button (usually simulating a mouse button right click).  Basically, some <em>TabletRelease</em>/<em>MouseButtonRelease</em> seems to be missing and the information in <em>QEvent::button()</em> and <em>QEvent::buttons()</em> are inconsistent.</p>
<p dir="auto">I made a simple application tracking tablet/mouse events, keeping a list of pressed buttons. I place the stylus on the tablet and press the stylus button and below is info about one of the events I receive:</p>
<pre><code>Event: 151972
  event type: TabletPress
  watched class: QLabel
  device type: Stylus
  button: LeftButton
  buttons: RightButton
  m_keys: 
  m_buttons: LeftButton, RightButton
</code></pre>
<p dir="auto">The problem here is that we have a <em>TabletPress</em> event with the button <em>LeftButton</em> but with buttons <em>[RightButton]</em>, while in my private list <em>m_buttons</em> I have the accurate state of buttons pressed <em>[LeftButton, RightButton]</em>.</p>
<p dir="auto">Another problem is that later, in the same conditions as written above, I lift the stylus from the tablet: the event <em>MouseButtonRelease</em>/<em>TabletRelease</em> for the <em>LeftButton</em> will <strong>not be sent</strong>. The event's <em>buttons</em> will be correct (<em>[RightButton]</em>) while my private list <em>m_buttons</em> will not (<em>[LeftButton, RightButton]</em>) .</p>
<p dir="auto">I tried to toggle the attribute <em>Qt::AA_SynthesizeMouseForUnhandledTabletEvents</em> but it does not change anything.</p>
<p dir="auto">Is this the expected behavior or am I missing something ?</p>
<h2>How to reproduce the problem</h2>
<ul>
<li>Place the stylus on the tablet</li>
<li>Press the stylus button (simulating a mouse right click button)</li>
<li>Lift the stylus from the tablet (<strong>the event with inconsistent <em>button</em>/<em>buttons</em> is sent</strong>)</li>
<li>Release the stylus button (<strong>there is no <em>MouseButtonRelease</em>/<em>TabletRelease</em> event sent here</strong>)</li>
</ul>
<h2>Simple test application code</h2>
<p dir="auto">I also added some fix in my code to be able to get the corresponding buttons state of the events in my private list.</p>
<p dir="auto">mainwindow.h</p>
<pre><code>#pragma once

#include &lt;QMainWindow&gt;
#include &lt;QList&gt;
#include &lt;QObject&gt;
#include &lt;QEvent&gt;
#include &lt;QSinglePointEvent&gt;
#include &lt;QMetaEnum&gt;
#include &lt;QTextEdit&gt;
#include &lt;QPushButton&gt;
#include &lt;QLabel&gt;

class TabletButtonInputDebugger : public QObject
{
    Q_OBJECT

public:
    TabletButtonInputDebugger(QObject* parent = nullptr);

    bool useFix() const;
    void setUseFix(bool useFix);

    void setDebugOutputTextEdit(QTextEdit* textEdit);
    void setDebugDownButtons(QLabel* label);

protected:
    bool eventFilter(QObject* watched, QEvent* event) override;

private:
    void updateInput(
        const QObject&amp; watched,
        const QEvent&amp; ev);

    void onKeyPress(
        const QObject&amp; watched,
        const QKeyEvent&amp; ev);
    void onKeyRelease(
        const QObject&amp; watched,
        const QKeyEvent&amp; ev);

    void onButtonPress(
        const QObject&amp; watched,
        const QSinglePointEvent&amp; ev);
    void onButtonRelease(
        const QObject&amp; watched,
        const QSinglePointEvent&amp; ev);

    void onButtonDoubleClick(
        const QObject&amp; watched,
        const QSinglePointEvent&amp; ev);

    void debugInput(
        const QObject&amp; watched,
        const QInputEvent&amp; event);

    void appendDebugLine(const QString&amp; line);

    QList&lt;Qt::Key&gt; m_keys;
    QList&lt;Qt::MouseButton&gt; m_buttons;

    bool m_isStylusDown = false;
    bool m_isStylusDeviceUnstable = false;
    bool m_useFix = false;

    QTextEdit* m_debugOutputTextEdit = nullptr;
    QLabel* m_debugDownButtonsLabel = nullptr;
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);

private:
    TabletButtonInputDebugger m_tbid;
};

</code></pre>
<p dir="auto">main_window.cpp</p>
<pre><code>#include "mainwindow.h"

#include &lt;QSplitter&gt;
#include &lt;QVBoxLayout&gt;
#include &lt;QHBoxLayout&gt;
#include &lt;QCheckBox&gt;
#include &lt;QTimer&gt;

namespace
{

template &lt;typename Enum&gt;
const char* enumValueToKey(Enum val)
{
    const QMetaEnum metaEnum = QMetaEnum::fromType&lt;Enum&gt;();
    return metaEnum.valueToKey(val);
}

QString toString(QInputDevice::DeviceType deviceType)
{
    switch (deviceType)
    {
        default:
        case QInputDevice::DeviceType::Unknown:
            return "Unknown";
        case QInputDevice::DeviceType::Mouse: return "Mouse";
        case QInputDevice::DeviceType::TouchScreen: return "TouchScreen";
        case QInputDevice::DeviceType::TouchPad: return "TouchPad";
        case QInputDevice::DeviceType::Puck: return "Puck";
        case QInputDevice::DeviceType::Stylus: return "Stylus";
        case QInputDevice::DeviceType::Airbrush: return "Airbrush";
        case QInputDevice::DeviceType::Keyboard: return "Keyboard";
    }
}

QString toString(Qt::MouseButton button)
{
    if (button == Qt::LeftButton)
        return "LeftButton";
    if (button == Qt::RightButton)
        return "RightButton";
    if (button == Qt::MiddleButton)
        return "MiddleButton";
    if (button == Qt::BackButton)
        return "BackButton";
    if (button == Qt::ForwardButton)
        return "ForwardButton";
    if (button == Qt::TaskButton)
        return  "TaskButton";

    return "UnknownButton";
}

QString toString(Qt::MouseButtons buttons)
{
    QStringList names;

    if (buttons &amp; Qt::LeftButton)
        names &lt;&lt; "LeftButton";
    if (buttons &amp; Qt::RightButton)
        names &lt;&lt; "RightButton";
    if (buttons &amp; Qt::MiddleButton)
        names &lt;&lt; "MiddleButton";
    if (buttons &amp; Qt::BackButton)
        names &lt;&lt; "BackButton";
    if (buttons &amp; Qt::ForwardButton)
        names &lt;&lt; "ForwardButton";
    if (buttons &amp; Qt::TaskButton)
        names &lt;&lt; "TaskButton";
    if (names.isEmpty())
        names &lt;&lt; "NoButton";

    return names.join("|");
}

QString toString(Qt::Key key)
{
    return QString(enumValueToKey(key));
}

template &lt;typename T&gt;
QString join(const QList&lt;T&gt;&amp; list, const QString&amp; sep)
{
    QString res;

    for (const T&amp; e : list)
    {
        res += toString(e) + sep;
    }

    res.chop(sep.size());

    return res;
}

}

TabletButtonInputDebugger::TabletButtonInputDebugger(QObject* parent) :
    QObject(parent)
{

}

bool TabletButtonInputDebugger::useFix() const
{
    return m_useFix;
}

void TabletButtonInputDebugger::setUseFix(bool useFix)
{
    if (m_useFix == useFix)
    {
        return;
    }

    m_buttons.clear();
    m_useFix = useFix;
}

void TabletButtonInputDebugger::setDebugOutputTextEdit(QTextEdit* textEdit)
{
    m_debugOutputTextEdit = textEdit;
}

void TabletButtonInputDebugger::setDebugDownButtons(QLabel* label)
{
    m_debugDownButtonsLabel = label;
}

bool TabletButtonInputDebugger::eventFilter(QObject* watched, QEvent* event)
{
    updateInput(*watched, *event);
    return QObject::eventFilter(watched, event);
}

void TabletButtonInputDebugger::updateInput(
    const QObject&amp; watched,
    const QEvent&amp; ev)
{
    // Ignoring non-input events.
    if (!ev.isInputEvent())
    {
        return;
    }

    // Ignoring events on QWidgetWindow.
    if (!watched.isWidgetType())
    {
        return;
    }

    switch (ev.type())
    {
    case QEvent::KeyPress:
        onKeyPress(watched, static_cast&lt;const QKeyEvent&amp;&gt;(ev));
        break;
    case QEvent::KeyRelease:
        onKeyRelease(watched, static_cast&lt;const QKeyEvent&amp;&gt;(ev));
        break;
    case QEvent::MouseButtonPress:
    case QEvent::TabletPress:
        onButtonPress(watched, static_cast&lt;const QSinglePointEvent&amp;&gt;(ev));
        break;
    case QEvent::MouseButtonRelease:
    case QEvent::TabletRelease:
        onButtonRelease(watched, static_cast&lt;const QSinglePointEvent&amp;&gt;(ev));
        break;
    case QEvent::MouseButtonDblClick:
        onButtonDoubleClick(watched, static_cast&lt;const QSinglePointEvent&amp;&gt;(ev));
        break;
    case QEvent::MouseMove:
    case QEvent::TabletMove:
        break;
    default:
        // debugInput(watched, ev);
        break;
    }
}

void TabletButtonInputDebugger::onKeyPress(
    const QObject&amp; watched,
    const QKeyEvent&amp; ev)
{
    if (ev.isAutoRepeat()) { return; }

    const Qt::Key key = static_cast&lt;Qt::Key&gt;(ev.key());
    if (!m_keys.contains(key))
    {
        m_keys.push_back(key);
    }
}

void TabletButtonInputDebugger::onKeyRelease(
    const QObject&amp; watched,
    const QKeyEvent&amp; ev)
{
    if (ev.isAutoRepeat()) { return; }

    m_keys.removeAll(ev.key());
}

void TabletButtonInputDebugger::onButtonPress(
    const QObject&amp; watched,
    const QSinglePointEvent&amp; ev)
{
    if (m_useFix)
    {
        // If the stylus is down and we receive an event from another device,
        // we cannot track its down state anymore. It is unstable.
        if (m_isStylusDown &amp;&amp;
            ev.deviceType() != QInputDevice::DeviceType::Stylus)
        {
            m_isStylusDown = false;
            m_isStylusDeviceUnstable = true;
            m_buttons.clear();

            appendDebugLine(" -----------------");
            appendDebugLine("| Stylus unstable |");
            appendDebugLine(" -----------------");
            appendDebugLine("");
        }
        else
        {
            if (ev.deviceType() == QInputDevice::DeviceType::Stylus)
            {
                if (m_isStylusDeviceUnstable)
                {
                    // Ignoring those events.
                    return;
                }

                if (ev.button() == Qt::LeftButton)
                {
                    m_isStylusDown = true;
                }
            }
        }
    }

    if (!m_buttons.contains(ev.button()))
    {
        m_buttons.push_back(ev.button());
    }

    debugInput(watched, ev);
}

void TabletButtonInputDebugger::onButtonRelease(
    const QObject&amp; watched,
    const QSinglePointEvent&amp; ev)
{
    if (m_useFix)
    {
        if (m_isStylusDeviceUnstable)
        {
            // If Qt tells that is that there are no buttons pressed,
            // the stylus is considered stable again.
            if (ev.buttons() == Qt::NoButton)
            {
                appendDebugLine(" ---------------------");
                appendDebugLine("| Stylus stable again |");
                appendDebugLine(" ---------------------");
                appendDebugLine("");

                m_isStylusDeviceUnstable = false;
                m_buttons.clear();
            }
        }
        else
        {
            if (ev.deviceType() == QInputDevice::DeviceType::Stylus &amp;&amp;
                ev.button() == Qt::LeftButton)
            {
                m_isStylusDown = false;
            }
        }
    }

    m_buttons.removeAll(ev.button());

    debugInput(watched, ev);
}

void TabletButtonInputDebugger::onButtonDoubleClick(
    const QObject&amp; watched,
    const QSinglePointEvent&amp; ev)
{
    debugInput(watched, ev);
}

void TabletButtonInputDebugger::debugInput(
    const QObject&amp; watched,
    const QInputEvent&amp; event)
{
    const auto eventTypeStr = enumValueToKey(event.type());
    const auto watchedClassName = watched.metaObject()-&gt;className();
    const auto deviceTypeStr = toString(event.deviceType());
    const auto m_keysStr = join(m_keys, ", ");
    const auto m_buttonsStr = join(m_buttons, ", ");

    appendDebugLine(QString() + "Event: " + QString::number(event.timestamp()));
    appendDebugLine(QString() + "  event type: " + eventTypeStr);
    appendDebugLine(QString() + "  watched class: " + watchedClassName);
    appendDebugLine(QString() + "  device type: " + deviceTypeStr);

    if (event.type() == QEvent::KeyPress ||
        event.type() == QEvent::KeyRelease)
    {
        const auto&amp; ke = static_cast&lt;const QKeyEvent&amp;&gt;(event);
        appendDebugLine(QString() + "  key: " + toString(static_cast&lt;Qt::Key&gt;(ke.key())));
    }
    if (event.isSinglePointEvent())
    {
        const auto&amp; spe = static_cast&lt;const QSinglePointEvent&amp;&gt;(event);
        appendDebugLine(QString() + "  button: " + toString(spe.button()));
        appendDebugLine(QString() + "  buttons: " + toString(spe.buttons()));
    }

    appendDebugLine(QString() + "  m_keys: " + m_keysStr);
    appendDebugLine(QString() + "  m_buttons: " + m_buttonsStr);

    if (m_debugDownButtonsLabel)
    {
        m_debugDownButtonsLabel-&gt;setText(m_buttonsStr);
    }

    appendDebugLine("");
}

void TabletButtonInputDebugger::appendDebugLine(const QString&amp; line)
{
    qDebug() &lt;&lt; line;

    if (m_debugOutputTextEdit)
    {
        m_debugOutputTextEdit-&gt;append(line);
    }
}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    auto centralSplitterWidget = new QSplitter(this);
    setCentralWidget(centralSplitterWidget);
    centralSplitterWidget-&gt;setHandleWidth(20);
    centralSplitterWidget-&gt;setChildrenCollapsible(false);

    auto testAreaWidget = new QLabel(centralSplitterWidget);
    centralSplitterWidget-&gt;addWidget(testAreaWidget);
    testAreaWidget-&gt;setText("Test area");
    testAreaWidget-&gt;setAlignment(Qt::AlignCenter);
    testAreaWidget-&gt;setMouseTracking(true);
    testAreaWidget-&gt;setTabletTracking(true);

    auto middlePaneWidget = new QWidget(centralSplitterWidget);
    centralSplitterWidget-&gt;addWidget(middlePaneWidget);
    auto middlePaneLayout = new QVBoxLayout(middlePaneWidget);

    auto outputTextEdit = new QTextEdit(middlePaneWidget);
    middlePaneLayout-&gt;addWidget(outputTextEdit, 1);
    outputTextEdit-&gt;setReadOnly(true);

    auto clearOutputTextEditButton = new QPushButton(middlePaneWidget);
    middlePaneLayout-&gt;addWidget(clearOutputTextEditButton);
    clearOutputTextEditButton-&gt;setText("Clear");
    connect(
        clearOutputTextEditButton, &amp;QPushButton::clicked,
        this, [outputTextEdit]() { outputTextEdit-&gt;clear(); });

    auto rightPaneWidget = new QWidget(centralSplitterWidget);
    centralSplitterWidget-&gt;addWidget(rightPaneWidget);
    auto rightPaneLayout = new QVBoxLayout(rightPaneWidget);
    rightPaneLayout-&gt;setAlignment(Qt::AlignTop);
    rightPaneLayout-&gt;addSpacerItem(new QSpacerItem(-1, -1));

    auto downButtonsLayout = new QHBoxLayout();
    rightPaneLayout-&gt;addLayout(downButtonsLayout);

    downButtonsLayout-&gt;addWidget(new QLabel("Buttons down: ", rightPaneWidget));

    auto downButtonsLabel = new QLabel(rightPaneWidget);
    downButtonsLayout-&gt;addWidget(downButtonsLabel, 1);

    auto toggleFixCheckBox = new QCheckBox(rightPaneWidget);
    rightPaneLayout-&gt;addWidget(toggleFixCheckBox);
    toggleFixCheckBox-&gt;setText("Use fix");
    toggleFixCheckBox-&gt;setChecked(m_tbid.useFix());
    connect(
        toggleFixCheckBox, &amp;QCheckBox::toggled,
        this, [this](bool state) { m_tbid.setUseFix(state); });

    auto synthesizeMouseForUnhandledTabletEventsButton = new QCheckBox(rightPaneWidget);
    rightPaneLayout-&gt;addWidget(synthesizeMouseForUnhandledTabletEventsButton);
    synthesizeMouseForUnhandledTabletEventsButton-&gt;setText("AA_SynthesizeMouseForUnhandledTabletEventsButton");
    synthesizeMouseForUnhandledTabletEventsButton-&gt;setChecked(qApp-&gt;testAttribute(Qt::AA_SynthesizeMouseForUnhandledTabletEvents));
    connect(
        synthesizeMouseForUnhandledTabletEventsButton, &amp;QCheckBox::toggled,
        this, [](bool state) { qApp-&gt;setAttribute(Qt::AA_SynthesizeMouseForUnhandledTabletEvents, state); });

    // - Tablet debugger -

    testAreaWidget-&gt;installEventFilter(&amp;m_tbid);
    // Also tested with the application, same problem.
    // qApp-&gt;installEventFilter(&amp;m_tbid);
    m_tbid.setDebugOutputTextEdit(outputTextEdit);
    m_tbid.setDebugDownButtons(downButtonsLabel);
}

</code></pre>
<p dir="auto">main.cpp</p>
<pre><code>#include "mainwindow.h"

#include &lt;QApplication&gt;

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    MainWindow w;
    w.show();

    return app.exec();
}

</code></pre>
<h2>Info</h2>
<p dir="auto">OS: Windows 11<br />
Qt: 6.9.1 (I also tested with Qt 6.7.2.)</p>
]]></description><link>https://forum.qt.io/topic/163555/tablet-events-behavior-with-stylus-button-click</link><generator>RSS for Node</generator><lastBuildDate>Tue, 21 Apr 2026 09:35:45 GMT</lastBuildDate><atom:link href="https://forum.qt.io/topic/163555.rss" rel="self" type="application/rss+xml"/><pubDate>Tue, 21 Oct 2025 02:55:19 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Tablet events behavior with stylus button click on Fri, 07 Nov 2025 04:48:25 GMT]]></title><description><![CDATA[<p dir="auto">I filed it as a bug <a href="https://bugreports.qt.io/browse/QTBUG-141746" target="_blank" rel="noopener noreferrer nofollow ugc">here</a>.</p>
]]></description><link>https://forum.qt.io/post/833393</link><guid isPermaLink="true">https://forum.qt.io/post/833393</guid><dc:creator><![CDATA[qt-public-name]]></dc:creator><pubDate>Fri, 07 Nov 2025 04:48:25 GMT</pubDate></item><item><title><![CDATA[Reply to Tablet events behavior with stylus button click on Fri, 24 Oct 2025 01:55:40 GMT]]></title><description><![CDATA[<p dir="auto">I cannot edit my previous message (considered being a spam). I add corrections and additional info below:</p>
<h1>How to reproduce the problem</h1>
<ul>
<li>Place the stylus on the tablet</li>
<li>Press the stylus button (simulating a mouse right click button) <strong>(the event with inconsistent button/buttons is sent)</strong></li>
<li>Lift the stylus from the tablet <strong>(there is no MouseButtonRelease/TabletRelease event sent here)</strong></li>
<li>Release the stylus button</li>
</ul>
<h1>Info</h1>
<p dir="auto">OS: Windows 11<br />
Qt: 6.9.1 (I also tested with Qt 6.7.2.)<br />
Tablet: Gaomon M6</p>
]]></description><link>https://forum.qt.io/post/833086</link><guid isPermaLink="true">https://forum.qt.io/post/833086</guid><dc:creator><![CDATA[qt-public-name]]></dc:creator><pubDate>Fri, 24 Oct 2025 01:55:40 GMT</pubDate></item></channel></rss>