From bdd406e6ad96cfb7d31897e1bb2e551cf69036eb Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Fri, 19 Jun 2009 12:33:33 +0200 Subject: Multitouch, Cocoa: First revision of multi touch in Qt/Cocoa --- src/gui/kernel/kernel.pri | 16 +-- src/gui/kernel/qcocoaview_mac.mm | 66 +++++++++++- src/gui/kernel/qmultitouch_mac.mm | 208 +++++++++++++++++++++++++++++++++++++ src/gui/kernel/qmultitouch_mac_p.h | 103 ++++++++++++++++++ src/gui/kernel/qwidget.cpp | 4 +- src/gui/kernel/qwidget_mac.mm | 20 +++- src/gui/kernel/qwidget_p.h | 5 +- 7 files changed, 410 insertions(+), 12 deletions(-) create mode 100644 src/gui/kernel/qmultitouch_mac.mm create mode 100644 src/gui/kernel/qmultitouch_mac_p.h diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri index 3016afb..2917592 100644 --- a/src/gui/kernel/kernel.pri +++ b/src/gui/kernel/kernel.pri @@ -177,24 +177,26 @@ embedded { qcocoaview_mac_p.h \ qcocoaapplication_mac_p.h \ qcocoaapplicationdelegate_mac_p.h \ + qmultitouch_mac_p.h OBJECTIVE_SOURCES += \ kernel/qcursor_mac.mm \ kernel/qdnd_mac.mm \ kernel/qsound_mac.mm \ kernel/qapplication_mac.mm \ - kernel/qwidget_mac.mm \ - kernel/qcocoapanel_mac.mm \ - kernel/qcocoaview_mac.mm \ - kernel/qcocoawindow_mac.mm \ - kernel/qcocoawindowdelegate_mac.mm \ + kernel/qwidget_mac.mm \ + kernel/qcocoapanel_mac.mm \ + kernel/qcocoaview_mac.mm \ + kernel/qcocoawindow_mac.mm \ + kernel/qcocoawindowdelegate_mac.mm \ kernel/qcocoamenuloader_mac.mm \ kernel/qcocoaapplication_mac.mm \ kernel/qcocoaapplicationdelegate_mac.mm \ kernel/qt_cocoa_helpers_mac.mm \ - kernel/qdesktopwidget_mac.mm \ + kernel/qdesktopwidget_mac.mm \ kernel/qeventdispatcher_mac.mm \ - kernel/qcocoawindowcustomthemeframe_mac.mm + kernel/qcocoawindowcustomthemeframe_mac.mm \ + kernel/qmultitouch_mac.mm \ HEADERS += \ kernel/qt_cocoa_helpers_mac_p.h \ diff --git a/src/gui/kernel/qcocoaview_mac.mm b/src/gui/kernel/qcocoaview_mac.mm index 3d0bafa..9aa4dd2 100644 --- a/src/gui/kernel/qcocoaview_mac.mm +++ b/src/gui/kernel/qcocoaview_mac.mm @@ -49,6 +49,7 @@ #include #include #include +#include #include #include @@ -68,6 +69,7 @@ Q_GLOBAL_STATIC(DnDParams, qMacDnDParams); extern void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos); // qcursor_mac.mm extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); // qapplication.cpp +extern bool qt_translateRawTouchEvent(const QList &touchPoints, QWidget *window); // qapplication.cpp extern OSViewRef qt_mac_nativeview_for(const QWidget *w); // qwidget_mac.mm extern const QStringList& qEnabledDraggedTypes(); // qmime_mac.cpp extern QPointer qt_mouseover; //qapplication_mac.mm @@ -702,6 +704,11 @@ extern "C" { - (void)mouseUp:(NSEvent *)theEvent { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) + QCocoaTouch::setMouseInDraggingState(false); +#endif + bool mouseOK = qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, Qt::LeftButton); if (!mouseOK) @@ -709,7 +716,7 @@ extern "C" { } - (void)rightMouseDown:(NSEvent *)theEvent -{ +{ bool mouseOK = qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, Qt::RightButton); if (!mouseOK) @@ -745,6 +752,11 @@ extern "C" { - (void)mouseDragged:(NSEvent *)theEvent { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) + QCocoaTouch::setMouseInDraggingState(true); +#endif + qMacDnDParams()->view = self; qMacDnDParams()->theEvent = theEvent; bool mouseOK = qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::LeftButton); @@ -868,6 +880,58 @@ extern "C" { [super tabletPoint:tabletEvent]; } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 +- (void)touchesBeganWithEvent:(NSEvent *)event; +{ + qt_translateRawTouchEvent(QCocoaTouch::getCurrentTouchPointList(event), qwidget); +} + +- (void)touchesMovedWithEvent:(NSEvent *)event; +{ + qt_translateRawTouchEvent(QCocoaTouch::getCurrentTouchPointList(event), qwidget); +} + +- (void)touchesEndedWithEvent:(NSEvent *)event; +{ + qt_translateRawTouchEvent(QCocoaTouch::getCurrentTouchPointList(event), qwidget); +} + +- (void)touchesCancelledWithEvent:(NSEvent *)event; +{ + qt_translateRawTouchEvent(QCocoaTouch::getCurrentTouchPointList(event), qwidget); +} + +- (void)magnifyWithEvent:(NSEvent *)event; +{ + Q_UNUSED(event); +// qDebug() << "magnifyWithEvent"; +} + +- (void)rotateWithEvent:(NSEvent *)event; +{ + Q_UNUSED(event); +// qDebug() << "rotateWithEvent"; +} + +- (void)swipeWithEvent:(NSEvent *)event; +{ + Q_UNUSED(event); +// qDebug() << "swipeWithEvent"; +} + +- (void)beginGestureWithEvent:(NSEvent *)event; +{ + Q_UNUSED(event); +// qDebug() << "beginGestureWithEvent"; +} + +- (void)endGestureWithEvent:(NSEvent *)event; +{ + Q_UNUSED(event); +// qDebug() << "endGestureWithEvent"; +} +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + - (void)frameDidChange:(NSNotification *)note { Q_UNUSED(note); diff --git a/src/gui/kernel/qmultitouch_mac.mm b/src/gui/kernel/qmultitouch_mac.mm new file mode 100644 index 0000000..89cfc24 --- /dev/null +++ b/src/gui/kernel/qmultitouch_mac.mm @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + +#include +#include + +QT_BEGIN_NAMESPACE + +#ifdef QT_MAC_USE_COCOA + +QHash QCocoaTouch::_currentTouches; +QPointF QCocoaTouch::_screenReferencePos; +QPointF QCocoaTouch::_trackpadReferencePos; +bool QCocoaTouch::_inMouseDraggingState = false; +int QCocoaTouch::_idAssignmentCount = 0; + +QCocoaTouch::QCocoaTouch(NSTouch *nstouch) +{ + _identity = int([nstouch identity]); + + if (_currentTouches.size() == 0){ + // A new touch sequence is about to begin: + _touchPoint.setState(Qt::TouchPointPressed | Qt::TouchPointPrimary); + NSPoint npos = [nstouch normalizedPosition]; + _trackpadPos = QPointF(npos.x, npos.y); + // This touch will act as reference for all subsequent + // touches, so they appear next to each other on screen: + _trackpadReferencePos = _trackpadPos; + _screenReferencePos = QCursor::pos(); + _touchPoint.setScreenPos(_screenReferencePos); + // The first touch should always have 0 as id: + _touchPoint.setId(0); + _idAssignmentCount = 0; + _currentTouches.insert(_identity, this); + } else { + // We are already tracking at least one touch point. + _touchPoint.setId(++_idAssignmentCount); + _currentTouches.insert(_identity, this); + updateTouchData(nstouch, NSTouchPhaseBegan); + } +} + +QCocoaTouch::~QCocoaTouch() +{ + _currentTouches.remove(_identity); +} + +void QCocoaTouch::updateTouchData(NSTouch *nstouch, NSTouchPhase phase) +{ + NSPoint npos = [nstouch normalizedPosition]; + _trackpadPos = QPointF(npos.x, npos.y); + + if (_inMouseDraggingState || _currentTouches.size() == 1) + _touchPoint.setState(toTouchPointState(phase) | Qt::TouchPointPrimary); + else + _touchPoint.setState(toTouchPointState(phase)); + + // From the normalized position on the trackpad, calculate + // where on screen the touchpoint should be according to the + // reference position: + NSSize dsize = [nstouch deviceSize]; + float ppiX = (_trackpadPos.x() - _trackpadReferencePos.x()) * dsize.width; + float ppiY = (_trackpadPos.y() - _trackpadReferencePos.y()) * dsize.height; + QPointF relativePos = _trackpadReferencePos - QPointF(ppiX, 1 - ppiY); + _touchPoint.setScreenPos(_screenReferencePos - relativePos); +} + +QCocoaTouch *QCocoaTouch::findQCocoaTouch(NSTouch *nstouch) +{ + int identity = int([nstouch identity]); + if (_currentTouches.contains(identity)) + return _currentTouches.value(identity); + return 0; +} + +Qt::TouchPointState QCocoaTouch::toTouchPointState(NSTouchPhase nsState) +{ + Qt::TouchPointState qtState = Qt::TouchPointReleased; + switch (nsState) { + case NSTouchPhaseBegan: + qtState = Qt::TouchPointPressed; + break; + case NSTouchPhaseMoved: + qtState = Qt::TouchPointMoved; + break; + case NSTouchPhaseStationary: + qtState = Qt::TouchPointStationary; + break; + case NSTouchPhaseEnded: + case NSTouchPhaseCancelled: + qtState = Qt::TouchPointReleased; + break; + default: + break; + } + return qtState; +} + +void QCocoaTouch::setMouseInDraggingState(bool inDraggingState) +{ + // In mouse dragging state, _all_ fingers control the mouse. + // As such, all fingers should have the primary flag. + _inMouseDraggingState = inDraggingState; +} + +void QCocoaTouch::validateCurrentTouchList(NSEvent *event) +{ + // If the application shows the task switcher during a touch sequence + // we get into an inconsistant state. We try to detect that here: + NSTouchPhase p = NSTouchPhaseAny & ~NSTouchPhaseBegan; + NSSet *s = [event touchesMatchingPhase:p inView:nil]; + if ([s count] == 0 && !_currentTouches.isEmpty()) { + // Remove all instances, and basically start from scratch: + QList list = _currentTouches.values(); + foreach (QCocoaTouch *t, _currentTouches.values()) + delete t; + _currentTouches.clear(); + } +} + +QList QCocoaTouch::getCurrentTouchPointList(NSEvent *event) +{ + validateCurrentTouchList(event); + // Go through all the touchpoints in cocoa, find, or create, a QCocoaTouch for + // them, update them, and return the result: + QList touchPoints; + + NSSet *ended = [event touchesMatchingPhase:NSTouchPhaseEnded | NSTouchPhaseCancelled inView:nil]; + for (int i=0; iupdateTouchData(touch, [touch phase]); + touchPoints.append(qcocoaTouch->_touchPoint); + delete qcocoaTouch; + } + } + + NSSet *began = [event touchesMatchingPhase:NSTouchPhaseBegan inView:nil]; + for (int i=0; iupdateTouchData(touch, [touch phase]); + touchPoints.append(qcocoaTouch->_touchPoint); + } + + NSSet *active = [event touchesMatchingPhase:NSTouchPhaseMoved | NSTouchPhaseStationary inView:nil]; + for (int i=0; iupdateTouchData(touch, [touch phase]); + touchPoints.append(qcocoaTouch->_touchPoint); + } + } + + return touchPoints; +} + +#endif + +QT_END_NAMESPACE + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + diff --git a/src/gui/kernel/qmultitouch_mac_p.h b/src/gui/kernel/qmultitouch_mac_p.h new file mode 100644 index 0000000..92ed11a --- /dev/null +++ b/src/gui/kernel/qmultitouch_mac_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QMULTITOUCH_MAC_P_H +#define QMULTITOUCH_MAC_P_H + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + +#ifdef QT_MAC_USE_COCOA +#import +#endif + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +#ifdef QT_MAC_USE_COCOA + +class QCocoaTouch +{ + public: + static QList getCurrentTouchPointList(NSEvent *event); + static void setMouseInDraggingState(bool inDraggingState); + + private: + static QHash _currentTouches; + static QPointF _screenReferencePos; + static QPointF _trackpadReferencePos; + static bool _inMouseDraggingState; + static int _idAssignmentCount; + + QTouchEvent::TouchPoint _touchPoint; + QPointF _trackpadPos; + int _identity; + + QCocoaTouch(NSTouch *nstouch); + ~QCocoaTouch(); + + void updateTouchData(NSTouch *nstouch, NSTouchPhase phase); + static QCocoaTouch *findQCocoaTouch(NSTouch *nstouch); + static Qt::TouchPointState toTouchPointState(NSTouchPhase nsState); + static void validateCurrentTouchList(NSEvent *event); +}; + +#endif // QT_MAC_USE_COCOA + +QT_END_NAMESPACE + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + +#endif // QMULTITOUCH_MAC_P_H + diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index 8dd32f5..304d72a 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -9928,12 +9928,12 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) } break; -#if defined(Q_WS_WIN) case Qt::WA_AcceptTouchEvents: +#if defined(Q_WS_WIN) || defined(Q_WS_MAC) if (on) d->registerTouchWindow(); - break; #endif + break; default: break; } diff --git a/src/gui/kernel/qwidget_mac.mm b/src/gui/kernel/qwidget_mac.mm index ad80f15..2dda15d 100644 --- a/src/gui/kernel/qwidget_mac.mm +++ b/src/gui/kernel/qwidget_mac.mm @@ -2512,9 +2512,10 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO setFocus_sys(); if (!topLevel && initializeWindow) setWSGeometry(); - if (destroyid) qt_mac_destructView(destroyid); + if (q->testAttribute(Qt::WA_AcceptTouchEvents)) + registerTouchWindow(); } /*! @@ -4491,6 +4492,23 @@ void QWidgetPrivate::registerDropSite(bool on) #endif } +void QWidgetPrivate::registerTouchWindow() +{ +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_6) + return; + Q_Q(QWidget); + if (!q->testAttribute(Qt::WA_WState_Created)) + return; +#ifndef QT_MAC_USE_COCOA + // Needs implementation! +#else + NSView *view = qt_mac_nativeview_for(q); + [view setAcceptsTouchEvents:YES]; +#endif +#endif +} + void QWidgetPrivate::setMask_sys(const QRegion ®ion) { Q_UNUSED(region); diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index cf065d3..d597a50 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -541,6 +541,10 @@ public: uint usesDoubleBufferedGLContext : 1; // *************************** Platform specific ************************************ +#if defined(Q_WS_X11) || defined(Q_WS_MAC) + void registerTouchWindow(); +#endif + #if defined(Q_WS_X11) // <----------------------------------------------------------- X11 QX11Info xinfo; Qt::HANDLE picture; @@ -563,7 +567,6 @@ public: void unregisterOleDnd(QWidget *widget, QOleDropTarget *target); #endif void grabMouseWhileInWindow(); - void registerTouchWindow(); #elif defined(Q_WS_MAC) // <--------------------------------------------------------- MAC // This is new stuff uint needWindowChange : 1; -- cgit v0.12