Skip to content

Commit

Permalink
Ignore spontaneous tablet events in canvas instead
Browse files Browse the repository at this point in the history
Rather than doing it with an event filter, because that apparently
doesn't work properly and still lets spontaneous events through. They're
now ignored in the canvas controller/canvas view instead.
  • Loading branch information
askmeaboutlo0m committed Nov 24, 2024
1 parent 6e84573 commit 9ed28b7
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 71 deletions.
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Unreleased Version 2.2.2-pre
* Fix: Don't carry over HUD button presses to dock UI in small screen mode. Thanks Meru for reporting.
* Feature: Make list action buttons in settings dialog clearer, adding an edit button for canvas shortcuts. Thanks Maffi for suggesting.
* Fix: Work around broken transparency when copying images between canvases on Wayland. Thanks Absolute Goober for reporting.
* Fix: Properly ignore system tablet events when using KisTablet drivers on Windows. Thanks Doc for reporting.

2024-11-06 Version 2.2.2-beta.4
* Fix: Solve rendering glitches with selection outlines that happen on some systems. Thanks xxxx for reporting.
Expand Down
1 change: 1 addition & 0 deletions src/desktop/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ target_sources(drawpile PRIVATE
utils/qtguicompat.h
utils/recents.cpp
utils/recents.h
utils/tabletfilter.h
utils/touchhandler.cpp
utils/touchhandler.h
utils/widgetutils.cpp
Expand Down
1 change: 1 addition & 0 deletions src/desktop/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ class DrawpileApp final : public QApplication {
void setDockTitleBarsHidden(bool hidden);
void focusCanvas();
void shortcutsChanged();
void tabletDriverChanged();

protected:
bool event(QEvent *e) override;
Expand Down
50 changes: 38 additions & 12 deletions src/desktop/scene/canvasview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,12 @@ CanvasView::CanvasView(QWidget *parent)
setAcceptDrops(true);
setFrameShape(QFrame::NoFrame);

auto &settings = dpApp().settings();
DrawpileApp &app = dpApp();
connect(
&app, &DrawpileApp::tabletDriverChanged, this,
&CanvasView::resetTabletFilter, Qt::QueuedConnection);

desktop::settings::Settings &settings = app.settings();
settings.bindCanvasViewBackgroundColor(this, [this](QColor color) {
color.setAlpha(255);
setBackgroundBrush(color);
Expand Down Expand Up @@ -1024,6 +1029,8 @@ void CanvasView::enterEvent(compat::EnterEvent *event)
oldfocus->inherits("QPlainTextEdit"))) {
setFocus(Qt::MouseFocusReason);
}

m_tabletFilter.reset();
}

void CanvasView::leaveEvent(QEvent *event)
Expand All @@ -1037,6 +1044,7 @@ void CanvasView::leaveEvent(QEvent *event)
m_scene->removeHover();
updateOutline();
resetCursor();
m_tabletFilter.reset();
}

void CanvasView::focusInEvent(QFocusEvent *event)
Expand Down Expand Up @@ -1211,6 +1219,11 @@ void CanvasView::startTabletEventTimer()
}
}

void CanvasView::resetTabletFilter()
{
m_tabletFilter.reset();
}

void CanvasView::penPressEvent(
QEvent *event, long long timeMsec, const QPointF &pos, qreal pressure,
qreal xtilt, qreal ytilt, qreal rotation, Qt::MouseButton button,
Expand Down Expand Up @@ -2006,16 +2019,20 @@ bool CanvasView::viewportEvent(QEvent *event)
QTabletEvent *tabev = static_cast<QTabletEvent *>(event);
const auto tabPos = compat::tabPosF(*tabev);
Qt::KeyboardModifiers modifiers = getTabletModifiers(tabev);
bool ignore = m_tabletFilter.shouldIgnore(tabev);
DP_EVENT_LOG(
"tablet_press spontaneous=%d x=%f y=%f pressure=%f xtilt=%d "
"ytilt=%d rotation=%f buttons=0x%x modifiers=0x%x pendown=%d "
"touching=%d effectivemodifiers=0x%u",
tabev->spontaneous(), tabPos.x(), tabPos.y(), tabev->pressure(),
compat::cast_6<int>(tabev->xTilt()),
"touching=%d effectivemodifiers=0x%u ignore=%d",
int(tabev->spontaneous()), tabPos.x(), tabPos.y(),
tabev->pressure(), compat::cast_6<int>(tabev->xTilt()),
compat::cast_6<int>(tabev->yTilt()),
qDegreesToRadians(tabev->rotation()), unsigned(tabev->buttons()),
unsigned(tabev->modifiers()), m_pendown, m_touch->isTouching(),
unsigned(modifiers));
unsigned(modifiers), int(ignore));
if(ignore) {
return true;
}

