Skip to content

Commit

Permalink
Allow pen input
Browse files Browse the repository at this point in the history
  • Loading branch information
Eeems committed Jan 13, 2024
1 parent 8832ccc commit 08afa0f
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 0 deletions.
151 changes: 151 additions & 0 deletions src/eventfilter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#include "eventfilter.h"

#include <QTimer>
#include <QMouseEvent>
#include <QTabletEvent>
#include <QScreen>
#include <QGuiApplication>

#define DISPLAYWIDTH 1404
#define DISPLAYHEIGHT 1872.0
#define WACOM_X_SCALAR (float(DISPLAYWIDTH) / float(DISPLAYHEIGHT))
#define WACOM_Y_SCALAR (float(DISPLAYHEIGHT) / float(DISPLAYWIDTH))
#define DEBUG_EVENTS
#ifdef DEBUG_EVENTS
#define O_DEBUG_EVENT(msg) qDebug() << msg
#else
#define O_DEBUG_EVENT(msg)
#endif

EventFilter::EventFilter(QObject *parent) : QObject(parent), root(nullptr){}

QPointF swap(QPointF pointF){
return QPointF(pointF.y(), pointF.x());
}

QPointF transpose(QPointF pointF){
pointF = swap(pointF);
// Handle scaling from wacom to screensize
pointF.setX(pointF.x() * WACOM_X_SCALAR);
pointF.setY((DISPLAYWIDTH - pointF.y()) * WACOM_Y_SCALAR);
return pointF;
}
QPointF globalPos(QQuickItem* obj){
qreal x = obj->x();
qreal y = obj->y();
while(obj->parentItem() != nullptr){
obj = obj->parentItem();
x += obj->x();
y += obj->y();
}
return QPointF(x, y);
}
QMouseEvent* toMouseEvent(QEvent::Type type, QEvent* ev){
auto tabletEvent = static_cast<QTabletEvent*>(ev);
return new QMouseEvent(
type,
transpose(tabletEvent->posF()),
transpose(tabletEvent->globalPosF()),
transpose(tabletEvent->globalPosF()),
tabletEvent->button(),
tabletEvent->buttons(),
tabletEvent->modifiers()
);
}
bool isAt(QQuickItem* item, QPointF pos){
auto itemPos = globalPos(item);
auto otherItemPos = QPointF(itemPos.x() + item->width(), itemPos.y() + item->height());
return pos.x() >= itemPos.x() && pos.x() <= otherItemPos.x() && pos.y() >= itemPos.y() && pos.y() <= otherItemPos.y();
}
QList<QObject*> widgetsAt(QQuickItem* root, QPointF pos){
QList<QObject*> result;
auto children = root->findChildren<QQuickItem*>();
for(auto child : children){
if(result.contains(child)){
continue;
}
if(!child->isVisible() || !child->isEnabled()){
continue;
}
if(child->acceptedMouseButtons() & Qt::LeftButton && isAt(child, pos)){
result.append((QObject*)child);
for(auto item : widgetsAt(child, pos)){
if(!result.contains(item)){
result.append(item);
}
}
continue;
}
if(!child->clip()){
for(auto item : widgetsAt(child, pos)){
if(!result.contains(item)){
result.append(item);
}
}
}
}
return result;
}
int parentCount(QQuickItem* obj){
int count = 0;
while(obj->parentItem()){
count++;
obj = obj->parentItem();
}
return count;
}
void postEvent(QEvent::Type type, QEvent* ev, QQuickItem* root){
auto mouseEvent = toMouseEvent(type, ev);
auto pos = mouseEvent->globalPos();
for(auto postWidget : widgetsAt(root, pos)){
if(parentCount((QQuickItem*)postWidget)){
O_DEBUG_EVENT("postWidget: " << postWidget);
auto event = new QMouseEvent(
mouseEvent->type(), mouseEvent->localPos(), mouseEvent->windowPos(),
mouseEvent->screenPos(), mouseEvent->button(), mouseEvent->buttons(),
mouseEvent->modifiers()
);
auto widgetPos = globalPos((QQuickItem*)postWidget);
auto localPos = event->localPos();
localPos.setX(pos.x() - widgetPos.x());
localPos.setY((pos.y()) - widgetPos.y());
event->setLocalPos(localPos);
QGuiApplication::postEvent(postWidget, event);
}
}
delete mouseEvent;
}

