add buttons to QTabletEvent

Until now, it has been necessary for tablet-oriented applications which
care about multi-button styli to reject each tablet event and wait for
the mouse event in order to know which buttons are pressed.  This patch
adds the new API and also the X11/xcb implementation.

[ChangeLog][QtCore][Tablet support] Added buttons to QTabletEvent

Task-number: QTBUG-39458
Change-Id: If2c9ec1ceacc1700a82686c5fc6f568f9111055a
Reviewed-by: Frederik Gladhorn <frederik.gladhorn@digia.com>
bb10
Shawn Rutledge 2014-06-05 06:33:05 +02:00
parent 67410cd644
commit 6ad66140b1
13 changed files with 209 additions and 53 deletions

View File

@ -2046,6 +2046,66 @@ QVariant QInputMethodQueryEvent::value(Qt::InputMethodQuery query) const
\sa pointerType()
*/
/*!
Construct a tablet event of the given \a type.
The \a pos parameter indicates where the event occurred in the
widget; \a globalPos is the corresponding position in absolute
coordinates.
\a pressure contains the pressure exerted on the \a device.
\a pointerType describes the type of pen that is being used.
\a xTilt and \a yTilt contain the device's degree of tilt from the
x and y axes respectively.
\a keyState specifies which keyboard modifiers are pressed (e.g.,
\uicontrol{Ctrl}).
The \a uniqueID parameter contains the unique ID for the current device.
The \a z parameter contains the coordinate of the device on the tablet, this
is usually given by a wheel on 4D mouse. If the device does not support a
Z-axis, pass zero here.
The \a tangentialPressure parameter contins the tangential pressure of an air
brush. If the device does not support tangential pressure, pass 0 here.
\a rotation contains the device's rotation in degrees. 4D mice and the Wacom
Art Pen support rotation. If the device does not support rotation, pass 0 here.
The \a button that caused the event is given as a value from the
\l Qt::MouseButton enum. If the event \a type is not \l TabletPress or
\l TabletRelease, the appropriate button for this event is \l Qt::NoButton.
\a buttons is the state of all buttons at the time of the event.
\sa pos(), globalPos(), device(), pressure(), xTilt(), yTilt(), uniqueId(), rotation(),
tangentialPressure(), z()
*/
QTabletEvent::QTabletEvent(Type type, const QPointF &pos, const QPointF &globalPos,
int device, int pointerType,
qreal pressure, int xTilt, int yTilt, qreal tangentialPressure,
qreal rotation, int z, Qt::KeyboardModifiers keyState, qint64 uniqueID,
Qt::MouseButton button, Qt::MouseButtons buttons)
: QInputEvent(type, keyState),
mPos(pos),
mGPos(globalPos),
mDev(device),
mPointerType(pointerType),
mXT(xTilt),
mYT(yTilt),
mZ(z),
mPress(pressure),
mTangential(tangentialPressure),
mRot(rotation),
mUnique(uniqueID),
mExtra(new QTabletEventPrivate(button, buttons))
{
}
/*!
Construct a tablet event of the given \a type.
@ -2077,6 +2137,8 @@ QVariant QInputMethodQueryEvent::value(Qt::InputMethodQuery query) const
\sa pos(), globalPos(), device(), pressure(), xTilt(), yTilt(), uniqueId(), rotation(),
tangentialPressure(), z()
\deprecated in 5.4: use the constructor with MouseButton status
*/
QTabletEvent::QTabletEvent(Type type, const QPointF &pos, const QPointF &globalPos,
@ -2095,7 +2157,7 @@ QTabletEvent::QTabletEvent(Type type, const QPointF &pos, const QPointF &globalP
mTangential(tangentialPressure),
mRot(rotation),
mUnique(uniqueID),
mExtra(0)
mExtra(new QTabletEventPrivate(Qt::NoButton, Qt::NoButton))
{
}
@ -2106,6 +2168,34 @@ QTabletEvent::~QTabletEvent()
{
}
/*!
Returns the button that caused the event.
Note that the returned value is always Qt::NoButton for \l TabletMove,
\l TabletEnterProximity and \l TabletLeaveProximity events.
\sa buttons(), Qt::MouseButton
*/
Qt::MouseButton QTabletEvent::button() const
{
return static_cast<QTabletEventPrivate *>(mExtra)->b;
}
/*!
Returns the button state when the event was generated. The button state is
a combination of buttons from the \l Qt::MouseButton enum using the OR
operator. For \l TabletMove events, this is all buttons that are pressed
down. For \l TabletPress events this includes the button that caused the
event. For \l TabletRelease events this excludes the button that caused the
event.
\sa button(), Qt::MouseButton
*/
Qt::MouseButtons QTabletEvent::buttons() const
{
return static_cast<QTabletEventPrivate *>(mExtra)->buttonState;
}
/*!
\fn TabletDevices QTabletEvent::device() const

View File

@ -230,7 +230,12 @@ public:
QTabletEvent(Type t, const QPointF &pos, const QPointF &globalPos,
int device, int pointerType, qreal pressure, int xTilt, int yTilt,
qreal tangentialPressure, qreal rotation, int z,
Qt::KeyboardModifiers keyState, qint64 uniqueID);
Qt::KeyboardModifiers keyState, qint64 uniqueID); // ### remove in Qt 6
QTabletEvent(Type t, const QPointF &pos, const QPointF &globalPos,
int device, int pointerType, qreal pressure, int xTilt, int yTilt,
qreal tangentialPressure, qreal rotation, int z,
Qt::KeyboardModifiers keyState, qint64 uniqueID,
Qt::MouseButton button, Qt::MouseButtons buttons);
~QTabletEvent();
inline QPoint pos() const { return mPos.toPoint(); }
@ -257,6 +262,8 @@ public:
inline qreal rotation() const { return mRot; }
inline int xTilt() const { return mXT; }
inline int yTilt() const { return mYT; }
Qt::MouseButton button() const;
Qt::MouseButtons buttons() const;
protected:
QPointF mPos, mGPos;
@ -264,9 +271,8 @@ protected:
qreal mPress, mTangential, mRot;
qint64 mUnique;
// I don't know what the future holds for tablets but there could be some
// new devices coming along, and there seem to be "holes" in the
// OS-specific events for this.
// QTabletEventPrivate for extra storage.
// ### Qt 6: QPointingEvent will have Buttons, QTabletEvent will inherit
void *mExtra;
};
#endif // QT_NO_TABLETEVENT

View File

@ -92,6 +92,20 @@ public:
QVector<QPointF> rawScreenPositions;
};
#ifndef QT_NO_TABLETEVENT
class QTabletEventPrivate
{
public:
inline QTabletEventPrivate(Qt::MouseButton button, Qt::MouseButtons buttons)
: b(button),
buttonState(buttons)
{ }
Qt::MouseButton b;
Qt::MouseButtons buttonState;
};
#endif // QT_NO_TABLETEVENT
QT_END_NAMESPACE
#endif // QEVENT_P_H

View File

@ -114,7 +114,7 @@ Qt::KeyboardModifiers QGuiApplicationPrivate::modifier_buttons = Qt::NoModifier;
QPointF QGuiApplicationPrivate::lastCursorPosition(qInf(), qInf());
bool QGuiApplicationPrivate::tabletState = false;
Qt::MouseButtons QGuiApplicationPrivate::tabletState = Qt::NoButton;
QWindow *QGuiApplicationPrivate::tabletPressTarget = 0;
QWindow *QGuiApplicationPrivate::currentMouseWindow = 0;
@ -2067,10 +2067,8 @@ void QGuiApplicationPrivate::processTabletEvent(QWindowSystemInterfacePrivate::T
{
#ifndef QT_NO_TABLETEVENT
QEvent::Type type = QEvent::TabletMove;
if (e->down != tabletState) {
type = e->down ? QEvent::TabletPress : QEvent::TabletRelease;
tabletState = e->down;
}
if (e->buttons != tabletState)
type = (e->buttons > tabletState) ? QEvent::TabletPress : QEvent::TabletRelease;
QWindow *window = e->window.data();
modifier_buttons = e->modifiers;
@ -2102,12 +2100,21 @@ void QGuiApplicationPrivate::processTabletEvent(QWindowSystemInterfacePrivate::T
QPointF delta = e->global - e->global.toPoint();
local = window->mapFromGlobal(e->global.toPoint()) + delta;
}
Qt::MouseButtons stateChange = e->buttons ^ tabletState;
Qt::MouseButton button = Qt::NoButton;
for (int check = Qt::LeftButton; check <= int(Qt::MaxMouseButton); check = check << 1) {
if (check & stateChange) {
button = Qt::MouseButton(check);
break;
}
}
QTabletEvent ev(type, local, e->global,
e->device, e->pointerType, e->pressure, e->xTilt, e->yTilt,
e->tangentialPressure, e->rotation, e->z,
e->modifiers, e->uid);
e->modifiers, e->uid, button, e->buttons);
ev.setTimestamp(e->timestamp);
QGuiApplication::sendSpontaneousEvent(window, &ev);
tabletState = e->buttons;
#else
Q_UNUSED(e)
#endif
@ -2119,7 +2126,7 @@ void QGuiApplicationPrivate::processTabletEnterProximityEvent(QWindowSystemInter
QTabletEvent ev(QEvent::TabletEnterProximity, QPointF(), QPointF(),
e->device, e->pointerType, 0, 0, 0,
0, 0, 0,
Qt::NoModifier, e->uid);
Qt::NoModifier, e->uid, Qt::NoButton, tabletState);
ev.setTimestamp(e->timestamp);
QGuiApplication::sendSpontaneousEvent(qGuiApp, &ev);
#else
@ -2133,7 +2140,7 @@ void QGuiApplicationPrivate::processTabletLeaveProximityEvent(QWindowSystemInter
QTabletEvent ev(QEvent::TabletLeaveProximity, QPointF(), QPointF(),
e->device, e->pointerType, 0, 0, 0,
0, 0, 0,
Qt::NoModifier, e->uid);
Qt::NoModifier, e->uid, Qt::NoButton, tabletState);
ev.setTimestamp(e->timestamp);
QGuiApplication::sendSpontaneousEvent(qGuiApp, &ev);
#else

View File

@ -207,7 +207,7 @@ public:
static int mousePressY;
static int mouse_double_click_distance;
static QPointF lastCursorPosition;
static bool tabletState;
static Qt::MouseButtons tabletState;
static QWindow *tabletPressTarget;
static QWindow *currentMouseWindow;
static Qt::ApplicationState applicationState;

View File

@ -629,15 +629,34 @@ void QWindowSystemInterface::handleFileOpenEvent(const QUrl &url)
QGuiApplicationPrivate::processWindowSystemEvent(&e);
}
void QWindowSystemInterface::handleTabletEvent(QWindow *w, ulong timestamp, const QPointF &local, const QPointF &global,
int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt,
qreal tangentialPressure, qreal rotation, int z, qint64 uid,
Qt::KeyboardModifiers modifiers)
{
QWindowSystemInterfacePrivate::TabletEvent *e =
new QWindowSystemInterfacePrivate::TabletEvent(w, timestamp, local, global, device, pointerType, buttons, pressure,
xTilt, yTilt, tangentialPressure, rotation, z, uid, modifiers);
QWindowSystemInterfacePrivate::handleWindowSystemEvent(e);
}
void QWindowSystemInterface::handleTabletEvent(QWindow *w, const QPointF &local, const QPointF &global,
int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt,
qreal tangentialPressure, qreal rotation, int z, qint64 uid,
Qt::KeyboardModifiers modifiers)
{
ulong time = QWindowSystemInterfacePrivate::eventTime.elapsed();
handleTabletEvent(w, time, local, global, device, pointerType, buttons, pressure,
xTilt, yTilt, tangentialPressure, rotation, z, uid, modifiers);
}
void QWindowSystemInterface::handleTabletEvent(QWindow *w, ulong timestamp, bool down, const QPointF &local, const QPointF &global,
int device, int pointerType, qreal pressure, int xTilt, int yTilt,
qreal tangentialPressure, qreal rotation, int z, qint64 uid,
Qt::KeyboardModifiers modifiers)
{
QWindowSystemInterfacePrivate::TabletEvent *e =
new QWindowSystemInterfacePrivate::TabletEvent(w, timestamp, down, local, global, device, pointerType, pressure,
xTilt, yTilt, tangentialPressure, rotation, z, uid, modifiers);
QWindowSystemInterfacePrivate::handleWindowSystemEvent(e);
handleTabletEvent(w, timestamp, local, global, device, pointerType, (down ? Qt::LeftButton : Qt::NoButton), pressure,
xTilt, yTilt, tangentialPressure, rotation, z, uid, modifiers);
}
void QWindowSystemInterface::handleTabletEvent(QWindow *w, bool down, const QPointF &local, const QPointF &global,
@ -645,8 +664,7 @@ void QWindowSystemInterface::handleTabletEvent(QWindow *w, bool down, const QPoi
qreal tangentialPressure, qreal rotation, int z, qint64 uid,
Qt::KeyboardModifiers modifiers)
{
ulong time = QWindowSystemInterfacePrivate::eventTime.elapsed();
handleTabletEvent(w, time, down, local, global, device, pointerType, pressure,
handleTabletEvent(w, local, global, device, pointerType, (down ? Qt::LeftButton : Qt::NoButton), pressure,
xTilt, yTilt, tangentialPressure, rotation, z, uid, modifiers);
}

View File

@ -172,14 +172,22 @@ public:
static void handleFileOpenEvent(const QString& fileName);
static void handleFileOpenEvent(const QUrl &url);
static void handleTabletEvent(QWindow *w, ulong timestamp, const QPointF &local, const QPointF &global,
int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt,
qreal tangentialPressure, qreal rotation, int z, qint64 uid,
Qt::KeyboardModifiers modifiers = Qt::NoModifier);
static void handleTabletEvent(QWindow *w, const QPointF &local, const QPointF &global,
int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt,
qreal tangentialPressure, qreal rotation, int z, qint64 uid,
Qt::KeyboardModifiers modifiers = Qt::NoModifier);
static void handleTabletEvent(QWindow *w, ulong timestamp, bool down, const QPointF &local, const QPointF &global,
int device, int pointerType, qreal pressure, int xTilt, int yTilt,
qreal tangentialPressure, qreal rotation, int z, qint64 uid,
Qt::KeyboardModifiers modifiers = Qt::NoModifier);
Qt::KeyboardModifiers modifiers = Qt::NoModifier); // ### remove in Qt 6
static void handleTabletEvent(QWindow *w, bool down, const QPointF &local, const QPointF &global,
int device, int pointerType, qreal pressure, int xTilt, int yTilt,
qreal tangentialPressure, qreal rotation, int z, qint64 uid,
Qt::KeyboardModifiers modifiers = Qt::NoModifier);
Qt::KeyboardModifiers modifiers = Qt::NoModifier); // ### remove in Qt 6
static void handleTabletEnterProximityEvent(ulong timestamp, int device, int pointerType, qint64 uid);
static void handleTabletEnterProximityEvent(int device, int pointerType, qint64 uid);
static void handleTabletLeaveProximityEvent(ulong timestamp, int device, int pointerType, qint64 uid);

View File

@ -338,19 +338,19 @@ public:
class TabletEvent : public InputEvent {
public:
static void handleTabletEvent(QWindow *w, bool down, const QPointF &local, const QPointF &global,
int device, int pointerType, qreal pressure, int xTilt, int yTilt,
static void handleTabletEvent(QWindow *w, const QPointF &local, const QPointF &global,
int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt,
qreal tangentialPressure, qreal rotation, int z, qint64 uid,
Qt::KeyboardModifiers modifiers = Qt::NoModifier);
TabletEvent(QWindow *w, ulong time, bool down, const QPointF &local, const QPointF &global,
int device, int pointerType, qreal pressure, int xTilt, int yTilt, qreal tpressure,
TabletEvent(QWindow *w, ulong time, const QPointF &local, const QPointF &global,
int device, int pointerType, Qt::MouseButtons b, qreal pressure, int xTilt, int yTilt, qreal tpressure,
qreal rotation, int z, qint64 uid, Qt::KeyboardModifiers mods)
: InputEvent(w, time, Tablet, mods),
down(down), local(local), global(global), device(device), pointerType(pointerType),
buttons(b), local(local), global(global), device(device), pointerType(pointerType),
pressure(pressure), xTilt(xTilt), yTilt(yTilt), tangentialPressure(tpressure),
rotation(rotation), z(z), uid(uid) { }
bool down;
Qt::MouseButtons buttons;
QPointF local;
QPointF global;
int device;

View File

@ -479,8 +479,9 @@ bool QWindowsTabletSupport::translateTabletPacketEvent()
<< tiltY << "tanP:" << tangentialPressure << "rotation:" << rotation;
}
QWindowSystemInterface::handleTabletEvent(target, packet.pkButtons, localPos, globalPosF,
QWindowSystemInterface::handleTabletEvent(target, QPointF(localPos), globalPosF,
currentDevice, currentPointer,
static_cast<Qt::MouseButtons>(packet.pkButtons),
pressureNew, tiltX, tiltY,
tangentialPressure, rotation, z,
m_devices.at(m_currentDevice).uniqueId,

View File

@ -509,11 +509,11 @@ private:
#ifndef QT_NO_TABLETEVENT
struct TabletData {
TabletData() : deviceId(0), pointerType(QTabletEvent::UnknownPointer),
tool(QTabletEvent::Stylus), down(false), serialId(0), inProximity(false) { }
tool(QTabletEvent::Stylus), buttons(0), serialId(0), inProximity(false) { }
int deviceId;
QTabletEvent::PointerType pointerType;
QTabletEvent::TabletDevice tool;
bool down;
Qt::MouseButtons buttons;
qint64 serialId;
bool inProximity;
struct ValuatorClassInfo {

View File

@ -685,6 +685,19 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin
#endif // XCB_USE_XINPUT21
}
static Qt::MouseButton xiToQtMouseButton(uint32_t b) {
switch (b) {
case 1: return Qt::LeftButton;
case 2: return Qt::MiddleButton;
case 3: return Qt::RightButton;
// 4-7 are for scrolling
default: break;
}
if (b >= 8 && b <= Qt::MaxMouseButton)
return static_cast<Qt::MouseButton>(Qt::BackButton << (b - 8));
return Qt::NoButton;
}
static QTabletEvent::TabletDevice toolIdToTabletDevice(quint32 toolId) {
// keep in sync with wacom_intuos_inout() in Linux kernel driver wacom_wac.c
switch (toolId) {
@ -725,24 +738,22 @@ bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData)
Display *xDisplay = static_cast<Display *>(m_xlib_display);
xXIGenericDeviceEvent *xiEvent = static_cast<xXIGenericDeviceEvent *>(event);
switch (xiEvent->evtype) {
case XI_ButtonPress: // stylus down
if (reinterpret_cast<xXIDeviceEvent *>(event)->detail == 1) { // ignore the physical buttons on the stylus
tabletData->down = true;
xi2ReportTabletEvent(*tabletData, xiEvent);
} else
handled = false;
case XI_ButtonPress: {
Qt::MouseButton b = xiToQtMouseButton(reinterpret_cast<xXIDeviceEvent *>(event)->detail);
tabletData->buttons |= b;
xi2ReportTabletEvent(*tabletData, xiEvent);
break;
case XI_ButtonRelease: // stylus up
if (reinterpret_cast<xXIDeviceEvent *>(event)->detail == 1) {
tabletData->down = false;
xi2ReportTabletEvent(*tabletData, xiEvent);
} else
handled = false;
}
case XI_ButtonRelease: {
Qt::MouseButton b = xiToQtMouseButton(reinterpret_cast<xXIDeviceEvent *>(event)->detail);
tabletData->buttons ^= b;
xi2ReportTabletEvent(*tabletData, xiEvent);
break;
}
case XI_Motion:
// Report TabletMove only when the stylus is touching the tablet.
// No possibility to report proximity motion (no suitable Qt event exists yet).
if (tabletData->down)
// Report TabletMove only when the stylus is touching the tablet or any button is pressed.
// TODO: report proximity (hover) motion (no suitable Qt event exists yet).
if (tabletData->buttons != Qt::NoButton)
xi2ReportTabletEvent(*tabletData, xiEvent);
break;
case XI_PropertyEvent: {
@ -862,15 +873,16 @@ void QXcbConnection::xi2ReportTabletEvent(TabletData &tabletData, void *event)
}
if (Q_UNLIKELY(debug_xinput))
qDebug("XI2 event on tablet %d with tool %d type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f pressure %4.2lf tilt %d, %d rotation %6.2lf",
ev->deviceid, tabletData.tool, ev->type, ev->sequenceNumber, ev->detail,
qDebug("XI2 event on tablet %d with tool %d type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f buttons 0x%x pressure %4.2lf tilt %d, %d rotation %6.2lf",
ev->deviceid, tabletData.tool, ev->evtype, ev->sequenceNumber, ev->detail,
fixed1616ToReal(ev->event_x), fixed1616ToReal(ev->event_y),
fixed1616ToReal(ev->root_x), fixed1616ToReal(ev->root_y),
pressure, xTilt, yTilt, rotation);
(int)tabletData.buttons, pressure, xTilt, yTilt, rotation);
QWindowSystemInterface::handleTabletEvent(window, tabletData.down, local, global,
QWindowSystemInterface::handleTabletEvent(window, local, global,
tabletData.tool, tabletData.pointerType,
pressure, xTilt, yTilt, tangentialPressure,
tabletData.buttons, pressure,
xTilt, yTilt, tangentialPressure,
rotation, 0, tabletData.serialId);
}
#endif // QT_NO_TABLETEVENT

View File

@ -3411,7 +3411,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e)
tablet->device(), tablet->pointerType(),
tablet->pressure(), tablet->xTilt(), tablet->yTilt(),
tablet->tangentialPressure(), tablet->rotation(), tablet->z(),
tablet->modifiers(), tablet->uniqueId());
tablet->modifiers(), tablet->uniqueId(), tablet->button(), tablet->buttons());
te.spont = e->spontaneous();
res = d->notify_helper(w, w == receiver ? tablet : &te);
eventAccepted = ((w == receiver) ? tablet : &te)->isAccepted();

View File

@ -795,7 +795,7 @@ void QWidgetWindow::handleTabletEvent(QTabletEvent *event)
QPointF mapped = qt_tablet_target->mapFromGlobal(event->globalPos()) + delta;
QTabletEvent ev(event->type(), mapped, event->globalPosF(), event->device(), event->pointerType(),
event->pressure(), event->xTilt(), event->yTilt(), event->tangentialPressure(),
event->rotation(), event->z(), event->modifiers(), event->uniqueId());
event->rotation(), event->z(), event->modifiers(), event->uniqueId(), event->button(), event->buttons());
ev.setTimestamp(event->timestamp());
QGuiApplication::sendSpontaneousEvent(qt_tablet_target, &ev);
}