Qt::MouseButton button;
bool eraserOverride;
Expand Down Expand Up @@ -2057,16 +2074,20 @@ bool CanvasView::viewportEvent(QEvent *event)
const auto tabPos = compat::tabPosF(*tabev);
Qt::KeyboardModifiers modifiers = getTabletModifiers(tabev);
Qt::MouseButtons buttons = tabev->buttons();
bool ignore = m_tabletFilter.shouldIgnore(tabev);
DP_EVENT_LOG(
"tablet_move spontaneous=%d x=%f y=%f pressure=%f xtilt=%d "
"ytilt=%d rotation=%f buttons=0x%x modifiers=0x%x pendown=%d "
"touching=%d effectivemodifiers=0x%u",
tabev->spontaneous(), tabPos.x(), tabPos.y(), tabev->pressure(),
compat::cast_6<int>(tabev->xTilt()),
"touching=%d effectivemodifiers=0x%u ignore=%d",
int(tabev->spontaneous()), tabPos.x(), tabPos.y(),
tabev->pressure(), compat::cast_6<int>(tabev->xTilt()),
compat::cast_6<int>(tabev->yTilt()),
qDegreesToRadians(tabev->rotation()), unsigned(buttons),
unsigned(tabev->modifiers()), m_pendown, m_touch->isTouching(),
unsigned(modifiers));
unsigned(modifiers), int(ignore));
if(ignore) {
return true;
}

if(!tabletinput::passPenEvents()) {
tabev->accept();
Expand All @@ -2091,12 +2112,17 @@ bool CanvasView::viewportEvent(QEvent *event)
QTabletEvent *tabev = static_cast<QTabletEvent *>(event);
const auto tabPos = compat::tabPosF(*tabev);
Qt::KeyboardModifiers modifiers = getTabletModifiers(tabev);
bool ignore = m_tabletFilter.shouldIgnore(tabev);
DP_EVENT_LOG(
"tablet_release spontaneous=%d x=%f y=%f buttons=0x%x pendown=%d "
"touching=%d effectivemodifiers=0x%u",
tabev->spontaneous(), tabPos.x(), tabPos.y(),
"touching=%d effectivemodifiers=0x%u ignore=%d",
int(tabev->spontaneous()), tabPos.x(), tabPos.y(),
unsigned(tabev->buttons()), m_pendown, m_touch->isTouching(),
unsigned(modifiers));
unsigned(modifiers), int(ignore));
if(ignore) {
return true;
}

updateCursorPos(tabPos.toPoint());
if(!tabletinput::passPenEvents()) {
tabev->accept();
Expand Down
3 changes: 3 additions & 0 deletions src/desktop/scene/canvasview.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#ifndef DESKTOP_SCENE_CANVASVIEW
#define DESKTOP_SCENE_CANVASVIEW
#include "desktop/utils/qtguicompat.h"
#include "desktop/utils/tabletfilter.h"
#include "desktop/view/lock.h"
#include "libclient/canvas/canvasshortcuts.h"
#include "libclient/canvas/point.h"
Expand Down Expand Up @@ -274,6 +275,7 @@ private slots:
class SetDragParams;

void startTabletEventTimer();
void resetTabletFilter();

// unified mouse/stylus event handlers
void penPressEvent(
Expand Down Expand Up @@ -457,6 +459,7 @@ private slots:
#ifdef Q_OS_LINUX
bool m_waylandWorkarounds;
#endif
TabletFilter m_tabletFilter;
};

}
Expand Down
58 changes: 9 additions & 49 deletions src/desktop/tabletinput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,45 +20,12 @@ static Mode currentMode = Mode::Uninitialized;

#ifdef Q_OS_WIN

class SpontaneousTabletEventFilter final : public QObject {
public:
explicit SpontaneousTabletEventFilter(QObject *parent)
: QObject{parent}
{
}

protected:
bool eventFilter(QObject *watched, QEvent *event) override final
{
switch(event->type()) {
case QEvent::TabletEnterProximity:
case QEvent::TabletLeaveProximity:
case QEvent::TabletMove:
case QEvent::TabletPress:
case QEvent::TabletRelease:
// KisTablet uses QApplication::sendEvent, which means its events
// are never set to be spontaneous. We use that to filter out Qt's
// own tablet events, which in turn are always spontaneous.
if(event->spontaneous()) {
return false;
}
[[fallthrough]];
default:
return QObject::eventFilter(watched, event);
}
}
};

static KisTabletSupportWin8 *kisTabletSupportWin8;
static SpontaneousTabletEventFilter *spontaneousTabletEventFilter;
static bool shouldPassPenEvents = true;

