blob: 287ba931d9b1da3534159f7fae6331fe5fb720f6 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qwindowstabletsupport.h"
#include "qwindowscontext.h"
#include "qwindowskeymapper.h"
#include "qwindowswindow.h"
#include "qwindowsscreen.h"
#include <qpa/qwindowsysteminterface.h>
#include <QtGui/qevent.h>
#include <QtGui/qscreen.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qwindow.h>
#include <QtCore/qdebug.h>
#include <QtCore/qvarlengtharray.h>
#include <QtCore/qmath.h>
#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 | PK_TIME)
#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 {x, y};
}
template <class Stream>
static void formatOptions(Stream &str, unsigned options)
{
if (options & CXO_SYSTEM)
str << " CXO_SYSTEM";
if (options & CXO_PEN)
str << " CXO_PEN";
if (options & CXO_MESSAGES)
str << " CXO_MESSAGES";
if (options & CXO_MARGIN)
str << " CXO_MARGIN";
if (options & CXO_MGNINSIDE)
str << " CXO_MGNINSIDE";
if (options & CXO_CSRMESSAGES)
str << " CXO_CSRMESSAGES";
}
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug d, const QWindowsTabletDeviceData &t)
{
QDebugStateSaver saver(d);
d.nospace();
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;
}
QDebug operator<<(QDebug d, const LOGCONTEXT &lc)
{
QDebugStateSaver saver(d);
d.nospace();
d << "LOGCONTEXT(\"" << QString::fromWCharArray(lc.lcName) << "\", options=0x"
<< Qt::hex << lc.lcOptions << Qt::dec;
formatOptions(d, lc.lcOptions);
d << ", status=0x" << Qt::hex << lc.lcStatus << ", device=0x" << lc.lcDevice
<< Qt::dec << ", PktRate=" << lc.lcPktRate
<< ", PktData=" << lc.lcPktData << ", PktMode=" << lc.lcPktMode
<< ", MoveMask=0x" << Qt::hex << lc.lcMoveMask << ", BtnDnMask=0x" << lc.lcBtnDnMask
<< ", BtnUpMask=0x" << lc.lcBtnUpMask << Qt::dec << ", SysMode=" << lc.lcSysMode
<< ", InOrg=(" << lc.lcInOrgX << ", " << lc.lcInOrgY << ", " << lc.lcInOrgZ
<< "), InExt=(" << lc.lcInExtX << ", " << lc.lcInExtY << ", " << lc.lcInExtZ
<< ") OutOrg=(" << lc.lcOutOrgX << ", " << lc.lcOutOrgY << ", "
<< lc.lcOutOrgZ << "), OutExt=(" << lc.lcOutExtX << ", " << lc.lcOutExtY
<< ", " << lc.lcOutExtZ
<< "), Sens=(" << lc.lcSensX << ", " << lc.lcSensX << ", " << lc.lcSensZ
<< ") SysOrg=(" << lc.lcSysOrgX << ", " << lc.lcSysOrgY
<< "), SysExt=(" << lc.lcSysExtX << ", " << lc.lcSysExtY
<< "), SysSens=(" << lc.lcSysSensX << ", " << lc.lcSysSensY << "))";
return d;
}
#endif // !QT_NO_DEBUG_STREAM
QWindowsWinTab32DLL QWindowsTabletSupport::m_winTab32DLL;
/*!
\class QWindowsWinTab32DLL QWindowsTabletSupport
\brief Functions from wintabl32.dll shipped with WACOM tablets used by QWindowsTabletSupport.
\internal
*/
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
*/
QWindowsTabletSupport::QWindowsTabletSupport(HWND window, HCTX context)
: m_window(window)
, m_context(context)
{
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 nullptr;
const HWND window = QWindowsContext::instance()->createDummyWindow(QStringLiteral("TabletDummyWindow"),
L"TabletDummyWindow",
qWindowsTabletSupportWndProc);
if (!window) {
qCWarning(lcQpaTablet) << __FUNCTION__ << "Unable to create window for tablet.";
return nullptr;
}
LOGCONTEXT lcMine;
// build our context from the default context
QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEFSYSCTX, 0, &lcMine);
qCDebug(lcQpaTablet) << "Default: " << 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;
qCDebug(lcQpaTablet) << "Requesting: " << lcMine;
const HCTX context = QWindowsTabletSupport::m_winTab32DLL.wTOpen(window, &lcMine, true);
if (!context) {
qCDebug(lcQpaTablet) << __FUNCTION__ << "Unable to open tablet.";
DestroyWindow(window);
return nullptr;
}
// 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 nullptr;
} // cannot restore old size
} // cannot set
} // mismatch
qCDebug(lcQpaTablet) << "Opened tablet context " << context << " on window "
<< window << "changed packet queue size " << currentQueueSize
<< "->" << TabletPacketQSize << "\nobtained: " << lcMine;
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, nullptr);
if (!size)
return QString();
QVarLengthArray<TCHAR> winTabId(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();
WORD devices = 0;
m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_NDEVICES, &devices);
WORD cursors = 0;
m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_NCURSORS, &cursors);
WORD extensions = 0;
m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_NEXTENSIONS, &extensions);
QString result;
QTextStream str(&result);
str << '"' << QString::fromWCharArray(winTabId.data())
<< "\" specification: v" << (specificationVersion >> 8)
<< '.' << (specificationVersion & 0xFF) << " implementation: v"
<< (implementationVersion >> 8) << '.' << (implementationVersion & 0xFF)
<< ' ' << devices << " device(s), " << cursors << " cursor(s), "
<< extensions << " extensions" << ", options: 0x" << Qt::hex << opts << Qt::dec;
formatOptions(str, opts);
if (m_tiltSupport)
str << " 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);
qCDebug(lcQpaTablet) << __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;
if (cursorType == 0x4020) // Surface Pro 2 tablet device
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;
}
QWindowsTabletDeviceData QWindowsTabletSupport::tabletInit(qint64 uniqueId, 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)
{
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 (!LOWORD(lParam)) {
qCDebug(lcQpaTablet) << "leave proximity for device #" << m_currentDevice;
if (m_currentDevice < 0 || m_currentDevice >= m_devices.size()) // QTBUG-65120, spurious leave observed
return false;
m_state = PenUp;
if (totalPacks > 0) {
QWindowSystemInterface::handleTabletLeaveProximityEvent(proximityBuffer[0].pkTime,
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;
}
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));
}
/**
* We should check button map for changes on every proximity event, not
* only during initialization phase.
*
* WARNING: in 2016 there were some Wacom table drivers, which could mess up
* button mapping if the remapped button was pressed, while the
* application **didn't have input focus**. This bug is somehow
* related to the fact that Wacom drivers allow user to configure
* per-application button-mappings. If the bug shows up again,
* just move this button-map fetching into initialization block.
*
* See https://bugs.kde.org/show_bug.cgi?id=359561
*/
BYTE logicalButtons[32];
memset(logicalButtons, 0, 32);
m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_SYSBTNMAP, &logicalButtons);
m_devices[m_currentDevice].buttonsMap[0x1] = logicalButtons[0];
m_devices[m_currentDevice].buttonsMap[0x2] = logicalButtons[1];
m_devices[m_currentDevice].buttonsMap[0x4] = logicalButtons[2];
m_devices[m_currentDevice].currentPointerType = pointerType(currentCursor);
m_state = PenProximity;
qCDebug(lcQpaTablet) << "enter proximity for device #"
<< m_currentDevice << m_devices.at(m_currentDevice);
QWindowSystemInterface::handleTabletEnterProximityEvent(proximityBuffer[0].pkTime,
m_devices.at(m_currentDevice).currentDevice,
m_devices.at(m_currentDevice).currentPointerType,
m_devices.at(m_currentDevice).uniqueId);
return true;
}
Qt::MouseButton buttonValueToEnum(DWORD button,
const QWindowsTabletDeviceData &tdd) {
enum : unsigned {
leftButtonValue = 0x1,
middleButtonValue = 0x2,
rightButtonValue = 0x4,
doubleClickButtonValue = 0x7
};
button = tdd.buttonsMap.value(button);
return button == leftButtonValue ? Qt::LeftButton :
button == rightButtonValue ? Qt::RightButton :
button == doubleClickButtonValue ? Qt::MiddleButton :
button == middleButtonValue ? Qt::MiddleButton :
button ? Qt::LeftButton /* fallback item */ :
Qt::NoButton;
}
Qt::MouseButtons convertTabletButtons(DWORD btnNew,
const QWindowsTabletDeviceData &tdd) {
Qt::MouseButtons buttons = Qt::NoButton;
for (unsigned int i = 0; i < 3; i++) {
unsigned int btn = 0x1 << i;
if (btn & btnNew) {
Qt::MouseButton convertedButton =
buttonValueToEnum(btn, tdd);
buttons |= convertedButton;
/**
* If a button that is present in hardware input is
* mapped to a Qt::NoButton, it means that it is going
* to be eaten by the driver, for example by its
* "Pan/Scroll" feature. Therefore we shouldn't handle
* any of the events associated to it. We'll just return
* Qt::NoButtons here.
*/
}
}
return buttons;
}
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;
const qint64 uniqueId = m_devices.at(m_currentDevice).uniqueId;
// The tablet can be used in 2 different modes (reflected in enum Mode),
// depending on its settings:
// 1) Absolute (pen) mode:
// The coordinates are scaled to the virtual desktop (by default). The user
// can also choose to scale to the monitor or a region of the screen.
// When entering proximity, the tablet driver snaps the mouse pointer to the
// tablet position scaled to that area and keeps it in sync.
// 2) Relative (mouse) mode:
// The pen follows the mouse. The constant 'absoluteRange' specifies the
// manhattanLength difference for detecting if a tablet input device is in this mode,
// in which case we snap the position to the mouse position.
// It seems there is no way to find out the mode programmatically, the LOGCONTEXT orgX/Y/Ext
// area is always the virtual desktop.
const QRect virtualDesktopArea =
QWindowsScreen::virtualGeometry(QGuiApplication::primaryScreen()->handle());
if (QWindowsContext::verbose > 1) {
qCDebug(lcQpaTablet) << __FUNCTION__ << "processing" << packetCount
<< "mode=" << m_mode << "target:"
<< QGuiApplicationPrivate::tabletDevicePoint(uniqueId).target;
}
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;
QPointF globalPosF =
m_devices.at(m_currentDevice).scaleCoordinates(packet.pkX, packet.pkY, virtualDesktopArea);
QWindow *target = QGuiApplicationPrivate::tabletDevicePoint(uniqueId).target; // Pass to window that grabbed it.
// Get Mouse Position and compare to tablet info
const QPoint mouseLocation = QWindowsCursor::mousePosition();
if (m_state == PenProximity) {
m_state = PenDown;
m_mode = (mouseLocation - globalPosF).manhattanLength() > m_absoluteRange
? MouseMode : PenMode;
qCDebug(lcQpaTablet) << __FUNCTION__ << "mode=" << m_mode << "pen:"
<< globalPosF << "mouse:" << mouseLocation;
}
if (m_mode == MouseMode)
globalPosF = mouseLocation;
const QPoint globalPos = globalPosF.toPoint();
if (!target)
target = QWindowsScreen::windowAt(globalPos, CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT);
if (!target)
continue;
const QPlatformWindow *platformWindow = target->handle();
Q_ASSERT(platformWindow);
const QPoint localPos = platformWindow->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 = qDegreesToRadians(packet.pkOrientation.orAzimuth / 10.0);
const double tanAlt = std::tan(qDegreesToRadians(std::abs(packet.pkOrientation.orAltitude / 10.0)));
const double radX = std::atan(std::sin(radAzim) / tanAlt);
const double radY = std::atan(std::cos(radAzim) / tanAlt);
tiltX = int(qRadiansToDegrees(radX));
tiltY = int(qRadiansToDegrees(-radY));
rotation = 360.0 - (packet.pkOrientation.orTwist / 10.0);
if (rotation > 180.0)
rotation -= 360.0;
}
if (QWindowsContext::verbose > 1) {
qCDebug(lcQpaTablet)
<< "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;
}
Qt::MouseButtons buttons =
convertTabletButtons(packet.pkButtons, m_devices.at(m_currentDevice));
QWindowSystemInterface::handleTabletEvent(target, packet.pkTime, QPointF(localPos), globalPosF,
currentDevice, currentPointer,
buttons,
pressureNew, tiltX, tiltY,
tangentialPressure, rotation, z,
uniqueId,
keyboardModifiers);
}
return true;
}
QT_END_NAMESPACE