bool EventFilter::eventFilter(QObject* obj, QEvent* ev){
auto type = ev->type();
bool filtered = QObject::eventFilter(obj, ev);
if(!filtered){
if(type == QEvent::TabletPress){
O_DEBUG_EVENT(ev << ev->isAccepted());
postEvent(QMouseEvent::MouseButtonPress, ev, root);
}else if(type == QEvent::TabletRelease){
O_DEBUG_EVENT(ev << ev->isAccepted());
postEvent(QMouseEvent::MouseButtonRelease, ev, root);
}else if(type == QEvent::TabletMove){
O_DEBUG_EVENT(ev << ev->isAccepted());
postEvent(QMouseEvent::MouseMove, ev, root);
}
#ifdef DEBUG_EVENTS
else if(
type != QEvent::MetaCall
&& type != QEvent::SockAct
&& type != QEvent::SockClose
&& type != QEvent::ChildAdded
&& type != QEvent::ChildPolished
&& type != QEvent::ChildRemoved
&& type != QEvent::Timer
&& type != QEvent::UpdateRequest
){
O_DEBUG_EVENT(ev << ev->isAccepted() << obj);
}
#endif
}
return filtered;
}

#include "moc_eventfilter.cpp"
37 changes: 37 additions & 0 deletions src/eventfilter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#pragma once


#include <QObject>
#include <QEvent>
#include <QQuickItem>

/*!
* \brief An event filter that maps pen events to Qt touch events.
*
* It works by handling tablet events and translating them to mouse events.
* They are then sent to every enabled/visible widget at the x/y coordinate that have a parent widget, and accept left mouse button input.
* This doens't always work as some widgets aren't found with the current method of finding widgets at a location.
*
* The following is an example of adding it to an application:
* \snippet examples/oxide.cpp EventFilter
*/
class EventFilter : public QObject
{
Q_OBJECT
public:
/*!
* \brief The root element in the Qt application
*
* This is needed to find wigets to send mouse events when a pen touch is detected
*/
QQuickItem* root;
/*!
* \brief Create a new EventFilter instance
* \param parent The parent object. Usually should be qApp
*/
explicit EventFilter(QObject* parent = nullptr);
signals:
void suspend();
protected:
bool eventFilter(QObject* obj, QEvent* ev);
};
6 changes: 6 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include <cstdlib>
#include <signal.h>

#include "eventfilter.h"

#ifdef EPAPER
// This is required for Qt to display to the reMarkable's display
Q_IMPORT_PLUGIN(QsgEpaperPlugin)
Expand Down Expand Up @@ -53,6 +55,8 @@ int main(int argc, char *argv[]){
qDebug() << "Desktop detected...";
#endif
QGuiApplication app(argc, argv);
auto filter = new EventFilter(&app);
app.installEventFilter(filter);
app.setApplicationName("gameboy");
app.setApplicationDisplayName("Gameboy Emulator");
app.setApplicationVersion(APP_VERSION);
Expand All @@ -64,6 +68,8 @@ int main(int argc, char *argv[]){
qDebug() << "Nothing to display";
return -1;
}
auto root = engine.rootObjects().first();
filter->root = (QQuickItem*)root;

// Setup some signal handlers to make sure to quit the application normally if these signals are recieved
signal(SIGINT, sigHandler);
Expand Down
2 changes: 2 additions & 0 deletions src/src.pro
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ DEFINES += QT_DEPRECATED_WARNINGS
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x051510

SOURCES += \
eventfilter.cpp \
main.cpp

# This provides more context on crashes when built in debug mode
Expand Down Expand Up @@ -77,6 +78,7 @@ linux-oe-g++ {
}

HEADERS += \
eventfilter.h \
gameboy.h \
gameboythread.h

Expand Down

0 comments on commit 08afa0f

Please sign in to comment.