static void resetKisTablet(DrawpileApp &app)
{
if(spontaneousTabletEventFilter) {
app.removeEventFilter(spontaneousTabletEventFilter);
delete spontaneousTabletEventFilter;
spontaneousTabletEventFilter = nullptr;
}
shouldPassPenEvents = true;
if(kisTabletSupportWin8) {
app.removeNativeEventFilter(kisTabletSupportWin8);
delete kisTabletSupportWin8;
Expand All @@ -67,17 +34,11 @@ static void resetKisTablet(DrawpileApp &app)
KisTabletSupportWin::quit();
}

static void installSpontaneousTabletEventFilter(DrawpileApp &app)
{
spontaneousTabletEventFilter = new SpontaneousTabletEventFilter{&app};
app.installEventFilter(spontaneousTabletEventFilter);
}

static void enableKisTabletWinink(DrawpileApp &app)
{
kisTabletSupportWin8 = new KisTabletSupportWin8;
if(kisTabletSupportWin8->init()) {
installSpontaneousTabletEventFilter(app);
shouldPassPenEvents = false;
app.installNativeEventFilter(kisTabletSupportWin8);
currentMode = Mode::KisTabletWinink;
} else {
Expand All @@ -87,10 +48,10 @@ static void enableKisTabletWinink(DrawpileApp &app)
}
}

static void enableKisTabletWintab(DrawpileApp &app, bool relativePenModeHack)
static void enableKisTabletWintab(bool relativePenModeHack)
{
if(KisTabletSupportWin::init()) {
installSpontaneousTabletEventFilter(app);
shouldPassPenEvents = false;
KisTabletSupportWin::enableRelativePenModeHack(relativePenModeHack);
if(relativePenModeHack) {
currentMode = Mode::KisTabletWintabRelativePenHack;
Expand Down Expand Up @@ -148,10 +109,10 @@ void init(DrawpileApp &app)
enableKisTabletWinink(app);
break;
case Mode::KisTabletWintab:
enableKisTabletWintab(app, false);
enableKisTabletWintab(false);
break;
case Mode::KisTabletWintabRelativePenHack:
enableKisTabletWintab(app, true);
enableKisTabletWintab(true);
break;
# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
case Mode::Qt5:
Expand All @@ -171,6 +132,7 @@ void init(DrawpileApp &app)
case Mode::Uninitialized:
break;
}
emit app.tabletDriverChanged();
});
#else
// Nothing to do on other platforms.
Expand Down Expand Up @@ -201,9 +163,7 @@ const char *current()
#ifdef Q_OS_WIN
bool passPenEvents()
{
// The spontaneous event filter is installed if and only if a KisTablet
// input mode is currently active.
return !spontaneousTabletEventFilter;
return shouldPassPenEvents;
}
#endif

Expand Down
16 changes: 13 additions & 3 deletions src/desktop/tabletinput.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef TABLETINPUT_H
#define TABLETINPUT_H

#ifndef DESTKOP_TABLETINPUT_H
#define DESTKOP_TABLETINPUT_H
#include <QMetaType>
#include <QString>

class DrawpileApp;

#ifdef Q_OS_WIN
# define TABLETINPUT_CONSTEXPR_OR_INLINE inline
#else
# define TABLETINPUT_CONSTEXPR_OR_INLINE constexpr
#endif

namespace tabletinput {
Q_NAMESPACE

Expand Down Expand Up @@ -60,6 +65,11 @@ constexpr bool passPenEvents()
}
#endif

TABLETINPUT_CONSTEXPR_OR_INLINE bool ignoreSpontaneous()
{
return !passPenEvents();
}

}

#endif
39 changes: 39 additions & 0 deletions src/desktop/utils/tabletfilter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#ifndef DESKTOP_UTILS_TABLETFILTER_H
#define DESKTOP_UTILS_TABLETFILTER_H
#include "desktop/tabletinput.h"
#include <QTabletEvent>

class TabletFilter {
public:
TABLETINPUT_CONSTEXPR_OR_INLINE void reset()
{
#ifdef Q_OS_WIN
m_ignoreSpontaneous = false;
#endif
}

TABLETINPUT_CONSTEXPR_OR_INLINE bool shouldIgnore(const QTabletEvent *event)
{
#ifdef Q_OS_WIN
bool spontaneous = event->spontaneous();
if(m_ignoreSpontaneous) {
return spontaneous;
} else {
if(!spontaneous && tabletinput::ignoreSpontaneous()) {
m_ignoreSpontaneous = true;
}
return false;
}
#else
Q_UNUSED(event);
return false;
#endif
}

#ifdef Q_OS_WIN
private:
bool m_ignoreSpontaneous = false;
#endif
};

#endif
Loading

0 comments on commit 9ed28b7

Please sign in to comment.