From e44d64510e019e5d3b379b704cfb824e0d7ccc9d Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Mon, 15 Jun 2009 11:52:50 +0200 Subject: Add multitouch support for the RX-71 device We have to by-pass X11 mouse events when using this device since we are reading directly from the /dev/input/event* device files. --- src/gui/kernel/qapplication.h | 3 + src/gui/kernel/qapplication_p.h | 19 +++ src/gui/kernel/qapplication_x11.cpp | 275 ++++++++++++++++++++++++++++++++++++ 3 files changed, 297 insertions(+) diff --git a/src/gui/kernel/qapplication.h b/src/gui/kernel/qapplication.h index e09ea08..114e26f 100644 --- a/src/gui/kernel/qapplication.h +++ b/src/gui/kernel/qapplication.h @@ -391,6 +391,9 @@ private: #if defined(Q_WS_MAC) || defined(Q_WS_X11) Q_PRIVATE_SLOT(d_func(), void _q_alertTimeOut()) #endif +#if defined(QT_RX71_MULTITOUCH) + Q_PRIVATE_SLOT(d_func(), void _q_readRX71MultiTouchEvents()) +#endif }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index 60796fc..5f788d3 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -81,6 +81,7 @@ class QInputContext; class QObject; class QWidget; class QGestureManager; +class QSocketNotifier; extern bool qt_is_gui_used; #ifndef QT_NO_CLIPBOARD @@ -457,6 +458,24 @@ public: bool translateTouchEvent(const MSG &msg); #endif +#ifdef QT_RX71_MULTITOUCH + bool hasRX71MultiTouch; + + struct RX71TouchPointState { + QSocketNotifier *socketNotifier; + QTouchEvent::TouchPoint touchPoint; + + int minX, maxX, scaleX; + int minY, maxY, scaleY; + int minZ, maxZ; + }; + QList allRX71TouchPoints; + + bool readRX71MultiTouchEvents(int deviceNumber); + void fakeMouseEventFromRX71TouchEvent(); + void _q_readRX71MultiTouchEvents(); +#endif + private: #ifdef Q_WS_QWS QMap maxWindowRects; diff --git a/src/gui/kernel/qapplication_x11.cpp b/src/gui/kernel/qapplication_x11.cpp index d55aa2d..662819e 100644 --- a/src/gui/kernel/qapplication_x11.cpp +++ b/src/gui/kernel/qapplication_x11.cpp @@ -128,6 +128,12 @@ extern "C" { #include +#ifdef QT_RX71_MULTITOUCH +# include +# include +# include +#endif + QT_BEGIN_NAMESPACE //#define X_NOT_BROKEN @@ -3207,6 +3213,21 @@ int QApplication::x11ProcessEvent(XEvent* event) { Q_D(QApplication); QScopedLoopLevelCounter loopLevelCounter(d->threadData); + +#ifdef QT_RX71_MULTITOUCH + switch (event->type) { + case ButtonPress: + case ButtonRelease: + case MotionNotify: + // if multitouch is active, we have to block all pointer events from X11 + if (d->hasRX71MultiTouch) + return 1; + break; + default: + break; + } +#endif + #ifdef ALIEN_DEBUG //qDebug() << "QApplication::x11ProcessEvent:" << event->type; #endif @@ -5986,9 +6007,263 @@ void QSessionManager::requestPhase2() #endif // QT_NO_SESSIONMANAGER +#if defined(QT_RX71_MULTITOUCH) + +static int openRX71Device(const QByteArray &deviceName) +{ + int fd = open(deviceName, O_RDONLY | O_NONBLOCK); + if (fd == -1) { + fd = -errno; + return fd; + } + + // fetch the event type mask and check that the device reports absolute coordinates + QBitArray eventTypeMask(EV_CNT, false); + if (ioctl(fd, EVIOCGBIT(0, eventTypeMask.size()), eventTypeMask.data_ptr()->data) < 0) { + close(fd); + return -1; + } + if (!eventTypeMask.testBit(EV_ABS)) { + close(fd); + return -1; + } + + // make sure that we can get the absolute X and Y positions from the device + QBitArray absMask(ABS_CNT, false); + if (ioctl(fd, EVIOCGBIT(EV_ABS, absMask.size()), absMask.data_ptr()->data) < 0) { + close(fd); + return -1; + } + if (!absMask.testBit(ABS_X) || !absMask.testBit(ABS_Y)) { + close(fd); + return -1; + } + + return fd; +} + +void QApplicationPrivate::initializeMultitouch_sys() +{ + Q_Q(QApplication); + + QByteArray deviceName = QByteArray("/dev/input/event"); + int currentDeviceNumber = 0; + for (;;) { + int fd = openRX71Device(QByteArray(deviceName + QByteArray::number(currentDeviceNumber++)).data()); + if (fd == -ENOENT) { + // no more devices + break; + } + if (fd < 0) { + // not a touch device + continue; + } + + struct input_absinfo abs_x, abs_y, abs_z; + ioctl(fd, EVIOCGABS(ABS_X), &abs_x); + ioctl(fd, EVIOCGABS(ABS_Y), &abs_y); + ioctl(fd, EVIOCGABS(ABS_Z), &abs_z); + + int deviceNumber = allRX71TouchPoints.count(); + + QSocketNotifier *socketNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, q); + QObject::connect(socketNotifier, SIGNAL(activated(int)), q, SLOT(_q_readRX71MultiTouchEvents())); + + RX71TouchPointState touchPointState = { + socketNotifier, + QTouchEvent::TouchPoint(deviceNumber), + + abs_x.minimum, abs_x.maximum, q->desktop()->screenGeometry().width(), + abs_y.minimum, abs_y.maximum, q->desktop()->screenGeometry().height(), + abs_z.minimum, abs_z.maximum + }; + allRX71TouchPoints.append(touchPointState); + } + + hasRX71MultiTouch = allRX71TouchPoints.count() > 1; + if (!hasRX71MultiTouch) { + for (int i = 0; i < allRX71TouchPoints.count(); ++i) { + QSocketNotifier *socketNotifier = allRX71TouchPoints.at(i).socketNotifier; + close(socketNotifier->socket()); + delete socketNotifier; + } + allRX71TouchPoints.clear(); + } +} + +void QApplicationPrivate::cleanupMultitouch_sys() +{ + hasRX71MultiTouch = false; + for (int i = 0; i < allRX71TouchPoints.count(); ++i) { + QSocketNotifier *socketNotifier = allRX71TouchPoints.at(i).socketNotifier; + close(socketNotifier->socket()); + delete socketNotifier; + } + allRX71TouchPoints.clear(); +} + +bool QApplicationPrivate::readRX71MultiTouchEvents(int deviceNumber) +{ + RX71TouchPointState &touchPointState = allRX71TouchPoints[deviceNumber]; + QSocketNotifier *socketNotifier = touchPointState.socketNotifier; + int fd = socketNotifier->socket(); + + QTouchEvent::TouchPoint &touchPoint = touchPointState.touchPoint; + + bool down = touchPoint.state() != Qt::TouchPointReleased; + if (down) + touchPoint.setState(Qt::TouchPointStationary); + + bool changed = false; + for (;;) { + struct input_event inputEvent; + int bytesRead = read(fd, &inputEvent, sizeof(inputEvent)); + if (bytesRead <= 0) + break; + if (bytesRead != sizeof(inputEvent)) { + qWarning("Qt: INTERNAL ERROR: short read in readRX71MultiTouchEvents()"); + return false; + } + + switch (inputEvent.type) { + case EV_SYN: + changed = true; + switch (touchPoint.state()) { + case Qt::TouchPointPressed: + case Qt::TouchPointReleased: + // make sure we don't compress pressed and releases with any other events + return changed; + default: + break; + } + continue; + case EV_KEY: + case EV_ABS: + break; + default: + qWarning("Qt: WARNING: unknown event type %d on multitouch device", inputEvent.type); + continue; + } + + QPointF screenPos = touchPoint.screenPos(); + switch (inputEvent.code) { + case BTN_TOUCH: + if (!down && inputEvent.value != 0) + touchPoint.setState(Qt::TouchPointPressed); + else if (down && inputEvent.value == 0) + touchPoint.setState(Qt::TouchPointReleased); + break; + case ABS_TOOL_WIDTH: + case ABS_VOLUME: + case ABS_PRESSURE: + // ignore for now + break; + case ABS_X: + { + qreal newValue = ((qreal(inputEvent.value - touchPointState.minX) + / qreal(touchPointState.maxX - touchPointState.minX)) + * touchPointState.scaleX); + screenPos.rx() = newValue; + touchPoint.setScreenPos(screenPos); + break; + } + case ABS_Y: + { + qreal newValue = ((qreal(inputEvent.value - touchPointState.minY) + / qreal(touchPointState.maxY - touchPointState.minY)) + * touchPointState.scaleY); + screenPos.ry() = newValue; + touchPoint.setScreenPos(screenPos); + break; + } + case ABS_Z: + { + // map Z (signal strength) to pressure for now + qreal newValue = (qreal(inputEvent.value - touchPointState.minZ) + / qreal(touchPointState.maxZ - touchPointState.minZ)); + touchPoint.setPressure(newValue); + break; + } + default: + qWarning("Qt: WARNING: unknown event code %d on multitouch device", inputEvent.code); + continue; + } + } + + if (down && touchPoint.state() != Qt::TouchPointReleased) + touchPoint.setState(changed ? Qt::TouchPointMoved : Qt::TouchPointStationary); + + return changed; +} + +void QApplicationPrivate::_q_readRX71MultiTouchEvents() +{ + // read touch events from all devices + bool changed = false; + for (int i = 0; i < allRX71TouchPoints.count(); ++i) + changed = readRX71MultiTouchEvents(i) || changed; + if (!changed) + return; + + QList touchPoints; + for (int i = 0; i < allRX71TouchPoints.count(); ++i) + touchPoints.append(allRX71TouchPoints.at(i).touchPoint); + + if (!translateRawTouchEvent(0, touchPoints)) + fakeMouseEventFromRX71TouchEvent(); +} + +void QApplicationPrivate::fakeMouseEventFromRX71TouchEvent() +{ + // we only fake mouse events from the first touch point + const QTouchEvent::TouchPoint &touchPoint = allRX71TouchPoints.first().touchPoint; + + QEvent::Type mouseEventType; + switch (touchPoint.state()) { + case Qt::TouchPointPressed: + mouseEventType = QEvent::MouseButtonPress; + break; + case Qt::TouchPointMoved: + mouseEventType = QEvent::MouseMove; + break; + case Qt::TouchPointReleased: + mouseEventType = QEvent::MouseButtonRelease; + break; + case Qt::TouchPointStationary: + default: + // finger didn't move, ignore the event + return; + } + + QPoint screenPos = touchPoint.screenPos().toPoint(); + Q_Q(QApplication); + QWidget *window = q->activePopupWidget(); + if (!window) + window = q->topLevelAt(screenPos); + if (!window) + return; + + QPoint pos = window->mapFromGlobal(screenPos); + QWidget *widget = window->childAt(pos); + if (!widget) + widget = window; + + QMouseEvent mouseEvent(mouseEventType, + pos, + screenPos, + Qt::LeftButton, + Qt::LeftButton, + q->keyboardModifiers()); + (void) QApplication::sendEvent(widget, &mouseEvent); +} + +#else // !QT_RX71_MULTITOUCH + void QApplicationPrivate::initializeMultitouch_sys() { } void QApplicationPrivate::cleanupMultitouch_sys() { } +#endif // QT_RX71_MULTITOUCH + QT_END_NAMESPACE -- cgit v0.12