Add WACOM tablet support for Windows.
Task-number: QTBUG-30008 Change-Id: I3327baecae7c5d1411bcd125411097f36460cde5 Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com> Reviewed-by: Shawn Rutledge <shawn.rutledge@digia.com>bb10
parent
ac720981d2
commit
3168c94577
|
|
@ -47,6 +47,7 @@
|
|||
#include "qtwindowsglobal.h"
|
||||
#include "qwindowsmime.h"
|
||||
#include "qwindowsinputcontext.h"
|
||||
#include "qwindowstabletsupport.h"
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
#include "accessible/qwindowsaccessibility.h"
|
||||
#endif
|
||||
|
|
@ -83,6 +84,7 @@ int QWindowsContext::verboseOLE = 0;
|
|||
int QWindowsContext::verboseInputMethods = 0;
|
||||
int QWindowsContext::verboseDialogs = 0;
|
||||
int QWindowsContext::verboseTheming = 0;
|
||||
int QWindowsContext::verboseTablet = 0;
|
||||
|
||||
// Get verbosity of components from "foo:2,bar:3"
|
||||
static inline int componentVerbose(const char *v, const char *keyWord)
|
||||
|
|
@ -259,17 +261,20 @@ struct QWindowsContextPrivate {
|
|||
QWindowsMimeConverter m_mimeConverter;
|
||||
QWindowsScreenManager m_screenManager;
|
||||
QSharedPointer<QWindowCreationContext> m_creationContext;
|
||||
#if !defined(QT_NO_TABLETEVENT) && !defined(Q_OS_WINCE)
|
||||
QScopedPointer<QWindowsTabletSupport> m_tabletSupport;
|
||||
#endif
|
||||
const HRESULT m_oleInitializeResult;
|
||||
const QByteArray m_eventType;
|
||||
QWindow *m_lastActiveWindow;
|
||||
bool m_asyncExpose;
|
||||
};
|
||||
|
||||
QWindowsContextPrivate::QWindowsContextPrivate() :
|
||||
m_systemInfo(0),
|
||||
m_oleInitializeResult(OleInitialize(NULL)),
|
||||
m_eventType(QByteArrayLiteral("windows_generic_MSG")),
|
||||
m_lastActiveWindow(0), m_asyncExpose(0)
|
||||
QWindowsContextPrivate::QWindowsContextPrivate()
|
||||
: m_systemInfo(0)
|
||||
, m_oleInitializeResult(OleInitialize(NULL))
|
||||
, m_eventType(QByteArrayLiteral("windows_generic_MSG"))
|
||||
, m_lastActiveWindow(0), m_asyncExpose(0)
|
||||
{
|
||||
const QSysInfo::WinVersion ver = QSysInfo::windowsVersion();
|
||||
#ifndef Q_OS_WINCE
|
||||
|
|
@ -310,7 +315,13 @@ QWindowsContext::QWindowsContext() :
|
|||
QWindowsContext::verboseInputMethods = componentVerbose(v, "im");
|
||||
QWindowsContext::verboseDialogs = componentVerbose(v, "dialogs");
|
||||
QWindowsContext::verboseTheming = componentVerbose(v, "theming");
|
||||
QWindowsContext::verboseTablet = componentVerbose(v, "tablet");
|
||||
}
|
||||
#if !defined(QT_NO_TABLETEVENT) && !defined(Q_OS_WINCE)
|
||||
d->m_tabletSupport.reset(QWindowsTabletSupport::create());
|
||||
if (QWindowsContext::verboseTablet)
|
||||
qDebug() << "Tablet support: " << (d->m_tabletSupport.isNull() ? QStringLiteral("None") : d->m_tabletSupport->description());
|
||||
#endif
|
||||
}
|
||||
|
||||
QWindowsContext::~QWindowsContext()
|
||||
|
|
@ -626,6 +637,15 @@ QWindowsScreenManager &QWindowsContext::screenManager()
|
|||
return d->m_screenManager;
|
||||
}
|
||||
|
||||
QWindowsTabletSupport *QWindowsContext::tabletSupport() const
|
||||
{
|
||||
#if !defined(QT_NO_TABLETEVENT) && !defined(Q_OS_WINCE)
|
||||
return d->m_tabletSupport.data();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Convenience to create a non-visible, message-only dummy
|
||||
window for example used as clipboard watcher or for GL.
|
||||
|
|
@ -854,6 +874,10 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
|
|||
return true;
|
||||
#ifndef Q_OS_WINCE
|
||||
case QtWindows::ActivateWindowEvent:
|
||||
#ifndef QT_NO_TABLETEVENT
|
||||
if (!d->m_tabletSupport.isNull())
|
||||
d->m_tabletSupport->notifyActivate();
|
||||
#endif // !QT_NO_TABLETEVENT
|
||||
if (platformWindow->testFlag(QWindowsWindow::BlockedByModal))
|
||||
if (const QWindow *modalWindow = QGuiApplication::modalWindow())
|
||||
QWindowsWindow::baseWindowOf(modalWindow)->alertWindow();
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ QT_BEGIN_NAMESPACE
|
|||
class QWindow;
|
||||
class QPlatformScreen;
|
||||
class QWindowsScreenManager;
|
||||
class QWindowsTabletSupport;
|
||||
class QWindowsWindow;
|
||||
class QWindowsMimeConverter;
|
||||
struct QWindowCreationContext;
|
||||
|
|
@ -135,6 +136,7 @@ public:
|
|||
static int verboseInputMethods;
|
||||
static int verboseDialogs;
|
||||
static int verboseTheming;
|
||||
static int verboseTablet;
|
||||
|
||||
explicit QWindowsContext();
|
||||
~QWindowsContext();
|
||||
|
|
@ -184,6 +186,7 @@ public:
|
|||
|
||||
QWindowsMimeConverter &mimeConverter() const;
|
||||
QWindowsScreenManager &screenManager();
|
||||
QWindowsTabletSupport *tabletSupport() const;
|
||||
#ifndef Q_OS_WINCE
|
||||
static QWindowsUser32DLL user32dll;
|
||||
static QWindowsShell32DLL shell32dll;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,473 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the plugins of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** 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, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, 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.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qwindowstabletsupport.h"
|
||||
|
||||
#ifndef QT_NO_TABLETEVENT
|
||||
|
||||
#include "qwindowscontext.h"
|
||||
#include "qwindowskeymapper.h"
|
||||
#include "qwindowswindow.h"
|
||||
|
||||
#include <qpa/qwindowsysteminterface.h>
|
||||
|
||||
#include <QtGui/QTabletEvent>
|
||||
#include <QtGui/QScreen>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QWindow>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QScopedArrayPointer>
|
||||
#include <QtCore/QtMath>
|
||||
|
||||
#include <private/qguiapplication_p.h>
|
||||
#include <QtCore/private/qsystemlibrary_p.h>
|
||||
|
||||
// Note: The definition of the PACKET structure in pktdef.h depends on this define.
|
||||
#define PACKETDATA (PK_X | PK_Y | PK_BUTTONS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION | PK_CURSOR | PK_Z)
|
||||
#include <pktdef.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
enum {
|
||||
PacketMode = 0,
|
||||
TabletPacketQSize = 128,
|
||||
DeviceIdMask = 0xFF6, // device type mask && device color mask
|
||||
CursorTypeBitMask = 0x0F06 // bitmask to find the specific cursor type (see Wacom FAQ)
|
||||
};
|
||||
|
||||
extern "C" LRESULT QT_WIN_CALLBACK qWindowsTabletSupportWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (message) {
|
||||
case WT_PROXIMITY:
|
||||
if (QWindowsContext::instance()->tabletSupport()->translateTabletProximityEvent(wParam, lParam))
|
||||
return 0;
|
||||
break;
|
||||
case WT_PACKET:
|
||||
if (QWindowsContext::instance()->tabletSupport()->translateTabletPacketEvent())
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
return DefWindowProc(hwnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
|
||||
// Scale tablet coordinates to screen coordinates.
|
||||
|
||||
static inline int sign(int x)
|
||||
{
|
||||
return x >= 0 ? 1 : -1;
|
||||
}
|
||||
|
||||
inline QPointF QWindowsTabletDeviceData::scaleCoordinates(int coordX, int coordY, const QRect &targetArea) const
|
||||
{
|
||||
const int targetX = targetArea.x();
|
||||
const int targetY = targetArea.y();
|
||||
const int targetWidth = targetArea.width();
|
||||
const int targetHeight = targetArea.height();
|
||||
|
||||
const qreal x = sign(targetWidth) == sign(maxX) ?
|
||||
((coordX - minX) * qAbs(targetWidth) / qAbs(qreal(maxX - minX))) + targetX :
|
||||
((qAbs(maxX) - (coordX - minX)) * qAbs(targetWidth) / qAbs(qreal(maxX - minX))) + targetX;
|
||||
|
||||
const qreal y = sign(targetHeight) == sign(maxY) ?
|
||||
((coordY - minY) * qAbs(targetHeight) / qAbs(qreal(maxY - minY))) + targetY :
|
||||
((qAbs(maxY) - (coordY - minY)) * qAbs(targetHeight) / qAbs(qreal(maxY - minY))) + targetY;
|
||||
|
||||
return QPointF(x, y);
|
||||
}
|
||||
|
||||
QWindowsWinTab32DLL QWindowsTabletSupport::m_winTab32DLL;
|
||||
|
||||
/*!
|
||||
\class QWindowsWinTab32DLL QWindowsTabletSupport
|
||||
\brief Functions from wintabl32.dll shipped with WACOM tablets used by QWindowsTabletSupport.
|
||||
|
||||
\internal
|
||||
\ingroup qt-lighthouse-win
|
||||
*/
|
||||
|
||||
bool QWindowsWinTab32DLL::init()
|
||||
{
|
||||
if (wTInfo)
|
||||
return true;
|
||||
QSystemLibrary library(QStringLiteral("wintab32"));
|
||||
if (!library.load())
|
||||
return false;
|
||||
wTOpen = (PtrWTOpen)library.resolve("WTOpenW");
|
||||
wTClose = (PtrWTClose)library.resolve("WTClose");
|
||||
wTInfo = (PtrWTInfo)library.resolve("WTInfoW");
|
||||
wTEnable = (PtrWTEnable)library.resolve("WTEnable");
|
||||
wTOverlap = (PtrWTEnable)library.resolve("WTOverlap");
|
||||
wTPacketsGet = (PtrWTPacketsGet)library.resolve("WTPacketsGet");
|
||||
wTGet = (PtrWTGet)library.resolve("WTGetW");
|
||||
wTQueueSizeGet = (PtrWTQueueSizeGet)library.resolve("WTQueueSizeGet");
|
||||
wTQueueSizeSet = (PtrWTQueueSizeSet)library.resolve("WTQueueSizeSet");
|
||||
return wTOpen && wTClose && wTInfo && wTEnable && wTOverlap && wTPacketsGet && wTQueueSizeGet && wTQueueSizeSet;
|
||||
}
|
||||
|
||||
/*!
|
||||
\class QWindowsTabletSupport
|
||||
\brief Tablet support for Windows.
|
||||
|
||||
Support for WACOM tablets.
|
||||
|
||||
\sa http://www.wacomeng.com/windows/docs/Wintab_v140.htm
|
||||
|
||||
\internal
|
||||
\since 5.2
|
||||
\ingroup qt-lighthouse-win
|
||||
*/
|
||||
|
||||
QWindowsTabletSupport::QWindowsTabletSupport(HWND window, HCTX context)
|
||||
: m_window(window)
|
||||
, m_context(context)
|
||||
, m_tiltSupport(false)
|
||||
, m_currentDevice(-1)
|
||||
{
|
||||
AXIS orientation[3];
|
||||
// Some tablets don't support tilt, check if it is possible,
|
||||
if (QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES, DVC_ORIENTATION, &orientation))
|
||||
m_tiltSupport = orientation[0].axResolution && orientation[1].axResolution;
|
||||
}
|
||||
|
||||
QWindowsTabletSupport::~QWindowsTabletSupport()
|
||||
{
|
||||
QWindowsTabletSupport::m_winTab32DLL.wTClose(m_context);
|
||||
DestroyWindow(m_window);
|
||||
}
|
||||
|
||||
QWindowsTabletSupport *QWindowsTabletSupport::create()
|
||||
{
|
||||
if (!m_winTab32DLL.init())
|
||||
return 0;
|
||||
const HWND window = QWindowsContext::instance()->createDummyWindow(QStringLiteral("TabletDummyWindow"),
|
||||
L"TabletDummyWindow",
|
||||
qWindowsTabletSupportWndProc);
|
||||
if (!window) {
|
||||
if (QWindowsContext::verboseTablet)
|
||||
qWarning() << __FUNCTION__ << "Unable to create window for tablet.";
|
||||
return 0;
|
||||
}
|
||||
LOGCONTEXT lcMine;
|
||||
// build our context from the default context
|
||||
QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEFSYSCTX, 0, &lcMine);
|
||||
// Go for the raw coordinates, the tablet event will return good stuff
|
||||
lcMine.lcOptions |= CXO_MESSAGES | CXO_CSRMESSAGES;
|
||||
lcMine.lcPktData = lcMine.lcMoveMask = PACKETDATA;
|
||||
lcMine.lcPktMode = PacketMode;
|
||||
lcMine.lcOutOrgX = 0;
|
||||
lcMine.lcOutExtX = lcMine.lcInExtX;
|
||||
lcMine.lcOutOrgY = 0;
|
||||
lcMine.lcOutExtY = -lcMine.lcInExtY;
|
||||
const HCTX context = QWindowsTabletSupport::m_winTab32DLL.wTOpen(window, &lcMine, true);
|
||||
if (!context) {
|
||||
if (QWindowsContext::verboseTablet)
|
||||
qWarning() << __FUNCTION__ << "Unable to open tablet.";
|
||||
DestroyWindow(window);
|
||||
return 0;
|
||||
|
||||
}
|
||||
// Set the size of the Packet Queue to the correct size
|
||||
const int currentQueueSize = QWindowsTabletSupport::m_winTab32DLL.wTQueueSizeGet(context);
|
||||
if (currentQueueSize != TabletPacketQSize) {
|
||||
if (!QWindowsTabletSupport::m_winTab32DLL.wTQueueSizeSet(context, TabletPacketQSize)) {
|
||||
if (!QWindowsTabletSupport::m_winTab32DLL.wTQueueSizeSet(context, currentQueueSize)) {
|
||||
qWarning() << "Unable to set queue size on tablet. The tablet will not work.";
|
||||
QWindowsTabletSupport::m_winTab32DLL.wTClose(context);
|
||||
DestroyWindow(window);
|
||||
return 0;
|
||||
} // cannot restore old size
|
||||
} // cannot set
|
||||
} // mismatch
|
||||
if (QWindowsContext::verboseTablet)
|
||||
qDebug("Opened tablet context %p on window %p, changed packet queue size %d -> %d",
|
||||
context, window, currentQueueSize, TabletPacketQSize);
|
||||
return new QWindowsTabletSupport(window, context);
|
||||
}
|
||||
|
||||
unsigned QWindowsTabletSupport::options() const
|
||||
{
|
||||
UINT result = 0;
|
||||
m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_CTXOPTIONS, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
QString QWindowsTabletSupport::description() const
|
||||
{
|
||||
const unsigned size = m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_WINTABID, 0);
|
||||
if (!size)
|
||||
return QString();
|
||||
QScopedPointer<TCHAR> winTabId(new TCHAR[size + 1]);
|
||||
m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_WINTABID, winTabId.data());
|
||||
WORD implementationVersion = 0;
|
||||
m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_IMPLVERSION, &implementationVersion);
|
||||
WORD specificationVersion = 0;
|
||||
m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_SPECVERSION, &specificationVersion);
|
||||
const unsigned opts = options();
|
||||
QString result = QString::fromLatin1("%1 specification: v%2.%3 implementation: v%4.%5 options: 0x%6")
|
||||
.arg(QString::fromWCharArray(winTabId.data()))
|
||||
.arg(specificationVersion >> 8).arg(specificationVersion & 0xFF)
|
||||
.arg(implementationVersion >> 8).arg(implementationVersion & 0xFF)
|
||||
.arg(opts, 0, 16);
|
||||
if (opts & CXO_MESSAGES)
|
||||
result += QStringLiteral(" CXO_MESSAGES");
|
||||
if (opts & CXO_CSRMESSAGES)
|
||||
result += QStringLiteral(" CXO_CSRMESSAGES");
|
||||
if (m_tiltSupport)
|
||||
result += QStringLiteral(" tilt");
|
||||
return result;
|
||||
}
|
||||
|
||||
void QWindowsTabletSupport::notifyActivate()
|
||||
{
|
||||
// Cooperate with other tablet applications, but when we get focus, I want to use the tablet.
|
||||
const bool result = QWindowsTabletSupport::m_winTab32DLL.wTEnable(m_context, true)
|
||||
&& QWindowsTabletSupport::m_winTab32DLL.wTOverlap(m_context, true);
|
||||
if (QWindowsContext::verboseTablet)
|
||||
qDebug() << __FUNCTION__ << result;
|
||||
}
|
||||
|
||||
static inline int indexOfDevice(const QVector<QWindowsTabletDeviceData> &devices, qint64 uniqueId)
|
||||
{
|
||||
for (int i = 0; i < devices.size(); ++i)
|
||||
if (devices.at(i).uniqueId == uniqueId)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline QTabletEvent::TabletDevice deviceType(const UINT cursorType)
|
||||
{
|
||||
if (((cursorType & 0x0006) == 0x0002) && ((cursorType & CursorTypeBitMask) != 0x0902))
|
||||
return QTabletEvent::Stylus;
|
||||
switch (cursorType & CursorTypeBitMask) {
|
||||
case 0x0802:
|
||||
return QTabletEvent::Stylus;
|
||||
case 0x0902:
|
||||
return QTabletEvent::Airbrush;
|
||||
case 0x0004:
|
||||
return QTabletEvent::FourDMouse;
|
||||
case 0x0006:
|
||||
return QTabletEvent::Puck;
|
||||
case 0x0804:
|
||||
return QTabletEvent::RotationStylus;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QTabletEvent::NoDevice;
|
||||
}
|
||||
|
||||
static inline QTabletEvent::PointerType pointerType(unsigned currentCursor)
|
||||
{
|
||||
switch (currentCursor % 3) { // %3 for dual track
|
||||
case 0:
|
||||
return QTabletEvent::Cursor;
|
||||
case 1:
|
||||
return QTabletEvent::Pen;
|
||||
case 2:
|
||||
return QTabletEvent::Eraser;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QTabletEvent::UnknownPointer;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug d, const QWindowsTabletDeviceData &t)
|
||||
{
|
||||
d << "TabletDevice id:" << t.uniqueId << " pressure: " << t.minPressure
|
||||
<< ".." << t.maxPressure << " tan pressure: " << t.minTanPressure << ".."
|
||||
<< t.maxTanPressure << " area:" << t.minX << t.minY <<t.minZ
|
||||
<< ".." << t.maxX << t.maxY << t.maxZ << " device " << t.currentDevice
|
||||
<< " pointer " << t.currentPointerType;
|
||||
return d;
|
||||
}
|
||||
|
||||
QWindowsTabletDeviceData QWindowsTabletSupport::tabletInit(const quint64 uniqueId, const UINT cursorType) const
|
||||
{
|
||||
QWindowsTabletDeviceData result;
|
||||
result.uniqueId = uniqueId;
|
||||
/* browse WinTab's many info items to discover pressure handling. */
|
||||
AXIS axis;
|
||||
LOGCONTEXT lc;
|
||||
/* get the current context for its device variable. */
|
||||
QWindowsTabletSupport::m_winTab32DLL.wTGet(m_context, &lc);
|
||||
/* get the size of the pressure axis. */
|
||||
QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES + lc.lcDevice, DVC_NPRESSURE, &axis);
|
||||
result.minPressure = int(axis.axMin);
|
||||
result.maxPressure = int(axis.axMax);
|
||||
|
||||
QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES + lc.lcDevice, DVC_TPRESSURE, &axis);
|
||||
result.minTanPressure = int(axis.axMin);
|
||||
result.maxTanPressure = int(axis.axMax);
|
||||
|
||||
LOGCONTEXT defaultLc;
|
||||
/* get default region */
|
||||
QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEFCONTEXT, 0, &defaultLc);
|
||||
result.maxX = int(defaultLc.lcInExtX) - int(defaultLc.lcInOrgX);
|
||||
result.maxY = int(defaultLc.lcInExtY) - int(defaultLc.lcInOrgY);
|
||||
result.maxZ = int(defaultLc.lcInExtZ) - int(defaultLc.lcInOrgZ);
|
||||
result.currentDevice = deviceType(cursorType);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, LPARAM lParam)
|
||||
{
|
||||
const bool enteredProximity = LOWORD(lParam) != 0;
|
||||
PACKET proximityBuffer[1]; // we are only interested in the first packet in this case
|
||||
const int totalPacks = QWindowsTabletSupport::m_winTab32DLL.wTPacketsGet(m_context, 1, proximityBuffer);
|
||||
if (!totalPacks)
|
||||
return false;
|
||||
const UINT currentCursor = proximityBuffer[0].pkCursor;
|
||||
UINT physicalCursorId;
|
||||
QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_PHYSID, &physicalCursorId);
|
||||
UINT cursorType;
|
||||
QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_TYPE, &cursorType);
|
||||
const qint64 uniqueId = (qint64(cursorType & DeviceIdMask) << 32L) | qint64(physicalCursorId);
|
||||
// initializing and updating the cursor should be done in response to
|
||||
// WT_CSRCHANGE. We do it in WT_PROXIMITY because some wintab never send
|
||||
// the event WT_CSRCHANGE even if asked with CXO_CSRMESSAGES
|
||||
m_currentDevice = indexOfDevice(m_devices, uniqueId);
|
||||
if (m_currentDevice < 0) {
|
||||
m_currentDevice = m_devices.size();
|
||||
m_devices.push_back(tabletInit(uniqueId, cursorType));
|
||||
}
|
||||
m_devices[m_currentDevice].currentPointerType = pointerType(currentCursor);
|
||||
if (QWindowsContext::verboseTablet)
|
||||
qDebug() << __FUNCTION__ << (enteredProximity ? "enter" : "leave")
|
||||
<< " proximity for device #"
|
||||
<< m_currentDevice << m_devices.at(m_currentDevice);
|
||||
if (enteredProximity) {
|
||||
QWindowSystemInterface::handleTabletEnterProximityEvent(m_devices.at(m_currentDevice).currentDevice,
|
||||
m_devices.at(m_currentDevice).currentPointerType,
|
||||
m_devices.at(m_currentDevice).uniqueId);
|
||||
} else {
|
||||
QWindowSystemInterface::handleTabletLeaveProximityEvent(m_devices.at(m_currentDevice).currentDevice,
|
||||
m_devices.at(m_currentDevice).currentPointerType,
|
||||
m_devices.at(m_currentDevice).uniqueId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QWindowsTabletSupport::translateTabletPacketEvent()
|
||||
{
|
||||
static PACKET localPacketBuf[TabletPacketQSize]; // our own tablet packet queue.
|
||||
const int packetCount = QWindowsTabletSupport::m_winTab32DLL.wTPacketsGet(m_context, TabletPacketQSize, &localPacketBuf);
|
||||
if (!packetCount || m_currentDevice < 0)
|
||||
return false;
|
||||
|
||||
const int currentDevice = m_devices.at(m_currentDevice).currentDevice;
|
||||
const int currentPointer = m_devices.at(m_currentDevice).currentPointerType;
|
||||
|
||||
// When entering proximity, the tablet driver snaps the mouse pointer to the
|
||||
// tablet position scaled to the virtual desktop and keeps it in sync.
|
||||
const QRect virtualDesktopArea = QGuiApplication::primaryScreen()->virtualGeometry();
|
||||
|
||||
if (QWindowsContext::verboseTablet)
|
||||
qDebug() << __FUNCTION__ << "processing " << packetCount
|
||||
<< "target:" << QGuiApplicationPrivate::tabletPressTarget;
|
||||
|
||||
const Qt::KeyboardModifiers keyboardModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
|
||||
|
||||
for (int i = 0; i < packetCount ; ++i) {
|
||||
const PACKET &packet = localPacketBuf[i];
|
||||
|
||||
const int z = currentDevice == QTabletEvent::FourDMouse ? int(packet.pkZ) : 0;
|
||||
const QPointF globalPosF = m_devices.at(m_currentDevice).scaleCoordinates(packet.pkX, packet.pkY, virtualDesktopArea);
|
||||
|
||||
QWindow *target = QGuiApplicationPrivate::tabletPressTarget; // Pass to window that grabbed it.
|
||||
const QPoint globalPos = globalPosF.toPoint();
|
||||
if (!target)
|
||||
if (QPlatformWindow *pw = QWindowsContext::instance()->findPlatformWindowAt(GetDesktopWindow(), globalPos, CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT))
|
||||
target = pw->window();
|
||||
if (!target)
|
||||
continue;
|
||||
|
||||
const QPoint localPos = target->mapFromGlobal(globalPos);
|
||||
|
||||
const qreal pressureNew = packet.pkButtons && (currentPointer == QTabletEvent::Pen || currentPointer == QTabletEvent::Eraser) ?
|
||||
m_devices.at(m_currentDevice).scalePressure(packet.pkNormalPressure) :
|
||||
qreal(0);
|
||||
const qreal tangentialPressure = currentDevice == QTabletEvent::Airbrush ?
|
||||
m_devices.at(m_currentDevice).scaleTangentialPressure(packet.pkTangentPressure) :
|
||||
qreal(0);
|
||||
|
||||
int tiltX = 0;
|
||||
int tiltY = 0;
|
||||
qreal rotation = 0;
|
||||
if (m_tiltSupport) {
|
||||
// Convert from azimuth and altitude to x tilt and y tilt. What
|
||||
// follows is the optimized version. Here are the equations used:
|
||||
// X = sin(azimuth) * cos(altitude)
|
||||
// Y = cos(azimuth) * cos(altitude)
|
||||
// Z = sin(altitude)
|
||||
// X Tilt = arctan(X / Z)
|
||||
// Y Tilt = arctan(Y / Z)
|
||||
const double radAzim = (packet.pkOrientation.orAzimuth / 10) * (M_PI / 180);
|
||||
const double tanAlt = tan((abs(packet.pkOrientation.orAltitude / 10)) * (M_PI / 180));
|
||||
|
||||
const double degX = atan(sin(radAzim) / tanAlt);
|
||||
const double degY = atan(cos(radAzim) / tanAlt);
|
||||
tiltX = int(degX * (180 / M_PI));
|
||||
tiltY = int(-degY * (180 / M_PI));
|
||||
rotation = packet.pkOrientation.orTwist;
|
||||
}
|
||||
|
||||
if (QWindowsContext::verboseTablet > 1) {
|
||||
qDebug()
|
||||
<< "Packet #" << i << '/' << packetCount << "button:" << packet.pkButtons
|
||||
<< globalPosF << z << "to:" << target << localPos << "(packet" << packet.pkX
|
||||
<< packet.pkY << ") dev:" << currentDevice << "pointer:"
|
||||
<< currentPointer << "P:" << pressureNew << "tilt:" << tiltX << ','
|
||||
<< tiltY << "tanP:" << tangentialPressure << "rotation:" << rotation;
|
||||
}
|
||||
|
||||
QWindowSystemInterface::handleTabletEvent(target, packet.pkButtons, localPos, globalPosF,
|
||||
currentDevice, currentPointer,
|
||||
pressureNew, tiltX, tiltY,
|
||||
tangentialPressure, rotation, z,
|
||||
m_devices.at(m_currentDevice).uniqueId,
|
||||
keyboardModifiers);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_NO_TABLETEVENT
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the plugins of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** 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, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, 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.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QWINDOWSTABLETSUPPORT_H
|
||||
#define QWINDOWSTABLETSUPPORT_H
|
||||
|
||||
#include "qtwindowsglobal.h"
|
||||
|
||||
#if !defined(QT_NO_TABLETEVENT) && !defined(Q_OS_WINCE)
|
||||
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QPointF>
|
||||
|
||||
#include <wintab.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QDebug;
|
||||
class QWindow;
|
||||
class QRect;
|
||||
|
||||
struct QWindowsWinTab32DLL
|
||||
{
|
||||
QWindowsWinTab32DLL() : wTOpen(0), wTClose(0), wTInfo(0), wTEnable(0), wTOverlap(0), wTPacketsGet(0), wTGet(0),
|
||||
wTQueueSizeGet(0), wTQueueSizeSet(0) {}
|
||||
|
||||
bool init();
|
||||
|
||||
typedef HCTX (API *PtrWTOpen)(HWND, LPLOGCONTEXT, BOOL);
|
||||
typedef BOOL (API *PtrWTClose)(HCTX);
|
||||
typedef UINT (API *PtrWTInfo)(UINT, UINT, LPVOID);
|
||||
typedef BOOL (API *PtrWTEnable)(HCTX, BOOL);
|
||||
typedef BOOL (API *PtrWTOverlap)(HCTX, BOOL);
|
||||
typedef int (API *PtrWTPacketsGet)(HCTX, int, LPVOID);
|
||||
typedef BOOL (API *PtrWTGet)(HCTX, LPLOGCONTEXT);
|
||||
typedef int (API *PtrWTQueueSizeGet)(HCTX);
|
||||
typedef BOOL (API *PtrWTQueueSizeSet)(HCTX, int);
|
||||
|
||||
PtrWTOpen wTOpen;
|
||||
PtrWTClose wTClose;
|
||||
PtrWTInfo wTInfo;
|
||||
PtrWTEnable wTEnable;
|
||||
PtrWTOverlap wTOverlap;
|
||||
PtrWTPacketsGet wTPacketsGet;
|
||||
PtrWTGet wTGet;
|
||||
PtrWTQueueSizeGet wTQueueSizeGet;
|
||||
PtrWTQueueSizeSet wTQueueSizeSet;
|
||||
};
|
||||
|
||||
struct QWindowsTabletDeviceData
|
||||
{
|
||||
QWindowsTabletDeviceData() : minPressure(0), maxPressure(0), minTanPressure(0),
|
||||
maxTanPressure(0), minX(0), maxX(0), minY(0), maxY(0), minZ(0), maxZ(0),
|
||||
uniqueId(0), currentDevice(0), currentPointerType(0) {}
|
||||
|
||||
QPointF scaleCoordinates(int coordX, int coordY,const QRect &targetArea) const;
|
||||
qreal scalePressure(qreal p) const { return p / qreal(maxPressure - minPressure); }
|
||||
qreal scaleTangentialPressure(qreal p) const { return p / qreal(maxTanPressure - minTanPressure); }
|
||||
|
||||
int minPressure;
|
||||
int maxPressure;
|
||||
int minTanPressure;
|
||||
int maxTanPressure;
|
||||
int minX, maxX, minY, maxY, minZ, maxZ;
|
||||
qint64 uniqueId;
|
||||
int currentDevice;
|
||||
int currentPointerType;
|
||||
};
|
||||
|
||||
QDebug operator<<(QDebug d, const QWindowsTabletDeviceData &t);
|
||||
|
||||
class QWindowsTabletSupport
|
||||
{
|
||||
Q_DISABLE_COPY(QWindowsTabletSupport)
|
||||
|
||||
explicit QWindowsTabletSupport(HWND window, HCTX context);
|
||||
|
||||
public:
|
||||
~QWindowsTabletSupport();
|
||||
|
||||
static QWindowsTabletSupport *create();
|
||||
|
||||
void notifyActivate();
|
||||
QString description() const;
|
||||
|
||||
bool translateTabletProximityEvent(WPARAM wParam, LPARAM lParam);
|
||||
bool translateTabletPacketEvent();
|
||||
|
||||
private:
|
||||
unsigned options() const;
|
||||
QWindowsTabletDeviceData tabletInit(const quint64 uniqueId, const UINT cursorType) const;
|
||||
|
||||
static QWindowsWinTab32DLL m_winTab32DLL;
|
||||
const HWND m_window;
|
||||
const HCTX m_context;
|
||||
bool m_tiltSupport;
|
||||
QVector<QWindowsTabletDeviceData> m_devices;
|
||||
int m_currentDevice;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // !QT_NO_TABLETEVENT && !Q_OS_WINCE
|
||||
#endif // QWINDOWSTABLETSUPPORT_H
|
||||
|
|
@ -102,6 +102,12 @@ contains(QT_CONFIG, opengles2) {
|
|||
}
|
||||
}
|
||||
|
||||
!wince*:!contains( DEFINES, QT_NO_TABLETEVENT ) {
|
||||
INCLUDEPATH += ../../../3rdparty/wintab
|
||||
HEADERS += qwindowstabletsupport.h
|
||||
SOURCES += qwindowstabletsupport.cpp
|
||||
}
|
||||
|
||||
contains(QT_CONFIG, freetype) {
|
||||
DEFINES *= QT_NO_FONTCONFIG
|
||||
QT_FREETYPE_DIR = $$QT_SOURCE_TREE/src/3rdparty/freetype
|
||||
|
|
|
|||
Loading…
Reference in New Issue