xcb: Use XIGrabDevice instead of xcb_grab_pointer with XI 2.2
Switch to using the pointer events from XI2 when touch is available (i.e. version is >= 2.2). This allows us to select and grab the button and motion events together with the touch ones. This prevents the issue of not getting touch events when grabbing via the plain xcb functions. To prevent touch sequences from being replayed after ungrabbing (for example after dismissing a popup that caused a grab), we try to accept touches via XIAllowTouchEvents. Unfortunately this leads to a deadlock and therefore we can only do it when we know we have a new enough libXi. This is a configure time check which is not ideal since the system on which apps run can have a newer libXi than the machine that did the Qt build, but seems like the best we can do. The environment variable QT_XCB_NO_XI2_MOUSE can be set to 1 in order to prevent processing mouse events through XInput. This restores the old behavior with broken grabbing. [ChangeLog][QtGui] Pointer event delivery on X11 is now done via XInput 2.2+ when available. Done-with: Michal Klocek <michal.klocek@theqtcompany.com> Done-with: Alexander Volkov <a.volkov@rusbitech.ru> Task-number: QTBUG-43525 Task-number: QTBUG-45054 Task-number: QTBUG-30417 Change-Id: I7cb2002b31bef4cd527aa427549dcf2d5467968e Reviewed-by: Laszlo Agocs <laszlo.agocs@theqtcompany.com> Reviewed-by: Shawn Rutledge <shawn.rutledge@digia.com>
This commit is contained in:
parent
2d7004c587
commit
53d289ec4c
8
configure
vendored
8
configure
vendored
@ -5358,6 +5358,14 @@ fi
|
||||
# auto-detect XInput2 support
|
||||
if [ "$CFG_XINPUT2" != "no" ]; then
|
||||
if compileTest x11/xinput2 "XInput2"; then
|
||||
if [ -n "$PKG_CONFIG" ] && $PKG_CONFIG --exists xi 2>/dev/null; then
|
||||
QMAKE_LIBXI_VERSION_MAJOR=`$PKG_CONFIG --modversion xi 2>/dev/null | cut -d . -f 1`
|
||||
QMAKE_LIBXI_VERSION_MINOR=`$PKG_CONFIG --modversion xi 2>/dev/null | cut -d . -f 2`
|
||||
QMAKE_LIBXI_VERSION_PATCH=`$PKG_CONFIG --modversion xi 2>/dev/null | cut -d . -f 3`
|
||||
QMakeVar set QMAKE_LIBXI_VERSION_MAJOR "$QMAKE_LIBXI_VERSION_MAJOR"
|
||||
QMakeVar set QMAKE_LIBXI_VERSION_MINOR "$QMAKE_LIBXI_VERSION_MINOR"
|
||||
QMakeVar set QMAKE_LIBXI_VERSION_PATCH "$QMAKE_LIBXI_VERSION_PATCH"
|
||||
fi
|
||||
CFG_XINPUT2=yes
|
||||
CFG_XINPUT=no
|
||||
else
|
||||
|
@ -70,7 +70,6 @@
|
||||
#endif
|
||||
|
||||
#if defined(XCB_USE_XINPUT2)
|
||||
#include <X11/extensions/XInput2.h>
|
||||
#include <X11/extensions/XI2proto.h>
|
||||
#endif
|
||||
|
||||
@ -457,6 +456,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
|
||||
, m_focusWindow(0)
|
||||
, m_systemTrayTracker(0)
|
||||
, m_glIntegration(Q_NULLPTR)
|
||||
, m_xiGrab(false)
|
||||
{
|
||||
#ifdef XCB_USE_XLIB
|
||||
Display *dpy = XOpenDisplay(m_displayName.constData());
|
||||
@ -909,7 +909,7 @@ static Qt::MouseButtons translateMouseButtons(int s)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Qt::MouseButton translateMouseButton(xcb_button_t s)
|
||||
Qt::MouseButton QXcbConnection::translateMouseButton(xcb_button_t s)
|
||||
{
|
||||
switch (s) {
|
||||
case 1: return Qt::LeftButton;
|
||||
@ -944,39 +944,6 @@ static Qt::MouseButton translateMouseButton(xcb_button_t s)
|
||||
}
|
||||
}
|
||||
|
||||
void QXcbConnection::handleButtonPress(xcb_generic_event_t *ev)
|
||||
{
|
||||
xcb_button_press_event_t *event = (xcb_button_press_event_t *)ev;
|
||||
|
||||
// the event explicitly contains the state of the three first buttons,
|
||||
// the rest we need to manage ourselves
|
||||
m_buttons = (m_buttons & ~0x7) | translateMouseButtons(event->state);
|
||||
m_buttons |= translateMouseButton(event->detail);
|
||||
qCDebug(lcQpaXInput, "xcb: pressed mouse button %d, button state %X", event->detail, static_cast<unsigned int>(m_buttons));
|
||||
}
|
||||
|
||||
void QXcbConnection::handleButtonRelease(xcb_generic_event_t *ev)
|
||||
{
|
||||
xcb_button_release_event_t *event = (xcb_button_release_event_t *)ev;
|
||||
|
||||
// the event explicitly contains the state of the three first buttons,
|
||||
// the rest we need to manage ourselves
|
||||
m_buttons = (m_buttons & ~0x7) | translateMouseButtons(event->state);
|
||||
m_buttons &= ~translateMouseButton(event->detail);
|
||||
qCDebug(lcQpaXInput, "xcb: released mouse button %d, button state %X", event->detail, static_cast<unsigned int>(m_buttons));
|
||||
}
|
||||
|
||||
void QXcbConnection::handleMotionNotify(xcb_generic_event_t *ev)
|
||||
{
|
||||
xcb_motion_notify_event_t *event = (xcb_motion_notify_event_t *)ev;
|
||||
|
||||
m_buttons = (m_buttons & ~0x7) | translateMouseButtons(event->state);
|
||||
#ifdef Q_XCB_DEBUG
|
||||
qCDebug(lcQpaXInput, "xcb: moved mouse to %4d, %4d; button state %X",
|
||||
event->event_x, event->event_y, static_cast<unsigned int>(m_buttons));
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef QT_NO_XKB
|
||||
namespace {
|
||||
typedef union {
|
||||
@ -1018,18 +985,35 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
|
||||
switch (response_type) {
|
||||
case XCB_EXPOSE:
|
||||
HANDLE_PLATFORM_WINDOW_EVENT(xcb_expose_event_t, window, handleExposeEvent);
|
||||
case XCB_BUTTON_PRESS:
|
||||
m_keyboard->updateXKBStateFromCore(((xcb_button_press_event_t *)event)->state);
|
||||
handleButtonPress(event);
|
||||
|
||||
// press/release/motion is only delivered here when XI 2.2+ is _not_ in use
|
||||
case XCB_BUTTON_PRESS: {
|
||||
xcb_button_press_event_t *ev = (xcb_button_press_event_t *)event;
|
||||
m_keyboard->updateXKBStateFromCore(ev->state);
|
||||
// the event explicitly contains the state of the three first buttons,
|
||||
// the rest we need to manage ourselves
|
||||
m_buttons = (m_buttons & ~0x7) | translateMouseButtons(ev->state);
|
||||
m_buttons |= translateMouseButton(ev->detail);
|
||||
qCDebug(lcQpaXInput, "legacy mouse press, button %d state %X", ev->detail, static_cast<unsigned int>(m_buttons));
|
||||
HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_press_event_t, event, handleButtonPressEvent);
|
||||
case XCB_BUTTON_RELEASE:
|
||||
m_keyboard->updateXKBStateFromCore(((xcb_button_release_event_t *)event)->state);
|
||||
handleButtonRelease(event);
|
||||
}
|
||||
case XCB_BUTTON_RELEASE: {
|
||||
xcb_button_release_event_t *ev = (xcb_button_release_event_t *)event;
|
||||
m_keyboard->updateXKBStateFromCore(ev->state);
|
||||
m_buttons = (m_buttons & ~0x7) | translateMouseButtons(ev->state);
|
||||
m_buttons &= ~translateMouseButton(ev->detail);
|
||||
qCDebug(lcQpaXInput, "legacy mouse release, button %d state %X", ev->detail, static_cast<unsigned int>(m_buttons));
|
||||
HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_release_event_t, event, handleButtonReleaseEvent);
|
||||
case XCB_MOTION_NOTIFY:
|
||||
m_keyboard->updateXKBStateFromCore(((xcb_motion_notify_event_t *)event)->state);
|
||||
handleMotionNotify(event);
|
||||
}
|
||||
case XCB_MOTION_NOTIFY: {
|
||||
xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)event;
|
||||
m_keyboard->updateXKBStateFromCore(ev->state);
|
||||
m_buttons = (m_buttons & ~0x7) | translateMouseButtons(ev->state);
|
||||
qCDebug(lcQpaXInput, "legacy mouse move %d,%d button %d state %X", ev->event_x, ev->event_y,
|
||||
ev->detail, static_cast<unsigned int>(m_buttons));
|
||||
HANDLE_PLATFORM_WINDOW_EVENT(xcb_motion_notify_event_t, event, handleMotionNotifyEvent);
|
||||
}
|
||||
|
||||
case XCB_CONFIGURE_NOTIFY:
|
||||
HANDLE_PLATFORM_WINDOW_EVENT(xcb_configure_notify_event_t, event, handleConfigureNotifyEvent);
|
||||
case XCB_MAP_NOTIFY:
|
||||
@ -1090,6 +1074,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
|
||||
break;
|
||||
#if defined(XCB_USE_XINPUT2)
|
||||
case XCB_GE_GENERIC:
|
||||
// Here the windowEventListener is invoked from xi2HandleEvent()
|
||||
if (m_xi2Enabled)
|
||||
xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(event));
|
||||
break;
|
||||
@ -1931,6 +1916,12 @@ void QXcbConnection::initializeXKB()
|
||||
#endif
|
||||
}
|
||||
|
||||
bool QXcbConnection::xi2MouseEvents() const
|
||||
{
|
||||
static bool mouseViaXI2 = !qEnvironmentVariableIsSet("QT_XCB_NO_XI2_MOUSE");
|
||||
return mouseViaXI2;
|
||||
}
|
||||
|
||||
#if defined(XCB_USE_XINPUT2)
|
||||
static int xi2ValuatorOffset(unsigned char *maskPtr, int maskLen, int number)
|
||||
{
|
||||
|
@ -69,7 +69,8 @@
|
||||
#endif
|
||||
#endif
|
||||
struct XInput2TouchDeviceData;
|
||||
#endif
|
||||
#endif // XCB_USE_XINPUT2
|
||||
|
||||
struct xcb_randr_get_output_info_reply_t;
|
||||
|
||||
//#define Q_XCB_DEBUG
|
||||
@ -347,6 +348,7 @@ public:
|
||||
virtual void handleFocusInEvent(const xcb_focus_in_event_t *) {}
|
||||
virtual void handleFocusOutEvent(const xcb_focus_out_event_t *) {}
|
||||
virtual void handlePropertyNotifyEvent(const xcb_property_notify_event_t *) {}
|
||||
virtual void handleXIMouseEvent(xcb_ge_event_t *) {}
|
||||
|
||||
virtual QXcbWindow *toWindow() { return 0; }
|
||||
};
|
||||
@ -413,14 +415,14 @@ public:
|
||||
void xi2Select(xcb_window_t window);
|
||||
#endif
|
||||
#ifdef XCB_USE_XINPUT21
|
||||
bool isUsingXInput21() const { return m_xi2Enabled && m_xi2Minor >= 1; }
|
||||
bool isAtLeastXI21() const { return m_xi2Enabled && m_xi2Minor >= 1; }
|
||||
#else
|
||||
bool isUsingXInput21() const { return false; }
|
||||
bool isAtLeastXI21() const { return false; }
|
||||
#endif
|
||||
#ifdef XCB_USE_XINPUT22
|
||||
bool isUsingXInput22() const { return m_xi2Enabled && m_xi2Minor >= 2; }
|
||||
bool isAtLeastXI22() const { return m_xi2Enabled && m_xi2Minor >= 2; }
|
||||
#else
|
||||
bool isUsingXInput22() const { return false; }
|
||||
bool isAtLeastXI22() const { return false; }
|
||||
#endif
|
||||
|
||||
void sync();
|
||||
@ -457,7 +459,9 @@ public:
|
||||
|
||||
xcb_timestamp_t getTimestamp();
|
||||
|
||||
void setButton(Qt::MouseButton button, bool down) { if (down) m_buttons |= button; else m_buttons &= ~button; }
|
||||
Qt::MouseButtons buttons() const { return m_buttons; }
|
||||
Qt::MouseButton translateMouseButton(xcb_button_t s);
|
||||
|
||||
QXcbWindow *focusWindow() const { return m_focusWindow; }
|
||||
void setFocusWindow(QXcbWindow *);
|
||||
@ -477,11 +481,19 @@ public:
|
||||
void handleEnterEvent(const xcb_enter_notify_event_t *);
|
||||
#endif
|
||||
|
||||
#ifdef XCB_USE_XINPUT22
|
||||
bool xi2SetMouseGrabEnabled(xcb_window_t w, bool grab);
|
||||
#endif
|
||||
Qt::MouseButton xiToQtMouseButton(uint32_t b);
|
||||
|
||||
QXcbEventReader *eventReader() const { return m_reader; }
|
||||
|
||||
bool canGrab() const { return m_canGrabServer; }
|
||||
|
||||
QXcbGlIntegration *glIntegration() const { return m_glIntegration; }
|
||||
|
||||
bool xi2MouseEvents() const;
|
||||
|
||||
protected:
|
||||
bool event(QEvent *e) Q_DECL_OVERRIDE;
|
||||
|
||||
@ -509,9 +521,6 @@ private:
|
||||
bool checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output);
|
||||
void initializeScreens();
|
||||
void updateScreens(const xcb_randr_notify_event_t *event);
|
||||
void handleButtonPress(xcb_generic_event_t *event);
|
||||
void handleButtonRelease(xcb_generic_event_t *event);
|
||||
void handleMotionNotify(xcb_generic_event_t *event);
|
||||
|
||||
bool m_xi2Enabled;
|
||||
int m_xi2Minor;
|
||||
@ -524,6 +533,9 @@ private:
|
||||
void xi2HandleHierachyEvent(void *event);
|
||||
void xi2HandleDeviceChangedEvent(void *event);
|
||||
int m_xiOpCode, m_xiEventBase, m_xiErrorBase;
|
||||
#ifdef XCB_USE_XINPUT22
|
||||
void xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindow);
|
||||
#endif // XCB_USE_XINPUT22
|
||||
#ifndef QT_NO_TABLETEVENT
|
||||
struct TabletData {
|
||||
TabletData() : deviceId(0), pointerType(QTabletEvent::UnknownPointer),
|
||||
@ -543,10 +555,10 @@ private:
|
||||
};
|
||||
QHash<int, ValuatorClassInfo> valuatorInfo;
|
||||
};
|
||||
bool xi2HandleTabletEvent(void *event, TabletData *tabletData);
|
||||
bool xi2HandleTabletEvent(void *event, TabletData *tabletData, QXcbWindowEventListener *eventListener);
|
||||
void xi2ReportTabletEvent(TabletData &tabletData, void *event);
|
||||
QVector<TabletData> m_tabletData;
|
||||
#endif
|
||||
#endif // !QT_NO_TABLETEVENT
|
||||
struct ScrollingDevice {
|
||||
ScrollingDevice() : deviceId(0), verticalIndex(0), horizontalIndex(0), orientations(0), legacyOrientations(0) { }
|
||||
int deviceId;
|
||||
@ -559,9 +571,7 @@ private:
|
||||
void updateScrollingDevice(ScrollingDevice& scrollingDevice, int num_classes, void *classes);
|
||||
void xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice);
|
||||
QHash<int, ScrollingDevice> m_scrollingDevices;
|
||||
#endif // XCB_USE_XINPUT2
|
||||
|
||||
#if defined(XCB_USE_XINPUT2)
|
||||
static bool xi2GetValuatorValueIfSet(void *event, int valuatorNum, double *value);
|
||||
static bool xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event, int opCode);
|
||||
#endif
|
||||
@ -633,6 +643,7 @@ private:
|
||||
QByteArray m_startupId;
|
||||
QXcbSystemTrayTracker *m_systemTrayTracker;
|
||||
QXcbGlIntegration *m_glIntegration;
|
||||
bool m_xiGrab;
|
||||
|
||||
friend class QXcbEventReader;
|
||||
};
|
||||
|
@ -275,34 +275,37 @@ void QXcbConnection::xi2Select(xcb_window_t window)
|
||||
unsigned char *xiBitMask = reinterpret_cast<unsigned char *>(&bitMask);
|
||||
|
||||
#ifdef XCB_USE_XINPUT22
|
||||
if (isUsingXInput22()) {
|
||||
if (isAtLeastXI22()) {
|
||||
bitMask |= XI_TouchBeginMask;
|
||||
bitMask |= XI_TouchUpdateMask;
|
||||
bitMask |= XI_TouchEndMask;
|
||||
bitMask |= XI_PropertyEventMask; // for tablets
|
||||
if (xi2MouseEvents()) {
|
||||
// We want both mouse and touch through XI2 if touch is supported (>= 2.2).
|
||||
// The plain xcb press and motion events will not be delivered after this.
|
||||
bitMask |= XI_ButtonPressMask;
|
||||
bitMask |= XI_ButtonReleaseMask;
|
||||
bitMask |= XI_MotionMask;
|
||||
qCDebug(lcQpaXInput, "XInput 2.2: Selecting press/release/motion events in addition to touch");
|
||||
}
|
||||
XIEventMask mask;
|
||||
mask.mask_len = sizeof(bitMask);
|
||||
mask.mask = xiBitMask;
|
||||
if (!m_touchDevices.isEmpty()) {
|
||||
// If we select for touch events on the master pointer, XInput2
|
||||
// will not synthesize mouse events. This means Qt must do it,
|
||||
// which is also preferable, since Qt can control better when
|
||||
// to do so.
|
||||
mask.deviceid = XIAllMasterDevices;
|
||||
Status result = XISelectEvents(xDisplay, window, &mask, 1);
|
||||
if (result != Success)
|
||||
qCDebug(lcQpaXInput, "XInput 2.2: failed to select touch events, window %x, result %d", window, result);
|
||||
}
|
||||
// When xi2MouseEvents() is true (the default), pointer emulation for touch and tablet
|
||||
// events will get disabled. This is preferable for touch, as Qt Quick handles touch events
|
||||
// directly while for others QtGui synthesizes mouse events, not so much for tablets. For
|
||||
// the latter we will synthesize the events ourselves.
|
||||
mask.deviceid = XIAllMasterDevices;
|
||||
Status result = XISelectEvents(xDisplay, window, &mask, 1);
|
||||
if (result != Success)
|
||||
qCDebug(lcQpaXInput, "XInput 2.2: failed to select pointer/touch events, window %x, result %d", window, result);
|
||||
}
|
||||
#endif // XCB_USE_XINPUT22
|
||||
|
||||
const bool pointerSelected = isAtLeastXI22() && xi2MouseEvents();
|
||||
QSet<int> tabletDevices;
|
||||
#ifndef QT_NO_TABLETEVENT
|
||||
// For each tablet, select some additional event types.
|
||||
// Press, motion, etc. events must never be selected for _all_ devices
|
||||
// as that would render the standard XCB_MOTION_NOTIFY and
|
||||
// similar handlers useless and we have no intention to infect
|
||||
// all the pure xcb code with Xlib-based XI2.
|
||||
if (!m_tabletData.isEmpty()) {
|
||||
if (!m_tabletData.isEmpty() && !pointerSelected) {
|
||||
unsigned int tabletBitMask;
|
||||
unsigned char *xiTabletBitMask = reinterpret_cast<unsigned char *>(&tabletBitMask);
|
||||
QVector<XIEventMask> xiEventMask(m_tabletData.count());
|
||||
@ -323,7 +326,8 @@ void QXcbConnection::xi2Select(xcb_window_t window)
|
||||
|
||||
#ifdef XCB_USE_XINPUT21
|
||||
// Enable each scroll device
|
||||
if (!m_scrollingDevices.isEmpty()) {
|
||||
if (!m_scrollingDevices.isEmpty() && !pointerSelected) {
|
||||
// Only when XI2 mouse events are not enabled, otherwise motion and release are selected already.
|
||||
QVector<XIEventMask> xiEventMask(m_scrollingDevices.size());
|
||||
unsigned int scrollBitMask;
|
||||
unsigned char *xiScrollBitMask = reinterpret_cast<unsigned char *>(&scrollBitMask);
|
||||
@ -425,7 +429,7 @@ XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
|
||||
dev->size.width() > 10000 || dev->size.height() > 10000)
|
||||
dev->size = QSizeF(130, 110);
|
||||
}
|
||||
if (!isUsingXInput22() || type == QTouchDevice::TouchPad)
|
||||
if (!isAtLeastXI22() || type == QTouchDevice::TouchPad)
|
||||
caps |= QTouchDevice::MouseEmulation;
|
||||
|
||||
if (type >= QTouchDevice::TouchScreen && type <= QTouchDevice::TouchPad) {
|
||||
@ -447,7 +451,7 @@ XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
|
||||
}
|
||||
|
||||
#if defined(XCB_USE_XINPUT21) || !defined(QT_NO_TABLETEVENT)
|
||||
static qreal fixed1616ToReal(FP1616 val)
|
||||
static inline qreal fixed1616ToReal(FP1616 val)
|
||||
{
|
||||
return qreal(val) / 0x10000;
|
||||
}
|
||||
@ -468,155 +472,280 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
|
||||
{
|
||||
if (xi2PrepareXIGenericDeviceEvent(event, m_xiOpCode)) {
|
||||
xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
|
||||
int sourceDeviceId = xiEvent->deviceid; // may be the master id
|
||||
xXIDeviceEvent *xiDeviceEvent = 0;
|
||||
QXcbWindowEventListener *eventListener = 0;
|
||||
|
||||
if (xiEvent->evtype == XI_HierarchyChanged) {
|
||||
switch (xiEvent->evtype) {
|
||||
case XI_ButtonPress:
|
||||
case XI_ButtonRelease:
|
||||
case XI_Motion:
|
||||
case XI_TouchBegin:
|
||||
case XI_TouchUpdate:
|
||||
case XI_TouchEnd:
|
||||
{
|
||||
xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
|
||||
eventListener = windowEventListenerFromId(xiDeviceEvent->event);
|
||||
if (eventListener) {
|
||||
long result = 0;
|
||||
if (eventListener->handleGenericEvent(reinterpret_cast<xcb_generic_event_t *>(event), &result))
|
||||
return;
|
||||
}
|
||||
sourceDeviceId = xiDeviceEvent->sourceid; // use the actual device id instead of the master
|
||||
break;
|
||||
}
|
||||
case XI_HierarchyChanged:
|
||||
xi2HandleHierachyEvent(xiEvent);
|
||||
return;
|
||||
}
|
||||
if (xiEvent->evtype == XI_DeviceChanged) {
|
||||
case XI_DeviceChanged:
|
||||
xi2HandleDeviceChangedEvent(xiEvent);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#ifndef QT_NO_TABLETEVENT
|
||||
for (int i = 0; i < m_tabletData.count(); ++i) {
|
||||
if (m_tabletData.at(i).deviceId == xiEvent->deviceid) {
|
||||
if (xi2HandleTabletEvent(xiEvent, &m_tabletData[i]))
|
||||
if (m_tabletData.at(i).deviceId == sourceDeviceId) {
|
||||
if (xi2HandleTabletEvent(xiEvent, &m_tabletData[i], eventListener))
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif // QT_NO_TABLETEVENT
|
||||
|
||||
#ifdef XCB_USE_XINPUT21
|
||||
QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(xiEvent->deviceid);
|
||||
QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(sourceDeviceId);
|
||||
if (device != m_scrollingDevices.end())
|
||||
xi2HandleScrollEvent(xiEvent, device.value());
|
||||
#endif // XCB_USE_XINPUT21
|
||||
|
||||
#ifdef XCB_USE_XINPUT22
|
||||
if (xiEvent->evtype == XI_TouchBegin || xiEvent->evtype == XI_TouchUpdate || xiEvent->evtype == XI_TouchEnd) {
|
||||
xXIDeviceEvent* xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
|
||||
if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
|
||||
qCDebug(lcQpaXInput, "XI2 touch event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f",
|
||||
event->event_type, xiEvent->sequenceNumber, xiDeviceEvent->detail,
|
||||
fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y),
|
||||
fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y) );
|
||||
|
||||
if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event)) {
|
||||
XInput2TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid);
|
||||
Q_ASSERT(dev);
|
||||
const bool firstTouch = m_touchPoints.isEmpty();
|
||||
if (xiEvent->evtype == XI_TouchBegin) {
|
||||
QWindowSystemInterface::TouchPoint tp;
|
||||
tp.id = xiDeviceEvent->detail % INT_MAX;
|
||||
tp.state = Qt::TouchPointPressed;
|
||||
tp.pressure = -1.0;
|
||||
m_touchPoints[tp.id] = tp;
|
||||
}
|
||||
QWindowSystemInterface::TouchPoint &touchPoint = m_touchPoints[xiDeviceEvent->detail];
|
||||
qreal x = fixed1616ToReal(xiDeviceEvent->root_x);
|
||||
qreal y = fixed1616ToReal(xiDeviceEvent->root_y);
|
||||
qreal nx = -1.0, ny = -1.0, d = 0.0;
|
||||
QXcbScreen* screen = m_screens.at(0);
|
||||
for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) {
|
||||
XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i];
|
||||
if (classinfo->type == XIValuatorClass) {
|
||||
XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
|
||||
int n = vci->number;
|
||||
double value;
|
||||
if (!xi2GetValuatorValueIfSet(xiDeviceEvent, n, &value))
|
||||
continue;
|
||||
if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
|
||||
qCDebug(lcQpaXInput, " valuator %20s value %lf from range %lf -> %lf",
|
||||
atomName(vci->label).constData(), value, vci->min, vci->max );
|
||||
if (vci->label == atom(QXcbAtom::RelX)) {
|
||||
nx = valuatorNormalized(value, vci);
|
||||
} else if (vci->label == atom(QXcbAtom::RelY)) {
|
||||
ny = valuatorNormalized(value, vci);
|
||||
} else if (vci->label == atom(QXcbAtom::AbsX)) {
|
||||
nx = valuatorNormalized(value, vci);
|
||||
} else if (vci->label == atom(QXcbAtom::AbsY)) {
|
||||
ny = valuatorNormalized(value, vci);
|
||||
} else if (vci->label == atom(QXcbAtom::AbsMTPositionX)) {
|
||||
nx = valuatorNormalized(value, vci);
|
||||
} else if (vci->label == atom(QXcbAtom::AbsMTPositionY)) {
|
||||
ny = valuatorNormalized(value, vci);
|
||||
} else if (vci->label == atom(QXcbAtom::AbsMTTouchMajor)) {
|
||||
d = valuatorNormalized(value, vci) * screen->geometry().width();
|
||||
} else if (vci->label == atom(QXcbAtom::AbsMTPressure) ||
|
||||
vci->label == atom(QXcbAtom::AbsPressure)) {
|
||||
touchPoint.pressure = valuatorNormalized(value, vci);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If any value was not updated, use the last-known value.
|
||||
if (nx == -1.0) {
|
||||
x = touchPoint.area.center().x();
|
||||
nx = x / screen->geometry().width();
|
||||
}
|
||||
if (ny == -1.0) {
|
||||
y = touchPoint.area.center().y();
|
||||
ny = y / screen->geometry().height();
|
||||
}
|
||||
if (xiEvent->evtype != XI_TouchEnd) {
|
||||
if (d == 0.0)
|
||||
d = touchPoint.area.width();
|
||||
}
|
||||
|
||||
switch (xiEvent->evtype) {
|
||||
case XI_TouchBegin:
|
||||
if (firstTouch) {
|
||||
dev->firstPressedPosition = QPointF(x, y);
|
||||
dev->firstPressedNormalPosition = QPointF(nx, ny);
|
||||
}
|
||||
dev->pointPressedPosition.insert(touchPoint.id, QPointF(x, y));
|
||||
break;
|
||||
case XI_TouchUpdate:
|
||||
if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad && dev->pointPressedPosition.value(touchPoint.id) == QPointF(x, y)) {
|
||||
qreal dx = (nx - dev->firstPressedNormalPosition.x()) *
|
||||
dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
|
||||
qreal dy = (ny - dev->firstPressedNormalPosition.y()) *
|
||||
dev->size.height() * screen->geometry().height() / screen->physicalSize().height();
|
||||
x = dev->firstPressedPosition.x() + dx;
|
||||
y = dev->firstPressedPosition.y() + dy;
|
||||
touchPoint.state = Qt::TouchPointMoved;
|
||||
} else if (touchPoint.area.center() != QPoint(x, y)) {
|
||||
touchPoint.state = Qt::TouchPointMoved;
|
||||
dev->pointPressedPosition[touchPoint.id] = QPointF(x, y);
|
||||
}
|
||||
break;
|
||||
case XI_TouchEnd:
|
||||
touchPoint.state = Qt::TouchPointReleased;
|
||||
if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad && dev->pointPressedPosition.value(touchPoint.id) == QPointF(x, y)) {
|
||||
qreal dx = (nx - dev->firstPressedNormalPosition.x()) *
|
||||
dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
|
||||
qreal dy = (ny - dev->firstPressedNormalPosition.y()) *
|
||||
dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
|
||||
x = dev->firstPressedPosition.x() + dx;
|
||||
y = dev->firstPressedPosition.y() + dy;
|
||||
}
|
||||
dev->pointPressedPosition.remove(touchPoint.id);
|
||||
}
|
||||
touchPoint.area = QRectF(x - d/2, y - d/2, d, d);
|
||||
touchPoint.normalPosition = QPointF(nx, ny);
|
||||
if (xiDeviceEvent) {
|
||||
switch (xiDeviceEvent->evtype) {
|
||||
case XI_ButtonPress:
|
||||
case XI_ButtonRelease:
|
||||
case XI_Motion:
|
||||
if (xi2MouseEvents() && eventListener)
|
||||
eventListener->handleXIMouseEvent(event);
|
||||
break;
|
||||
|
||||
case XI_TouchBegin:
|
||||
case XI_TouchUpdate:
|
||||
case XI_TouchEnd:
|
||||
if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
|
||||
qCDebug(lcQpaXInput) << " touchpoint " << touchPoint.id << " state " << touchPoint.state << " pos norm " << touchPoint.normalPosition <<
|
||||
" area " << touchPoint.area << " pressure " << touchPoint.pressure;
|
||||
QWindowSystemInterface::handleTouchEvent(platformWindow->window(), xiEvent->time, dev->qtTouchDevice, m_touchPoints.values());
|
||||
if (touchPoint.state == Qt::TouchPointReleased)
|
||||
// If a touchpoint was released, we can forget it, because the ID won't be reused.
|
||||
m_touchPoints.remove(touchPoint.id);
|
||||
else
|
||||
// Make sure that we don't send TouchPointPressed/Moved in more than one QTouchEvent
|
||||
// with this touch point if the next XI2 event is about a different touch point.
|
||||
touchPoint.state = Qt::TouchPointStationary;
|
||||
qCDebug(lcQpaXInput, "XI2 touch event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f on window %x",
|
||||
event->event_type, xiDeviceEvent->sequenceNumber, xiDeviceEvent->detail,
|
||||
fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y),
|
||||
fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y),xiDeviceEvent->event);
|
||||
if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event))
|
||||
xi2ProcessTouch(xiDeviceEvent, platformWindow);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif // XCB_USE_XINPUT22
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef XCB_USE_XINPUT22
|
||||
void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindow)
|
||||
{
|
||||
xXIDeviceEvent *xiDeviceEvent = static_cast<xXIDeviceEvent *>(xiDevEvent);
|
||||
XInput2TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid);
|
||||
Q_ASSERT(dev);
|
||||
const bool firstTouch = m_touchPoints.isEmpty();
|
||||
if (xiDeviceEvent->evtype == XI_TouchBegin) {
|
||||
QWindowSystemInterface::TouchPoint tp;
|
||||
tp.id = xiDeviceEvent->detail % INT_MAX;
|
||||
tp.state = Qt::TouchPointPressed;
|
||||
tp.pressure = -1.0;
|
||||
m_touchPoints[tp.id] = tp;
|
||||
}
|
||||
QWindowSystemInterface::TouchPoint &touchPoint = m_touchPoints[xiDeviceEvent->detail];
|
||||
qreal x = fixed1616ToReal(xiDeviceEvent->root_x);
|
||||
qreal y = fixed1616ToReal(xiDeviceEvent->root_y);
|
||||
qreal nx = -1.0, ny = -1.0, d = 0.0;
|
||||
QXcbScreen* screen = m_screens.at(0);
|
||||
for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) {
|
||||
XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i];
|
||||
if (classinfo->type == XIValuatorClass) {
|
||||
XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
|
||||
int n = vci->number;
|
||||
double value;
|
||||
if (!xi2GetValuatorValueIfSet(xiDeviceEvent, n, &value))
|
||||
continue;
|
||||
if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
|
||||
qCDebug(lcQpaXInput, " valuator %20s value %lf from range %lf -> %lf",
|
||||
atomName(vci->label).constData(), value, vci->min, vci->max );
|
||||
if (vci->label == atom(QXcbAtom::RelX)) {
|
||||
nx = valuatorNormalized(value, vci);
|
||||
} else if (vci->label == atom(QXcbAtom::RelY)) {
|
||||
ny = valuatorNormalized(value, vci);
|
||||
} else if (vci->label == atom(QXcbAtom::AbsX)) {
|
||||
nx = valuatorNormalized(value, vci);
|
||||
} else if (vci->label == atom(QXcbAtom::AbsY)) {
|
||||
ny = valuatorNormalized(value, vci);
|
||||
} else if (vci->label == atom(QXcbAtom::AbsMTPositionX)) {
|
||||
nx = valuatorNormalized(value, vci);
|
||||
} else if (vci->label == atom(QXcbAtom::AbsMTPositionY)) {
|
||||
ny = valuatorNormalized(value, vci);
|
||||
} else if (vci->label == atom(QXcbAtom::AbsMTTouchMajor)) {
|
||||
d = valuatorNormalized(value, vci) * screen->geometry().width();
|
||||
} else if (vci->label == atom(QXcbAtom::AbsMTPressure) ||
|
||||
vci->label == atom(QXcbAtom::AbsPressure)) {
|
||||
touchPoint.pressure = valuatorNormalized(value, vci);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If any value was not updated, use the last-known value.
|
||||
if (nx == -1.0) {
|
||||
x = touchPoint.area.center().x();
|
||||
nx = x / screen->geometry().width();
|
||||
}
|
||||
if (ny == -1.0) {
|
||||
y = touchPoint.area.center().y();
|
||||
ny = y / screen->geometry().height();
|
||||
}
|
||||
if (xiDeviceEvent->evtype != XI_TouchEnd) {
|
||||
if (d == 0.0)
|
||||
d = touchPoint.area.width();
|
||||
}
|
||||
|
||||
switch (xiDeviceEvent->evtype) {
|
||||
case XI_TouchBegin:
|
||||
if (firstTouch) {
|
||||
dev->firstPressedPosition = QPointF(x, y);
|
||||
dev->firstPressedNormalPosition = QPointF(nx, ny);
|
||||
}
|
||||
dev->pointPressedPosition.insert(touchPoint.id, QPointF(x, y));
|
||||
|
||||
// Touches must be accepted when we are grabbing touch events. Otherwise the entire sequence
|
||||
// will get replayed when the grab ends.
|
||||
if (m_xiGrab) {
|
||||
// XIAllowTouchEvents deadlocks with libXi < 1.7.4 (this has nothing to do with the XI2 versions like 2.2)
|
||||
// http://lists.x.org/archives/xorg-devel/2014-July/043059.html
|
||||
#ifndef LIBXI_MAJOR
|
||||
static bool allowTouchWarningShown = false;
|
||||
if (!allowTouchWarningShown) {
|
||||
allowTouchWarningShown = true;
|
||||
qWarning("Skipping XIAllowTouchEvents() because it was not possible to detect libXi version at build time."
|
||||
" Minimum libXi version required is 1.7.4."
|
||||
" Expect issues with touch behavior.");
|
||||
}
|
||||
#elif LIBXI_MAJOR == 1 && (LIBXI_MINOR < 7 || (LIBXI_MINOR == 7 && LIBXI_PATCH < 4))
|
||||
static bool allowTouchWarningShown = false;
|
||||
if (!allowTouchWarningShown) {
|
||||
allowTouchWarningShown = true;
|
||||
qWarning("Skipping XIAllowTouchEvents() due to not having libXi >= 1.7.4."
|
||||
" libXi version at build time was %d.%d.%d."
|
||||
" Expect issues with touch behavior.", LIBXI_MAJOR, LIBXI_MINOR, LIBXI_PATCH);
|
||||
}
|
||||
#else
|
||||
XIAllowTouchEvents(static_cast<Display *>(m_xlib_display), xiDeviceEvent->deviceid,
|
||||
xiDeviceEvent->detail, xiDeviceEvent->event, XIAcceptTouch);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case XI_TouchUpdate:
|
||||
if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad && dev->pointPressedPosition.value(touchPoint.id) == QPointF(x, y)) {
|
||||
qreal dx = (nx - dev->firstPressedNormalPosition.x()) *
|
||||
dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
|
||||
qreal dy = (ny - dev->firstPressedNormalPosition.y()) *
|
||||
dev->size.height() * screen->geometry().height() / screen->physicalSize().height();
|
||||
x = dev->firstPressedPosition.x() + dx;
|
||||
y = dev->firstPressedPosition.y() + dy;
|
||||
touchPoint.state = Qt::TouchPointMoved;
|
||||
} else if (touchPoint.area.center() != QPoint(x, y)) {
|
||||
touchPoint.state = Qt::TouchPointMoved;
|
||||
dev->pointPressedPosition[touchPoint.id] = QPointF(x, y);
|
||||
}
|
||||
break;
|
||||
case XI_TouchEnd:
|
||||
touchPoint.state = Qt::TouchPointReleased;
|
||||
if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad && dev->pointPressedPosition.value(touchPoint.id) == QPointF(x, y)) {
|
||||
qreal dx = (nx - dev->firstPressedNormalPosition.x()) *
|
||||
dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
|
||||
qreal dy = (ny - dev->firstPressedNormalPosition.y()) *
|
||||
dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
|
||||
x = dev->firstPressedPosition.x() + dx;
|
||||
y = dev->firstPressedPosition.y() + dy;
|
||||
}
|
||||
dev->pointPressedPosition.remove(touchPoint.id);
|
||||
}
|
||||
touchPoint.area = QRectF(x - d/2, y - d/2, d, d);
|
||||
touchPoint.normalPosition = QPointF(nx, ny);
|
||||
|
||||
if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
|
||||
qCDebug(lcQpaXInput) << " touchpoint " << touchPoint.id << " state " << touchPoint.state << " pos norm " << touchPoint.normalPosition <<
|
||||
" area " << touchPoint.area << " pressure " << touchPoint.pressure;
|
||||
QWindowSystemInterface::handleTouchEvent(platformWindow->window(), xiDeviceEvent->time, dev->qtTouchDevice, m_touchPoints.values());
|
||||
if (touchPoint.state == Qt::TouchPointReleased)
|
||||
// If a touchpoint was released, we can forget it, because the ID won't be reused.
|
||||
m_touchPoints.remove(touchPoint.id);
|
||||
else
|
||||
// Make sure that we don't send TouchPointPressed/Moved in more than one QTouchEvent
|
||||
// with this touch point if the next XI2 event is about a different touch point.
|
||||
touchPoint.state = Qt::TouchPointStationary;
|
||||
}
|
||||
|
||||
bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
|
||||
{
|
||||
if (grab && !canGrab())
|
||||
return false;
|
||||
|
||||
int num_devices = 0;
|
||||
Display *xDisplay = static_cast<Display *>(xlib_display());
|
||||
XIDeviceInfo *info = XIQueryDevice(xDisplay, XIAllMasterDevices, &num_devices);
|
||||
if (!info)
|
||||
return false;
|
||||
|
||||
XIEventMask evmask;
|
||||
unsigned char mask[XIMaskLen(XI_LASTEVENT)];
|
||||
evmask.mask = mask;
|
||||
evmask.mask_len = sizeof(mask);
|
||||
memset(mask, 0, sizeof(mask));
|
||||
evmask.deviceid = XIAllMasterDevices;
|
||||
|
||||
XISetMask(mask, XI_ButtonPress);
|
||||
XISetMask(mask, XI_ButtonRelease);
|
||||
XISetMask(mask, XI_Motion);
|
||||
XISetMask(mask, XI_TouchBegin);
|
||||
XISetMask(mask, XI_TouchUpdate);
|
||||
XISetMask(mask, XI_TouchEnd);
|
||||
|
||||
bool grabbed = true;
|
||||
for (int i = 0; i < num_devices; i++) {
|
||||
int id = info[i].deviceid, n = 0;
|
||||
XIDeviceInfo *deviceInfo = XIQueryDevice(xDisplay, id, &n);
|
||||
if (deviceInfo) {
|
||||
const bool grabbable = deviceInfo->use != XIMasterKeyboard;
|
||||
XIFreeDeviceInfo(deviceInfo);
|
||||
if (!grabbable)
|
||||
continue;
|
||||
}
|
||||
if (!grab) {
|
||||
Status result = XIUngrabDevice(xDisplay, id, CurrentTime);
|
||||
if (result != Success) {
|
||||
grabbed = false;
|
||||
qCDebug(lcQpaXInput, "XInput 2.2: failed to ungrab events for device %d (result %d)", id, result);
|
||||
}
|
||||
} else {
|
||||
Status result = XIGrabDevice(xDisplay, id, w, CurrentTime, None, XIGrabModeAsync,
|
||||
XIGrabModeAsync, False, &evmask);
|
||||
if (result != Success) {
|
||||
grabbed = false;
|
||||
qCDebug(lcQpaXInput, "XInput 2.2: failed to grab events for device %d on window %x (result %d)", id, w, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XIFreeDeviceInfo(info);
|
||||
|
||||
m_xiGrab = grabbed;
|
||||
|
||||
return grabbed;
|
||||
}
|
||||
#endif // XCB_USE_XINPUT22
|
||||
|
||||
void QXcbConnection::xi2HandleHierachyEvent(void *event)
|
||||
{
|
||||
xXIHierarchyEvent *xiEvent = reinterpret_cast<xXIHierarchyEvent *>(event);
|
||||
@ -785,7 +914,8 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin
|
||||
#endif // XCB_USE_XINPUT21
|
||||
}
|
||||
|
||||
static Qt::MouseButton xiToQtMouseButton(uint32_t b) {
|
||||
Qt::MouseButton QXcbConnection::xiToQtMouseButton(uint32_t b)
|
||||
{
|
||||
switch (b) {
|
||||
case 1: return Qt::LeftButton;
|
||||
case 2: return Qt::MiddleButton;
|
||||
@ -832,20 +962,29 @@ static QTabletEvent::TabletDevice toolIdToTabletDevice(quint32 toolId) {
|
||||
}
|
||||
|
||||
#ifndef QT_NO_TABLETEVENT
|
||||
bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData)
|
||||
bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData, QXcbWindowEventListener *eventListener)
|
||||
{
|
||||
bool handled = true;
|
||||
Display *xDisplay = static_cast<Display *>(m_xlib_display);
|
||||
xXIGenericDeviceEvent *xiEvent = static_cast<xXIGenericDeviceEvent *>(event);
|
||||
xXIDeviceEvent *xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(xiEvent);
|
||||
|
||||
#ifdef XCB_USE_XINPUT22
|
||||
// Synthesize mouse events since otherwise there are no mouse events from
|
||||
// the pen on the XI 2.2+ path.
|
||||
if (xi2MouseEvents() && eventListener)
|
||||
eventListener->handleXIMouseEvent(reinterpret_cast<xcb_ge_event_t *>(event));
|
||||
#endif
|
||||
|
||||
switch (xiEvent->evtype) {
|
||||
case XI_ButtonPress: {
|
||||
Qt::MouseButton b = xiToQtMouseButton(reinterpret_cast<xXIDeviceEvent *>(event)->detail);
|
||||
Qt::MouseButton b = xiToQtMouseButton(xiDeviceEvent->detail);
|
||||
tabletData->buttons |= b;
|
||||
xi2ReportTabletEvent(*tabletData, xiEvent);
|
||||
break;
|
||||
}
|
||||
case XI_ButtonRelease: {
|
||||
Qt::MouseButton b = xiToQtMouseButton(reinterpret_cast<xXIDeviceEvent *>(event)->detail);
|
||||
Qt::MouseButton b = xiToQtMouseButton(xiDeviceEvent->detail);
|
||||
tabletData->buttons ^= b;
|
||||
xi2ReportTabletEvent(*tabletData, xiEvent);
|
||||
break;
|
||||
@ -908,7 +1047,7 @@ bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData)
|
||||
// TODO maybe have a hash of tabletData->deviceId to device data so we can
|
||||
// look up the tablet name here, and distinguish multiple tablets
|
||||
qCDebug(lcQpaXInput, "XI2 proximity change on tablet %d (USB %x): last tool: %x id %x current tool: %x id %x TabletDevice %d",
|
||||
ev->deviceid, ptr[_WACSER_USB_ID], ptr[_WACSER_LAST_TOOL_SERIAL], ptr[_WACSER_LAST_TOOL_ID],
|
||||
tabletData->deviceId, ptr[_WACSER_USB_ID], ptr[_WACSER_LAST_TOOL_SERIAL], ptr[_WACSER_LAST_TOOL_ID],
|
||||
ptr[_WACSER_TOOL_SERIAL], ptr[_WACSER_TOOL_ID], tabletData->tool);
|
||||
}
|
||||
XFree(data);
|
||||
@ -972,7 +1111,7 @@ void QXcbConnection::xi2ReportTabletEvent(TabletData &tabletData, void *event)
|
||||
|
||||
if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
|
||||
qCDebug(lcQpaXInput, "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,
|
||||
tabletData.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),
|
||||
(int)tabletData.buttons, pressure, xTilt, yTilt, rotation);
|
||||
|
@ -47,6 +47,12 @@
|
||||
#include <stdio.h>
|
||||
#include <X11/keysym.h>
|
||||
|
||||
#ifdef XCB_USE_XINPUT22
|
||||
#include <X11/extensions/XI2proto.h>
|
||||
#undef KeyPress
|
||||
#undef KeyRelease
|
||||
#endif
|
||||
|
||||
#ifndef XK_ISO_Left_Tab
|
||||
#define XK_ISO_Left_Tab 0xFE20
|
||||
#endif
|
||||
@ -791,6 +797,31 @@ void QXcbKeyboard::updateXKBStateFromCore(quint16 state)
|
||||
}
|
||||
}
|
||||
|
||||
void QXcbKeyboard::updateXKBStateFromXI(void *modInfo, void *groupInfo)
|
||||
{
|
||||
#ifdef XCB_USE_XINPUT22
|
||||
if (m_config && !connection()->hasXKB()) {
|
||||
xXIModifierInfo *mods = static_cast<xXIModifierInfo *>(modInfo);
|
||||
xXIGroupInfo *group = static_cast<xXIGroupInfo *>(groupInfo);
|
||||
const xkb_state_component newState = xkb_state_update_mask(xkb_state,
|
||||
mods->base_mods,
|
||||
mods->latched_mods,
|
||||
mods->locked_mods,
|
||||
group->base_group,
|
||||
group->latched_group,
|
||||
group->locked_group);
|
||||
|
||||
if ((newState & XKB_STATE_LAYOUT_EFFECTIVE) == XKB_STATE_LAYOUT_EFFECTIVE) {
|
||||
//qWarning("TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)");
|
||||
}
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(modInfo);
|
||||
Q_UNUSED(groupInfo);
|
||||
Q_ASSERT(false); // this can't be
|
||||
#endif
|
||||
}
|
||||
|
||||
quint32 QXcbKeyboard::xkbModMask(quint16 state)
|
||||
{
|
||||
quint32 xkb_mask = 0;
|
||||
|
@ -68,6 +68,7 @@ public:
|
||||
void updateXKBMods();
|
||||
quint32 xkbModMask(quint16 state);
|
||||
void updateXKBStateFromCore(quint16 state);
|
||||
void updateXKBStateFromXI(void *modInfo, void *groupInfo);
|
||||
#ifndef QT_NO_XKB
|
||||
// when XKEYBOARD is present on the X server
|
||||
int coreDeviceId() const { return core_device_id; }
|
||||
|
@ -96,6 +96,7 @@
|
||||
|
||||
#if defined(XCB_USE_XINPUT2)
|
||||
#include <X11/extensions/XInput2.h>
|
||||
#include <X11/extensions/XI2proto.h>
|
||||
#endif
|
||||
|
||||
#define XCOORD_MAX 16383
|
||||
@ -2106,16 +2107,17 @@ void QXcbWindow::handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event)
|
||||
}
|
||||
}
|
||||
|
||||
void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event)
|
||||
void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, int root_y,
|
||||
int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp)
|
||||
{
|
||||
const bool isWheel = event->detail >= 4 && event->detail <= 7;
|
||||
const bool isWheel = detail >= 4 && detail <= 7;
|
||||
if (!isWheel && window() != QGuiApplication::focusWindow()) {
|
||||
QWindow *w = static_cast<QWindowPrivate *>(QObjectPrivate::get(window()))->eventReceiver();
|
||||
if (!(w->flags() & Qt::WindowDoesNotAcceptFocus))
|
||||
w->requestActivate();
|
||||
}
|
||||
|
||||
updateNetWmUserTime(event->time);
|
||||
updateNetWmUserTime(timestamp);
|
||||
|
||||
if (m_embedded) {
|
||||
if (window() != QGuiApplication::focusWindow()) {
|
||||
@ -2126,53 +2128,125 @@ void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event)
|
||||
}
|
||||
}
|
||||
const int dpr = int(devicePixelRatio());
|
||||
QPoint local(event->event_x/dpr, event->event_y/dpr);
|
||||
QPoint global = xcbScreen()->mapFromNative(QPoint(event->root_x, event->root_y));
|
||||
|
||||
Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
|
||||
QPoint local(event_x / dpr, event_y / dpr);
|
||||
QPoint global = xcbScreen()->mapFromNative(QPoint(root_x, root_y));
|
||||
|
||||
if (isWheel) {
|
||||
if (!connection()->isUsingXInput21()) {
|
||||
if (!connection()->isAtLeastXI21()) {
|
||||
// Logic borrowed from qapplication_x11.cpp
|
||||
int delta = 120 * ((event->detail == 4 || event->detail == 6) ? 1 : -1);
|
||||
bool hor = (((event->detail == 4 || event->detail == 5)
|
||||
int delta = 120 * ((detail == 4 || detail == 6) ? 1 : -1);
|
||||
bool hor = (((detail == 4 || detail == 5)
|
||||
&& (modifiers & Qt::AltModifier))
|
||||
|| (event->detail == 6 || event->detail == 7));
|
||||
|| (detail == 6 || detail == 7));
|
||||
|
||||
QWindowSystemInterface::handleWheelEvent(window(), event->time,
|
||||
QWindowSystemInterface::handleWheelEvent(window(), timestamp,
|
||||
local, global, delta, hor ? Qt::Horizontal : Qt::Vertical, modifiers);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
handleMouseEvent(event->time, local, global, modifiers);
|
||||
handleMouseEvent(timestamp, local, global, modifiers);
|
||||
}
|
||||
|
||||
void QXcbWindow::handleButtonReleaseEvent(const xcb_button_release_event_t *event)
|
||||
void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x, int root_y,
|
||||
int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp)
|
||||
{
|
||||
const int dpr = int(devicePixelRatio());
|
||||
QPoint local(event->event_x/dpr, event->event_y/dpr);
|
||||
QPoint global = xcbScreen()->mapFromNative(QPoint(event->root_x, event->root_y));
|
||||
Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
|
||||
QPoint local(event_x / dpr, event_y / dpr);
|
||||
QPoint global = xcbScreen()->mapFromNative(QPoint(root_x, root_y));
|
||||
|
||||
if (event->detail >= 4 && event->detail <= 7) {
|
||||
if (detail >= 4 && detail <= 7) {
|
||||
// mouse wheel, handled in handleButtonPressEvent()
|
||||
return;
|
||||
}
|
||||
|
||||
handleMouseEvent(event->time, local, global, modifiers);
|
||||
handleMouseEvent(timestamp, local, global, modifiers);
|
||||
}
|
||||
|
||||
void QXcbWindow::handleMotionNotifyEvent(int event_x, int event_y, int root_x, int root_y,
|
||||
Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp)
|
||||
{
|
||||
if (!xcbScreen())
|
||||
return;
|
||||
const int dpr = int(devicePixelRatio());
|
||||
QPoint local(event_x / dpr, event_y / dpr);
|
||||
QPoint global = xcbScreen()->mapFromNative(QPoint(root_x, root_y));
|
||||
handleMouseEvent(timestamp, local, global, modifiers);
|
||||
}
|
||||
|
||||
// Handlers for plain xcb events. Used only when XI 2.2 or newer is not available.
|
||||
void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event)
|
||||
{
|
||||
Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
|
||||
handleButtonPressEvent(event->event_x, event->event_y, event->root_x, event->root_y, event->detail,
|
||||
modifiers, event->time);
|
||||
}
|
||||
|
||||
void QXcbWindow::handleButtonReleaseEvent(const xcb_button_release_event_t *event)
|
||||
{
|
||||
Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
|
||||
handleButtonReleaseEvent(event->event_x, event->event_y, event->root_x, event->root_y, event->detail,
|
||||
modifiers, event->time);
|
||||
}
|
||||
|
||||
void QXcbWindow::handleMotionNotifyEvent(const xcb_motion_notify_event_t *event)
|
||||
{
|
||||
const int dpr = int(devicePixelRatio());
|
||||
QPoint local(event->event_x/dpr, event->event_y/dpr);
|
||||
if (!xcbScreen())
|
||||
return;
|
||||
QPoint global = xcbScreen()->mapFromNative(QPoint(event->root_x, event->root_y));
|
||||
Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
|
||||
handleMotionNotifyEvent(event->event_x, event->event_y, event->root_x, event->root_y, modifiers, event->time);
|
||||
}
|
||||
|
||||
handleMouseEvent(event->time, local, global, modifiers);
|
||||
#ifdef XCB_USE_XINPUT22
|
||||
static inline int fixed1616ToInt(FP1616 val)
|
||||
{
|
||||
return int((qreal(val >> 16)) + (val & 0xFFFF) / (qreal)0xFFFF);
|
||||
}
|
||||
#endif
|
||||
|
||||
// With XI 2.2+ press/release/motion comes here instead of the above handlers.
|
||||
void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event)
|
||||
{
|
||||
#ifdef XCB_USE_XINPUT22
|
||||
QXcbConnection *conn = connection();
|
||||
xXIDeviceEvent *ev = reinterpret_cast<xXIDeviceEvent *>(event);
|
||||
const Qt::KeyboardModifiers modifiers = conn->keyboard()->translateModifiers(ev->mods.effective_mods);
|
||||
const int event_x = fixed1616ToInt(ev->event_x);
|
||||
const int event_y = fixed1616ToInt(ev->event_y);
|
||||
const int root_x = fixed1616ToInt(ev->root_x);
|
||||
const int root_y = fixed1616ToInt(ev->root_y);
|
||||
|
||||
conn->keyboard()->updateXKBStateFromXI(&ev->mods, &ev->group);
|
||||
|
||||
const Qt::MouseButton button = conn->xiToQtMouseButton(ev->detail);
|
||||
|
||||
if (ev->buttons_len > 0) {
|
||||
unsigned char *buttonMask = (unsigned char *) &ev[1];
|
||||
for (int i = 1; i <= 15; ++i)
|
||||
conn->setButton(conn->translateMouseButton(i), XIMaskIsSet(buttonMask, i));
|
||||
}
|
||||
|
||||
switch (ev->evtype) {
|
||||
case XI_ButtonPress:
|
||||
qCDebug(lcQpaXInput, "XI2 mouse press, button %d", button);
|
||||
conn->setButton(button, true);
|
||||
handleButtonPressEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time);
|
||||
break;
|
||||
case XI_ButtonRelease:
|
||||
qCDebug(lcQpaXInput, "XI2 mouse release, button %d", button);
|
||||
conn->setButton(button, false);
|
||||
handleButtonReleaseEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time);
|
||||
break;
|
||||
case XI_Motion:
|
||||
qCDebug(lcQpaXInput, "XI2 mouse motion %d,%d", event_x, event_y);
|
||||
handleMotionNotifyEvent(event_x, event_y, root_x, root_y, modifiers, ev->time);
|
||||
break;
|
||||
default:
|
||||
qWarning() << "Unrecognized XI2 mouse event" << ev->evtype;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(event);
|
||||
Q_ASSERT(false); // this can't be
|
||||
#endif
|
||||
}
|
||||
|
||||
QXcbWindow *QXcbWindow::toWindow() { return this; }
|
||||
@ -2356,6 +2430,10 @@ bool QXcbWindow::setKeyboardGrabEnabled(bool grab)
|
||||
|
||||
bool QXcbWindow::setMouseGrabEnabled(bool grab)
|
||||
{
|
||||
#ifdef XCB_USE_XINPUT22
|
||||
if (connection()->xi2MouseEvents())
|
||||
return connection()->xi2SetMouseGrabEnabled(m_window, grab);
|
||||
#endif
|
||||
if (grab && !connection()->canGrab())
|
||||
return false;
|
||||
|
||||
|
@ -131,6 +131,7 @@ public:
|
||||
void handleFocusInEvent(const xcb_focus_in_event_t *event) Q_DECL_OVERRIDE;
|
||||
void handleFocusOutEvent(const xcb_focus_out_event_t *event) Q_DECL_OVERRIDE;
|
||||
void handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) Q_DECL_OVERRIDE;
|
||||
void handleXIMouseEvent(xcb_ge_event_t *) Q_DECL_OVERRIDE;
|
||||
|
||||
QXcbWindow *toWindow() Q_DECL_OVERRIDE;
|
||||
|
||||
@ -199,6 +200,15 @@ protected:
|
||||
void doFocusIn();
|
||||
void doFocusOut();
|
||||
|
||||
void handleButtonPressEvent(int event_x, int event_y, int root_x, int root_y,
|
||||
int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp);
|
||||
|
||||
void handleButtonReleaseEvent(int event_x, int event_y, int root_x, int root_y,
|
||||
int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp);
|
||||
|
||||
void handleMotionNotifyEvent(int event_x, int event_y, int root_x, int root_y,
|
||||
Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp);
|
||||
|
||||
xcb_window_t m_window;
|
||||
|
||||
QXcbScreen *m_xcbScreen;
|
||||
|
@ -59,6 +59,11 @@ contains(QT_CONFIG, xcb-xlib) {
|
||||
DEFINES += XCB_USE_XINPUT2
|
||||
SOURCES += qxcbconnection_xi2.cpp
|
||||
LIBS += -lXi
|
||||
!isEmpty(QMAKE_LIBXI_VERSION_MAJOR) {
|
||||
DEFINES += LIBXI_MAJOR=$$QMAKE_LIBXI_VERSION_MAJOR \
|
||||
LIBXI_MINOR=$$QMAKE_LIBXI_VERSION_MINOR \
|
||||
LIBXI_PATCH=$$QMAKE_LIBXI_VERSION_PATCH
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user