9#include "QtCore/qmetaobject.h"
10#include "QtCore/qmath.h"
11#include <QtGui/qpointingdevice.h>
12#include <QtGui/private/qpointingdevice_p.h>
13#include <qpa/qwindowsysteminterface_p.h>
16#include <xcb/xinput.h>
18#if QT_CONFIG(gestures)
19#define QT_XCB_HAS_TOUCHPAD_GESTURES (XCB_INPUT_MINOR_VERSION >= 4)
25#if QT_XCB_HAS_TOUCHPAD_GESTURES
26using qt_xcb_input_pinch_event_t = xcb_input_gesture_pinch_begin_event_t;
27using qt_xcb_input_swipe_event_t = xcb_input_gesture_swipe_begin_event_t;
32 alignas(4) uint8_t
mask[8] = {};
38 mask[bit >> 3] |= 1 << (bit & 7);
46 xiEventMask.
header.deviceid = XCB_INPUT_DEVICE_ALL;
47 xiEventMask.header.mask_len = 1;
48 setXcbMask(xiEventMask.mask, XCB_INPUT_HIERARCHY);
49 setXcbMask(xiEventMask.mask, XCB_INPUT_DEVICE_CHANGED);
50 setXcbMask(xiEventMask.mask, XCB_INPUT_PROPERTY);
73#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
84 mask.header.deviceid = XCB_INPUT_DEVICE_ALL;
85 mask.header.mask_len = 2;
86 xcb_void_cookie_t cookie =
90 qCDebug(lcQpaXInput,
"failed to select events, window %x, error code %d",
window,
error->error_code);
102#if QT_CONFIG(tabletevent)
201 master ? master->seatName() :
QString(),
207 devPriv->toolId = toolId;
209 devPriv->seatName = master->seatName();
226void QXcbConnection::xi2SetupSlavePointerDevice(
void *
info,
bool removeExisting,
QPointingDevice *master)
228 auto *deviceInfo =
reinterpret_cast<xcb_input_xi_device_info_t *
>(
info);
229 if (removeExisting) {
230#if QT_CONFIG(tabletevent)
231 for (
int i = 0;
i < m_tabletData.size(); ++
i) {
232 if (m_tabletData.at(
i).deviceId == deviceInfo->deviceid) {
233 m_tabletData.remove(
i);
238 m_touchDevices.
remove(deviceInfo->deviceid);
242 xcb_input_xi_device_info_name_length(deviceInfo));
244 m_xiSlavePointerIds.
append(deviceInfo->deviceid);
245 qCDebug(lcQpaXInputDevices) <<
"input device " <<
name <<
"ID" << deviceInfo->deviceid;
246#if QT_CONFIG(tabletevent)
247 TabletData tabletData;
251 auto scrollingDevice = [&]() {
252 if (!scrollingDeviceP)
255 return scrollingDeviceP;
258 int buttonCount = 32;
259 auto classes_it = xcb_input_xi_device_info_classes_iterator(deviceInfo);
260 for (; classes_it.rem; xcb_input_device_class_next(&classes_it)) {
261 xcb_input_device_class_t *classinfo = classes_it.data;
262 switch (classinfo->type) {
263 case XCB_INPUT_DEVICE_CLASS_TYPE_VALUATOR: {
264 auto *vci =
reinterpret_cast<xcb_input_valuator_class_t *
>(classinfo);
265 const int valuatorAtom =
qatom(vci->label);
267#if QT_CONFIG(tabletevent)
269 TabletData::ValuatorClassInfo
info;
272 info.number = vci->number;
273 tabletData.valuatorInfo[valuatorAtom] =
info;
277 scrollingDevice()->lastScrollPosition.setX(
fixed3232ToReal(vci->value));
279 scrollingDevice()->lastScrollPosition.setY(
fixed3232ToReal(vci->value));
282 case XCB_INPUT_DEVICE_CLASS_TYPE_SCROLL: {
283 auto *sci =
reinterpret_cast<xcb_input_scroll_class_t *
>(classinfo);
284 if (sci->scroll_type == XCB_INPUT_SCROLL_TYPE_VERTICAL) {
285 auto dev = scrollingDevice();
287 dev->verticalIndex = sci->number;
289 }
else if (sci->scroll_type == XCB_INPUT_SCROLL_TYPE_HORIZONTAL) {
290 auto dev = scrollingDevice();
292 dev->horizontalIndex = sci->number;
297 case XCB_INPUT_DEVICE_CLASS_TYPE_BUTTON: {
298 auto *bci =
reinterpret_cast<xcb_input_button_class_t *
>(classinfo);
299 xcb_atom_t *labels =
nullptr;
300 if (bci->num_buttons >= 5) {
301 labels = xcb_input_button_class_labels(bci);
302 xcb_atom_t label4 = labels[3];
303 xcb_atom_t label5 = labels[4];
310 if (bci->num_buttons >= 7) {
311 xcb_atom_t label6 = labels[5];
312 xcb_atom_t label7 = labels[6];
316 buttonCount = bci->num_buttons;
317 qCDebug(lcQpaXInputDevices,
" has %d buttons", bci->num_buttons);
320 case XCB_INPUT_DEVICE_CLASS_TYPE_KEY:
321 qCDebug(lcQpaXInputDevices) <<
" it's a keyboard";
323 case XCB_INPUT_DEVICE_CLASS_TYPE_TOUCH:
324#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
325 case XCB_INPUT_DEVICE_CLASS_TYPE_GESTURE:
330 qCDebug(lcQpaXInputDevices) <<
" has class" << classinfo->type;
334 bool isTablet =
false;
335#if QT_CONFIG(tabletevent)
344 QString dbgType =
"UNKNOWN"_L1;
345 if (nameLower.contains(
"eraser")) {
348 dbgType =
"eraser"_L1;
349 }
else if (nameLower.contains(
"cursor") && !(nameLower.contains(
"cursor controls") && nameLower.contains(
"trackball"))) {
352 dbgType =
"cursor"_L1;
353 }
else if (nameLower.contains(
"wacom") && nameLower.contains(
"finger touch")) {
355 }
else if ((nameLower.contains(
"pen") || nameLower.contains(
"stylus")) && isTablet) {
358 }
else if (nameLower.contains(
"wacom") && isTablet && !nameLower.contains(
"touch")) {
362 }
else if (nameLower.contains(
"aiptek") ) {
367 }
else if (nameLower.contains(
"waltop") && nameLower.contains(
"tablet")) {
373 }
else if (nameLower.contains(
"uc-logic") && isTablet) {
376 }
else if (nameLower.contains(
"ugee")) {
385 tabletData.deviceId = deviceInfo->deviceid;
386 tabletData.name =
name;
387 m_tabletData.
append(tabletData);
388 qCDebug(lcQpaXInputDevices) <<
" it's a tablet with pointer type" << dbgType;
395 Q_ASSERT(deviceInfo->deviceid == tabletData.deviceId);
397 tabletData.name, deviceInfo->deviceid, 0, 0, tabletData.serialId,
398 tabletData.pointerType, capsOverride);
403 if (scrollingDeviceP) {
405 scrollingDeviceP->legacyOrientations &= ~scrollingDeviceP->orientations;
406 qCDebug(lcQpaXInputDevices) <<
" it's a scrolling device";
410 TouchDeviceData *dev = populateTouchDevices(deviceInfo, scrollingDeviceP, &used);
411 if (dev && lcQpaXInputDevices().isDebugEnabled()) {
413 qCDebug(lcQpaXInputDevices,
" it's a touchscreen with type %d capabilities 0x%X max touch points %d",
414 int(dev->qtTouchDevice->type()),
qint32(dev->qtTouchDevice->capabilities()),
415 dev->qtTouchDevice->maximumPoints());
417 qCDebug(lcQpaXInputDevices,
" it's a touchpad with type %d capabilities 0x%X max touch points %d size %f x %f",
418 int(dev->qtTouchDevice->type()),
qint32(dev->qtTouchDevice->capabilities()),
419 dev->qtTouchDevice->maximumPoints(),
420 dev->size.width(), dev->size.height());
425 qCDebug(lcQpaXInputDevices) <<
" it's a mouse";
427 if (scrollingDeviceP) {
428 scrollingDeviceP->capabilities |= caps;
429 scrollingDeviceP->buttonCount = buttonCount;
431 scrollingDeviceP->seatName = master->seatName();
436 name, deviceInfo->deviceid,
442 if (!used && scrollingDeviceP) {
455void QXcbConnection::xi2SetupDevices()
457 m_xiMasterPointerIds.
clear();
461 qCDebug(lcQpaXInputDevices) <<
"failed to query devices";
470 auto newOrKeep = [&previousDevices](
qint64 systemId) {
472 return !previousDevices.removeIf([systemId](
const QInputDevice *dev) {
480 auto it = xcb_input_xi_query_device_infos_iterator(
reply.get());
481 for (;
it.rem; xcb_input_xi_device_info_next(&
it)) {
482 xcb_input_xi_device_info_t *deviceInfo =
it.data;
483 switch (deviceInfo->type) {
484 case XCB_INPUT_DEVICE_TYPE_MASTER_KEYBOARD: {
485 if (newOrKeep(deviceInfo->deviceid)) {
488 QString::number(deviceInfo->deviceid << 16 | deviceInfo->attachment, 16),
this);
492 case XCB_INPUT_DEVICE_TYPE_MASTER_POINTER: {
493 m_xiMasterPointerIds.
append(deviceInfo->deviceid);
494 if (newOrKeep(deviceInfo->deviceid)) {
497 32,
QString::number(deviceInfo->attachment << 16 | deviceInfo->deviceid, 16),
this);
507 it = xcb_input_xi_query_device_infos_iterator(
reply.get());
508 for (;
it.rem; xcb_input_xi_device_info_next(&
it)) {
509 xcb_input_xi_device_info_t *deviceInfo =
it.data;
510 switch (deviceInfo->type) {
511 case XCB_INPUT_DEVICE_TYPE_MASTER_KEYBOARD:
512 case XCB_INPUT_DEVICE_TYPE_MASTER_POINTER:
515 case XCB_INPUT_DEVICE_TYPE_SLAVE_POINTER: {
516 if (newOrKeep(deviceInfo->deviceid)) {
517 m_xiSlavePointerIds.
append(deviceInfo->deviceid);
520 xi2SetupSlavePointerDevice(deviceInfo,
false, qobject_cast<QPointingDevice *>(master));
523 case XCB_INPUT_DEVICE_TYPE_SLAVE_KEYBOARD: {
524 if (newOrKeep(deviceInfo->deviceid)) {
528 QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)), deviceInfo->deviceid,
532 case XCB_INPUT_DEVICE_TYPE_FLOATING_SLAVE:
538 qCDebug(lcQpaXInputDevices) <<
"removed" << previousDevices;
540 const auto id = (*it)->systemId();
542 m_touchDevices.
remove(
id);
546 if (m_xiMasterPointerIds.
size() > 1)
547 qCDebug(lcQpaXInputDevices) <<
"multi-pointer X detected";
550QXcbConnection::TouchDeviceData *QXcbConnection::touchDeviceForId(
int id)
552 TouchDeviceData *dev =
nullptr;
554 dev = &m_touchDevices[
id];
560 auto *deviceInfo =
reinterpret_cast<xcb_input_xi_device_info_t *
>(
info);
561 QPointingDevice::Capabilities caps;
563 int maxTouchPoints = 1;
564 bool isTouchDevice =
false;
565 bool hasRelativeCoords =
false;
567 auto classes_it = xcb_input_xi_device_info_classes_iterator(deviceInfo);
568 for (; classes_it.rem; xcb_input_device_class_next(&classes_it)) {
569 xcb_input_device_class_t *classinfo = classes_it.data;
570 switch (classinfo->type) {
571 case XCB_INPUT_DEVICE_CLASS_TYPE_TOUCH: {
572 auto *tci =
reinterpret_cast<xcb_input_touch_class_t *
>(classinfo);
573 maxTouchPoints = tci->num_touches;
574 qCDebug(lcQpaXInputDevices,
" has touch class with mode %d", tci->mode);
576 case XCB_INPUT_TOUCH_MODE_DEPENDENT:
579 case XCB_INPUT_TOUCH_MODE_DIRECT:
585#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
586 case XCB_INPUT_DEVICE_CLASS_TYPE_GESTURE: {
590 auto *gci =
reinterpret_cast<xcb_input_gesture_class_t *
>(classinfo);
591 maxTouchPoints = gci->num_touches;
592 qCDebug(lcQpaXInputDevices,
" has gesture class");
597 case XCB_INPUT_DEVICE_CLASS_TYPE_VALUATOR: {
598 auto *vci =
reinterpret_cast<xcb_input_valuator_class_t *
>(classinfo);
601 TouchDeviceData::ValuatorClassInfo
info;
604 info.number = vci->number;
605 info.label = valuatorAtom;
606 dev.valuatorInfo.append(
info);
610 const int vciResolution = vci->resolution ? vci->resolution : 1;
616 dev.providesTouchOrientation =
true;
620 hasRelativeCoords =
true;
623 hasRelativeCoords =
true;
642 if (dev.size.width() < 10 || dev.size.height() < 10 ||
643 dev.size.width() > 10000 || dev.size.height() > 10000)
644 dev.size =
QSizeF(130, 110);
652 if (scrollingDeviceP) {
654 scrollingDeviceP->deviceType =
type;
656 scrollingDeviceP->capabilities |= caps;
657 scrollingDeviceP->maximumTouchPoints = maxTouchPoints;
658 scrollingDeviceP->buttonCount = 3;
659 scrollingDeviceP->seatName = master->seatName();
662 qCDebug(lcQpaXInputDevices) <<
"unexpectedly missing RelVert/HorizWheel atoms for touchpad with scroll capability" << dev.qtTouchDevice;
666 xcb_input_xi_device_info_name_length(deviceInfo)),
667 deviceInfo->deviceid,
673 m_touchDevices[deviceInfo->deviceid] = dev;
674 isTouchDevice =
true;
677 return isTouchDevice ? &m_touchDevices[deviceInfo->deviceid] :
nullptr;
685void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *
event)
689 if (m_xiSlavePointerIds.
contains(xiEvent->deviceid) && xiEvent->event_type != XCB_INPUT_PROPERTY) {
690 if (!m_duringSystemMoveResize)
692 if (xiEvent->event == XCB_NONE)
695 if (xiEvent->event_type == XCB_INPUT_BUTTON_RELEASE
696 && xiEvent->detail == XCB_BUTTON_INDEX_1 ) {
698 }
else if (xiEvent->event_type == XCB_INPUT_TOUCH_END) {
705 int sourceDeviceId = xiEvent->deviceid;
707 xcb_input_enter_event_t *xiEnterEvent =
nullptr;
710 switch (xiEvent->event_type) {
711 case XCB_INPUT_BUTTON_PRESS:
712 case XCB_INPUT_BUTTON_RELEASE:
713 case XCB_INPUT_MOTION:
714 case XCB_INPUT_TOUCH_BEGIN:
715 case XCB_INPUT_TOUCH_UPDATE:
716 case XCB_INPUT_TOUCH_END:
718 xiDeviceEvent = xiEvent;
720 sourceDeviceId = xiDeviceEvent->sourceid;
723#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
724 case XCB_INPUT_GESTURE_PINCH_BEGIN:
725 case XCB_INPUT_GESTURE_PINCH_UPDATE:
726 case XCB_INPUT_GESTURE_PINCH_END:
727 xi2HandleGesturePinchEvent(
event);
729 case XCB_INPUT_GESTURE_SWIPE_BEGIN:
730 case XCB_INPUT_GESTURE_SWIPE_UPDATE:
731 case XCB_INPUT_GESTURE_SWIPE_END:
732 xi2HandleGestureSwipeEvent(
event);
735 case XCB_INPUT_ENTER:
736 case XCB_INPUT_LEAVE: {
737 xiEnterEvent =
reinterpret_cast<xcb_input_enter_event_t *
>(
event);
739 sourceDeviceId = xiEnterEvent->sourceid;
742 case XCB_INPUT_HIERARCHY:
743 xi2HandleHierarchyEvent(
event);
745 case XCB_INPUT_DEVICE_CHANGED:
746 xi2HandleDeviceChangedEvent(
event);
753 if (eventListener->handleNativeEvent(
reinterpret_cast<xcb_generic_event_t *
>(
event)))
757#if QT_CONFIG(tabletevent)
760 QXcbConnection::TabletData *tablet = tabletDataForDevice(sourceDeviceId);
761 if (tablet && xi2HandleTabletEvent(
event, tablet))
769 qCDebug(lcQpaXInputEvents) <<
"scroll event from unregistered device" << sourceDeviceId;
772 switch (xiDeviceEvent->event_type) {
773 case XCB_INPUT_BUTTON_PRESS:
774 case XCB_INPUT_BUTTON_RELEASE:
775 case XCB_INPUT_MOTION:
776 if (eventListener && !(xiDeviceEvent->flags & XCB_INPUT_POINTER_EVENT_FLAGS_POINTER_EMULATED))
777 eventListener->handleXIMouseEvent(
event);
780 case XCB_INPUT_TOUCH_BEGIN:
781 case XCB_INPUT_TOUCH_UPDATE:
782 case XCB_INPUT_TOUCH_END:
783 if (
Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
784 qCDebug(lcQpaXInputEvents,
"XI2 touch event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f on window %x",
785 event->event_type, xiDeviceEvent->sequence, xiDeviceEvent->detail,
789 xi2ProcessTouch(xiDeviceEvent, platformWindow);
791 if (TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid))
792 dev->touchPoints.remove((xiDeviceEvent->detail % INT_MAX));
796 }
else if (xiEnterEvent && eventListener) {
797 switch (xiEnterEvent->event_type) {
798 case XCB_INPUT_ENTER:
799 case XCB_INPUT_LEAVE:
800 eventListener->handleXIEnterLeave(
event);
808 auto device = touchDeviceForId(
id);
812void QXcbConnection::xi2ProcessTouch(
void *xiDevEvent,
QXcbWindow *platformWindow)
814 auto *xiDeviceEvent =
reinterpret_cast<xcb_input_touch_begin_event_t *
>(xiDevEvent);
815 TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid);
817 qCDebug(lcQpaXInputEvents) <<
"didn't find the dev for given sourceid - " << xiDeviceEvent->sourceid
818 <<
", try to repopulate xi2 devices";
820 dev = touchDeviceForId(xiDeviceEvent->sourceid);
822 qCDebug(lcQpaXInputEvents) <<
"still can't find the dev for it, give up.";
826 const bool firstTouch = dev->touchPoints.isEmpty();
827 if (xiDeviceEvent->event_type == XCB_INPUT_TOUCH_BEGIN) {
829 tp.
id = xiDeviceEvent->detail % INT_MAX;
832 dev->touchPoints[tp.
id] = tp;
841 for (
const TouchDeviceData::ValuatorClassInfo &vci :
std::as_const(dev->valuatorInfo)) {
843 if (!xi2GetValuatorValueIfSet(xiDeviceEvent, vci.number, &
value))
845 if (
Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
846 qCDebug(lcQpaXInputEvents,
" valuator %20s value %lf from range %lf -> %lf",
852 qreal valuatorNormalized = (
value - vci.min) / (vci.max - vci.min);
854 nx = valuatorNormalized;
856 ny = valuatorNormalized;
858 nx = valuatorNormalized;
860 ny = valuatorNormalized;
862 nx = valuatorNormalized;
864 ny = valuatorNormalized;
868 w = valuatorNormalized *
qHypot(sw, sh);
872 h = valuatorNormalized *
qHypot(sw, sh);
880 while (
value > vci.max)
881 value -= 2 * vci.max;
885 touchPoint.
pressure = valuatorNormalized;
898 if (xiDeviceEvent->event_type != XCB_INPUT_TOUCH_END) {
899 if (!dev->providesTouchOrientation) {
913 switch (xiDeviceEvent->event_type) {
914 case XCB_INPUT_TOUCH_BEGIN:
916 dev->firstPressedPosition =
QPointF(
x,
y);
919 dev->pointPressedPosition.insert(touchPoint.
id,
QPointF(
x,
y));
924 xcb_input_xi_allow_events(
xcb_connection(), XCB_CURRENT_TIME, xiDeviceEvent->deviceid,
925 XCB_INPUT_EVENT_MODE_ACCEPT_TOUCH,
926 xiDeviceEvent->detail, xiDeviceEvent->event);
929 case XCB_INPUT_TOUCH_UPDATE:
931 qreal dx = (
nx - dev->firstPressedNormalPosition.x()) *
933 qreal dy = (
ny - dev->firstPressedNormalPosition.y()) *
935 x = dev->firstPressedPosition.x() + dx;
936 y = dev->firstPressedPosition.y() + dy;
941 dev->pointPressedPosition[touchPoint.
id] =
QPointF(
x,
y);
945 xiDeviceEvent->event == m_startSystemMoveResizeInfo.window &&
946 xiDeviceEvent->sourceid == m_startSystemMoveResizeInfo.deviceid &&
947 xiDeviceEvent->detail == m_startSystemMoveResizeInfo.pointid) {
950 xcb_input_xi_allow_events(
xcb_connection(), XCB_CURRENT_TIME, xiDeviceEvent->deviceid,
951 XCB_INPUT_EVENT_MODE_REJECT_TOUCH,
952 xiDeviceEvent->detail, xiDeviceEvent->event);
953 window->doStartSystemMoveResize(
QPoint(
x,
y), m_startSystemMoveResizeInfo.edges);
954 m_startSystemMoveResizeInfo.window = XCB_NONE;
958 case XCB_INPUT_TOUCH_END:
961 qreal dx = (
nx - dev->firstPressedNormalPosition.x()) *
963 qreal dy = (
ny - dev->firstPressedNormalPosition.y()) *
965 x = dev->firstPressedPosition.x() + dx;
966 y = dev->firstPressedPosition.y() + dy;
968 dev->pointPressedPosition.remove(touchPoint.
id);
973 if (
Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
974 qCDebug(lcQpaXInputEvents) <<
" touchpoint " << touchPoint.
id <<
" state " << touchPoint.
state <<
" pos norm " << touchPoint.
normalPosition <<
975 " area " << touchPoint.
area <<
" pressure " << touchPoint.
pressure;
980 dev->touchPoints.remove(touchPoint.
id);
990 for (; devIt != m_touchDevices.
constEnd(); ++devIt) {
991 TouchDeviceData deviceData = devIt.value();
993 auto pointIt = deviceData.touchPoints.
constBegin();
994 for (; pointIt != deviceData.touchPoints.
constEnd(); ++pointIt) {
997 m_startSystemMoveResizeInfo.window =
window;
998 m_startSystemMoveResizeInfo.deviceid = devIt.key();
999 m_startSystemMoveResizeInfo.pointid = pointIt.key();
1000 m_startSystemMoveResizeInfo.edges = edges;
1002 qCDebug(lcQpaXInputDevices) <<
"triggered system move or resize from touch";
1013 qCDebug(lcQpaXInputDevices) <<
"sending client message NET_WM_MOVERESIZE_CANCEL to window: " <<
window;
1014 m_startSystemMoveResizeInfo.window = XCB_NONE;
1017 xcb_client_message_event_t xev;
1018 xev.response_type = XCB_CLIENT_MESSAGE;
1019 xev.type = moveResize;
1023 xev.data.data32[0] = 0;
1024 xev.data.data32[1] = 0;
1025 xev.data.data32[2] = 11;
1026 xev.data.data32[3] = 0;
1027 xev.data.data32[4] = 0;
1029 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY,
1030 (
const char *)&xev);
1032 m_duringSystemMoveResize =
false;
1037 return m_duringSystemMoveResize;
1042 m_duringSystemMoveResize = during;
1050 uint8_t
mask[8] = {};
1061#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
1072 for (
int id : std::as_const(m_xiMasterPointerIds)) {
1073 xcb_generic_error_t *
error =
nullptr;
1074 auto cookie = xcb_input_xi_grab_device(
xcb_connection(),
w, XCB_CURRENT_TIME, XCB_CURSOR_NONE,
id,
1075 XCB_INPUT_GRAB_MODE_22_ASYNC, XCB_INPUT_GRAB_MODE_22_ASYNC,
1076 false, 2,
reinterpret_cast<uint32_t *
>(
mask));
1079 qCDebug(lcQpaXInput,
"failed to grab events for device %d on window %x"
1080 "(error code %d)",
id,
w,
error->error_code);
1090 for (
int id : std::as_const(m_xiMasterPointerIds)) {
1091 auto cookie = xcb_input_xi_ungrab_device_checked(
xcb_connection(), XCB_CURRENT_TIME,
id);
1094 qCDebug(lcQpaXInput,
"XIUngrabDevice failed - id: %d (error code %d)",
id,
error->error_code);
1111void QXcbConnection::xi2HandleHierarchyEvent(
void *
event)
1113 auto *xiEvent =
reinterpret_cast<xcb_input_hierarchy_event_t *
>(
event);
1118 if (xiEvent->flags & (XCB_INPUT_HIERARCHY_MASK_MASTER_ADDED |
1119 XCB_INPUT_HIERARCHY_MASK_MASTER_REMOVED |
1120 XCB_INPUT_HIERARCHY_MASK_SLAVE_REMOVED |
1121 XCB_INPUT_HIERARCHY_MASK_SLAVE_ADDED))
1125#if QT_XCB_HAS_TOUCHPAD_GESTURES
1126void QXcbConnection::xi2HandleGesturePinchEvent(
void *
event)
1128 auto *xiEvent =
reinterpret_cast<qt_xcb_input_pinch_event_t *
>(
event);
1130 if (
Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) {
1131 qCDebug(lcQpaXInputEvents,
"XI2 gesture event type %d seq %d fingers %d pos %6.1f, "
1132 "%6.1f root pos %6.1f, %6.1f delta_angle %6.1f scale %6.1f on window %x",
1133 xiEvent->event_type, xiEvent->sequence, xiEvent->detail,
1140 if (!platformWindow)
1145 TouchDeviceData *dev = touchDeviceForId(xiEvent->sourceid);
1148 uint32_t fingerCount = xiEvent->detail;
1150 switch (xiEvent->event_type) {
1151 case XCB_INPUT_GESTURE_PINCH_BEGIN:
1155 xcb_input_xi_allow_events(
xcb_connection(), XCB_CURRENT_TIME, xiEvent->deviceid,
1156 XCB_INPUT_EVENT_MODE_ASYNC_DEVICE, 0, xiEvent->event);
1158 m_lastPinchScale = 1.0;
1167 case XCB_INPUT_GESTURE_PINCH_UPDATE: {
1171 m_lastPinchScale =
scale;
1178 platformWindow->
window(), xiEvent->time, dev->qtTouchDevice,
1184 if (rotationDelta != 0) {
1186 platformWindow->
window(), xiEvent->time, dev->qtTouchDevice,
1193 if (scaleDelta != 0) {
1195 platformWindow->
window(), xiEvent->time, dev->qtTouchDevice,
1204 case XCB_INPUT_GESTURE_PINCH_END:
1215void QXcbConnection::xi2HandleGestureSwipeEvent(
void *
event)
1217 auto *xiEvent =
reinterpret_cast<qt_xcb_input_swipe_event_t *
>(
event);
1219 if (
Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) {
1220 qCDebug(lcQpaXInputEvents,
"XI2 gesture event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f on window %x",
1221 xiEvent->event_type, xiEvent->sequence, xiEvent->detail,
1227 if (!platformWindow)
1232 TouchDeviceData *dev = touchDeviceForId(xiEvent->sourceid);
1235 uint32_t fingerCount = xiEvent->detail;
1237 switch (xiEvent->event_type) {
1238 case XCB_INPUT_GESTURE_SWIPE_BEGIN:
1242 xcb_input_xi_allow_events(
xcb_connection(), XCB_CURRENT_TIME, xiEvent->deviceid,
1243 XCB_INPUT_EVENT_MODE_ASYNC_DEVICE, 0, xiEvent->event);
1252 case XCB_INPUT_GESTURE_SWIPE_UPDATE: {
1256 if (xiEvent->delta_x != 0 || xiEvent->delta_y != 0) {
1258 platformWindow->
window(), xiEvent->time, dev->qtTouchDevice,
1266 case XCB_INPUT_GESTURE_SWIPE_END:
1278void QXcbConnection::xi2HandleGesturePinchEvent(
void*) {}
1279void QXcbConnection::xi2HandleGestureSwipeEvent(
void*) {}
1282void QXcbConnection::xi2HandleDeviceChangedEvent(
void *
event)
1284 auto *xiEvent =
reinterpret_cast<xcb_input_device_changed_event_t *
>(
event);
1285 switch (xiEvent->reason) {
1286 case XCB_INPUT_CHANGE_REASON_DEVICE_CHANGE: {
1288 if (m_xiMasterPointerIds.
contains(xiEvent->deviceid) || m_xiSlavePointerIds.
contains(xiEvent->deviceid))
1293 auto it = xcb_input_xi_query_device_infos_iterator(
reply.get());
1294 xi2SetupSlavePointerDevice(
it.data);
1297 case XCB_INPUT_CHANGE_REASON_SLAVE_SWITCH: {
1298 if (
auto *scrollingDevice = scrollingDeviceForId(xiEvent->sourceid))
1299 xi2UpdateScrollingDevice(scrollingDevice);
1303 qCDebug(lcQpaXInputEvents,
"unknown device-changed-event (device %d)", xiEvent->sourceid);
1308void QXcbConnection::xi2UpdateScrollingDevice(
QInputDevice *dev)
1317 qCDebug(lcQpaXInputDevices,
"scrolling device %lld no longer present", scrollingDevice->systemId);
1321 if (lcQpaXInputEvents().isDebugEnabled())
1322 lastScrollPosition = scrollingDevice->lastScrollPosition;
1324 xcb_input_xi_device_info_t *deviceInfo = xcb_input_xi_query_device_infos_iterator(
reply.get()).data;
1325 auto classes_it = xcb_input_xi_device_info_classes_iterator(deviceInfo);
1326 for (; classes_it.rem; xcb_input_device_class_next(&classes_it)) {
1327 xcb_input_device_class_t *
classInfo = classes_it.data;
1328 if (
classInfo->type == XCB_INPUT_DEVICE_CLASS_TYPE_VALUATOR) {
1329 auto *vci =
reinterpret_cast<xcb_input_valuator_class_t *
>(
classInfo);
1330 const int valuatorAtom =
qatom(vci->label);
1332 scrollingDevice->lastScrollPosition.setX(
fixed3232ToReal(vci->value));
1334 scrollingDevice->lastScrollPosition.setY(
fixed3232ToReal(vci->value));
1337 if (
Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled() && lastScrollPosition != scrollingDevice->lastScrollPosition))
1338 qCDebug(lcQpaXInputEvents,
"scrolling device %lld moved from (%f, %f) to (%f, %f)", scrollingDevice->systemId,
1339 lastScrollPosition.
x(), lastScrollPosition.
y(),
1340 scrollingDevice->lastScrollPosition.x(),
1341 scrollingDevice->lastScrollPosition.y());
1349 xi2UpdateScrollingDevice(
const_cast<QInputDevice *
>(dev));
1358 return qobject_cast<QXcbScrollingDevice *>(
const_cast<QPointingDevice *
>(dev));
1370 if (xiDeviceEvent->event_type == XCB_INPUT_MOTION && scrollingDevice->orientations) {
1376 if (xi2GetValuatorValueIfSet(xiDeviceEvent, scrollingDevice->verticalIndex, &
value)) {
1377 double delta = scrollingDevice->lastScrollPosition.
y() -
value;
1378 scrollingDevice->lastScrollPosition.setY(
value);
1379 angleDelta.
setY((delta / scrollingDevice->verticalIncrement) * 120);
1383 if (scrollingDevice->verticalIncrement > 15)
1384 rawDelta.setY(delta);
1385 else if (scrollingDevice->verticalIncrement < -15)
1386 rawDelta.setY(-delta);
1390 if (xi2GetValuatorValueIfSet(xiDeviceEvent, scrollingDevice->horizontalIndex, &
value)) {
1391 double delta = scrollingDevice->lastScrollPosition.x() -
value;
1392 scrollingDevice->lastScrollPosition.setX(
value);
1393 angleDelta.
setX((delta / scrollingDevice->horizontalIncrement) * 120);
1395 if (scrollingDevice->horizontalIncrement > 15)
1396 rawDelta.setX(delta);
1397 else if (scrollingDevice->horizontalIncrement < -15)
1398 rawDelta.setX(-delta);
1401 if (!angleDelta.
isNull()) {
1407 rawDelta = rawDelta.transposed();
1409 qCDebug(lcQpaXInputEvents) <<
"scroll wheel from device" << scrollingDevice->systemId
1410 <<
"@ window pos" << local <<
"delta px" << rawDelta <<
"angle" << angleDelta;
1415 }
else if (xiDeviceEvent->event_type == XCB_INPUT_BUTTON_RELEASE && scrollingDevice->legacyOrientations) {
1418 if (scrollingDevice->legacyOrientations &
Qt::Vertical) {
1419 if (xiDeviceEvent->detail == 4)
1420 angleDelta.
setY(120);
1421 else if (xiDeviceEvent->detail == 5)
1422 angleDelta.
setY(-120);
1425 if (xiDeviceEvent->detail == 6)
1426 angleDelta.
setX(120);
1427 else if (xiDeviceEvent->detail == 7)
1428 angleDelta.
setX(-120);
1430 if (!angleDelta.
isNull()) {
1436 qCDebug(lcQpaXInputEvents) <<
"scroll wheel (button" << xiDeviceEvent->detail <<
") @ window pos" << local <<
"delta angle" << angleDelta;
1447 for (
int i = 0;
i < maskLen;
i++) {
1449 if ((maskPtr[
i] & (1 <<
number)) == 0)
1452 for (
int j = 0;
j < 8;
j++) {
1455 if (maskPtr[
i] & (1 <<
j))
1463bool QXcbConnection::xi2GetValuatorValueIfSet(
const void *
event,
int valuatorNum,
double *
value)
1466 auto *buttonsMaskAddr =
reinterpret_cast<const unsigned char *
>(&xideviceevent[1]);
1467 auto *valuatorsMaskAddr = buttonsMaskAddr + xideviceevent->buttons_len * 4;
1468 auto *valuatorsValuesAddr =
reinterpret_cast<const xcb_input_fp3232_t *
>(valuatorsMaskAddr + xideviceevent->valuators_len * 4);
1470 int valuatorOffset =
xi2ValuatorOffset(valuatorsMaskAddr, xideviceevent->valuators_len, valuatorNum);
1471 if (valuatorOffset < 0)
1474 *
value = valuatorsValuesAddr[valuatorOffset].integral;
1475 *
value += ((double)valuatorsValuesAddr[valuatorOffset].frac / (1 << 16) / (1 << 16));
1493#if QT_CONFIG(tabletevent)
1494bool QXcbConnection::xi2HandleTabletEvent(
const void *
event, TabletData *tabletData)
1496 bool handled =
true;
1499 switch (xiDeviceEvent->event_type) {
1500 case XCB_INPUT_BUTTON_PRESS: {
1502 tabletData->buttons |=
b;
1503 xi2ReportTabletEvent(
event, tabletData);
1506 case XCB_INPUT_BUTTON_RELEASE: {
1508 tabletData->buttons ^=
b;
1509 xi2ReportTabletEvent(
event, tabletData);
1512 case XCB_INPUT_MOTION:
1513 xi2ReportTabletEvent(
event, tabletData);
1515 case XCB_INPUT_PROPERTY: {
1518 const auto *ev =
reinterpret_cast<const xcb_input_property_event_t *
>(
event);
1519 if (ev->what == XCB_INPUT_PROPERTY_FLAG_MODIFIED) {
1521 enum WacomSerialIndex {
1523 _WACSER_LAST_TOOL_SERIAL,
1524 _WACSER_LAST_TOOL_ID,
1525 _WACSER_TOOL_SERIAL,
1531 ev->property, XCB_GET_PROPERTY_TYPE_ANY, 0, 100);
1538 if (!tool &&
ptr[_WACSER_TOOL_SERIAL])
1539 tool =
ptr[_WACSER_TOOL_SERIAL];
1544 const QPointingDevice *dev = tabletToolInstance(
nullptr, tabletData->name,
1545 tabletData->deviceId,
ptr[_WACSER_USB_ID], tool,
1547 tabletData->inProximity =
true;
1548 tabletData->tool = dev->
type();
1549 tabletData->serialId =
qint64(
ptr[_WACSER_TOOL_SERIAL]);
1552 tool =
ptr[_WACSER_LAST_TOOL_ID];
1556 tool =
ptr[_WACSER_LAST_TOOL_SERIAL];
1559 tabletData->tool = dev->
type();
1560 tabletData->inProximity =
false;
1561 tabletData->serialId =
qint64(
ptr[_WACSER_LAST_TOOL_SERIAL]);
1566 qCDebug(lcQpaXInputDevices,
"XI2 proximity change on tablet %d %s (USB %x): last tool: %x id %x current tool: %x id %x %s",
1567 tabletData->deviceId,
qPrintable(tabletData->name),
ptr[_WACSER_USB_ID],
1568 ptr[_WACSER_LAST_TOOL_SERIAL],
ptr[_WACSER_LAST_TOOL_ID],
1569 ptr[_WACSER_TOOL_SERIAL],
ptr[_WACSER_TOOL_ID], toolName(tabletData->tool));
1586 return screenMin + normValue * screenSize;
1590void QXcbConnection::xi2ReportTabletEvent(
const void *
event, TabletData *tabletData)
1600 double pressure = 0, rotation = 0, tangentialPressure = 0;
1601 int xTilt = 0, yTilt = 0;
1610 QRect physicalScreenArea;
1612 const QList<QPlatformScreen *> siblings =
window->screen()->handle()->virtualSiblings();
1614 physicalScreenArea |=
screen->geometry();
1618 ite = tabletData->valuatorInfo.end();
it != ite; ++
it) {
1619 int valuator =
it.key();
1620 TabletData::ValuatorClassInfo &
classInfo(
it.value());
1628 local.setX(xcbWindow->mapFromGlobalF(
global).x());
1635 local.setY(xcbWindow->mapFromGlobalF(
global).y());
1648 switch (tabletData->tool) {
1665 if (
Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
1666 qCDebug(lcQpaXInputEvents,
"XI2 event on tablet %d with tool %s %llx type %s seq %d detail %d time %d "
1667 "pos %6.1f, %6.1f root pos %6.1f, %6.1f buttons 0x%x pressure %4.2lf tilt %d, %d rotation %6.2lf modifiers 0x%x",
1668 tabletData->deviceId, toolName(tabletData->tool), tabletData->serialId, pointerTypeName(tabletData->pointerType),
1669 ev->sequence, ev->detail, ev->time,
1671 (
int)tabletData->buttons, pressure, xTilt, yTilt, rotation, (
int)
modifiers);
1674 tabletData->buttons, pressure,
1675 xTilt, yTilt, tangentialPressure,
1679QXcbConnection::TabletData *QXcbConnection::tabletDataForDevice(
int id)
1681 for (
int i = 0;
i < m_tabletData.size(); ++
i) {
1682 if (m_tabletData.at(
i).deviceId ==
id)
1683 return &m_tabletData[
i];
IOBluetoothDevice * device
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
QByteArray toLower() const &
State
Specifies the state of this event point.
bool remove(const Key &key)
Removes the item that has the key from the hash.
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
const_iterator constBegin() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
qsizetype size() const noexcept
qsizetype removeAll(const AT &t)
void append(parameter_type t)
void deleteLater()
\threadsafe
\inmodule QtCore\reentrant
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
bool isNull() const noexcept
Returns true if both the x and y coordinates are set to 0.0 (ignoring the sign); otherwise returns fa...
\inmodule QtCore\reentrant
constexpr bool isNull() const noexcept
Returns true if both the x and y coordinates are set to 0, otherwise returns false.
constexpr QPoint transposed() const noexcept
constexpr void setY(int y) noexcept
Sets the y coordinate of this point to the given y coordinate.
constexpr void setX(int x) noexcept
Sets the x coordinate of this point to the given x coordinate.
static const QPointingDevice * tabletDevice(QInputDevice::DeviceType deviceType, QPointingDevice::PointerType pointerType, QPointingDeviceUniqueId uniqueId)
static const QPointingDevice * queryTabletDevice(QInputDevice::DeviceType deviceType, QPointingDevice::PointerType pointerType, QPointingDeviceUniqueId uniqueId, QInputDevice::Capabilities capabilities=QInputDevice::Capability::None, qint64 systemId=0)
static const QPointingDevice * pointingDeviceById(qint64 systemId)
static QPointingDevicePrivate * get(QPointingDevice *q)
QPointingDeviceUniqueId identifies a unique object, such as a tagged token or stylus,...
static QPointingDeviceUniqueId fromNumericId(qint64 id)
Constructs a unique pointer ID from numeric ID id.
The QPointingDevice class describes a device from which mouse, touch or tablet events originate.
PointerType
This enum represents what is interacting with the pointing device.
\inmodule QtCore\reentrant
constexpr qreal height() const noexcept
Returns the height of the rectangle.
constexpr qreal width() const noexcept
Returns the width of the rectangle.
constexpr QPointF center() const noexcept
Returns the center point of the rectangle.
\inmodule QtCore\reentrant
constexpr int height() const noexcept
Returns the height of the rectangle.
constexpr int width() const noexcept
Returns the width of the rectangle.
QSizeF physicalSize
the screen's physical size (in millimeters)
QRect geometry
the screen's geometry in pixels
const_iterator constBegin() const noexcept
const_iterator constEnd() const noexcept
constexpr qreal width() const noexcept
Returns the width.
constexpr qreal height() const noexcept
Returns the height.
\macro QT_RESTRICTED_CAST_FROM_ASCII
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
QString & append(QChar c)
static void setPlatformSynthesizesMouse(bool v)
static bool handleTouchEvent(QWindow *window, const QPointingDevice *device, const QList< struct TouchPoint > &points, Qt::KeyboardModifiers mods=Qt::NoModifier)
static bool handleGestureEventWithValueAndDelta(QWindow *window, ulong timestamp, const QPointingDevice *device, Qt::NativeGestureType type, qreal value, const QPointF &delta, const QPointF &local, const QPointF &global, int fingerCount=2)
static bool handleTabletEvent(QWindow *window, ulong timestamp, const QPointingDevice *device, const QPointF &local, const QPointF &global, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt, qreal tangentialPressure, qreal rotation, int z, Qt::KeyboardModifiers modifiers=Qt::NoModifier)
static bool handleGestureEventWithRealValue(QWindow *window, ulong timestamp, const QPointingDevice *device, Qt::NativeGestureType type, qreal value, const QPointF &local, const QPointF &global, int fingerCount=2)
static void registerInputDevice(const QInputDevice *device)
static bool handleGestureEvent(QWindow *window, ulong timestamp, const QPointingDevice *device, Qt::NativeGestureType type, const QPointF &local, const QPointF &global, int fingerCount=0)
static bool handleTabletEnterLeaveProximityEvent(QWindow *window, ulong timestamp, const QPointingDevice *device, bool inProximity, const QPointF &local=QPointF(), const QPointF &global=QPointF(), Qt::MouseButtons buttons={}, int xTilt=0, int yTilt=0, qreal tangentialPressure=0, qreal rotation=0, int z=0, Qt::KeyboardModifiers modifiers=Qt::NoModifier)
static bool handleWheelEvent(QWindow *window, const QPointF &local, const QPointF &global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods=Qt::NoModifier, Qt::ScrollPhase phase=Qt::NoScrollPhase, Qt::MouseEventSource source=Qt::MouseEventNotSynthesized)
@ AtomButtonHorizWheelLeft
@ AtomButtonHorizWheelRight
QXcbAtom::Atom qatom(xcb_atom_t atom) const
bool isAtLeastXI22() const
bool isAtLeastXI24() const
QByteArray atomName(xcb_atom_t atom)
xcb_connection_t * xcb_connection() const
xcb_atom_t atom(QXcbAtom::Atom qatom) const
xcb_window_t rootWindow()
QXcbKeyboard * keyboard() const
bool isDuringSystemMoveResize() const
void setTime(xcb_timestamp_t t)
QXcbConnection * connection() const
bool isTouchScreen(int id)
void xi2UpdateScrollingDevices()
bool startSystemMoveResizeForTouch(xcb_window_t window, int edges)
QXcbScreen * primaryScreen() const
void xi2SelectStateEvents()
void abortSystemMoveResize(xcb_window_t window)
void setDuringSystemMoveResize(bool during)
void xi2SelectDeviceEvents(xcb_window_t window)
QXcbWindowEventListener * windowEventListenerFromId(xcb_window_t id)
Qt::MouseButton xiToQtMouseButton(uint32_t b)
QXcbWindow * platformWindowFromId(xcb_window_t id)
bool xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
Qt::KeyboardModifiers translateModifiers(int s) const
QPoint lastPointerPosition() const
QXcbScreen * xcbScreen() const
QPoint lastPointerGlobalPosition() const
EGLImageKHR int int EGLuint64KHR * modifiers
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
const char * classInfo(const QMetaObject *metaObject, const char *key)
char qt_getEnumMetaObject(const T &)
DBusConnection const char DBusError * error
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
auto qHypot(qfloat16 x, qfloat16 y)
#define qCDebug(category,...)
static ControlElement< T > * ptr(QWidget *widget)
constexpr const T & qMin(const T &a, const T &b)
constexpr const T & qMax(const T &a, const T &b)
constexpr T qAbs(const T &t)
GLboolean GLboolean GLboolean b
GLint GLint GLint GLint GLint x
[0]
GLfloat GLfloat GLfloat w
[0]
GLenum GLuint GLintptr offset
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
GLfloat GLfloat GLfloat GLfloat h
GLenum GLenum GLenum GLenum GLenum scale
#define qPrintable(string)
QT_BEGIN_NAMESPACE constexpr void qSwap(T &value1, T &value2) noexcept(std::is_nothrow_swappable_v< T >)
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
static QPointingDevice::PointerType pointerType(unsigned currentCursor)
#define Q_XCB_REPLY(call,...)
static int xi2ValuatorOffset(const unsigned char *maskPtr, int maskLen, int number)
static void setXcbMask(uint8_t *mask, int bit)
static qreal fixed1616ToReal(xcb_input_fp1616_t val)
xcb_input_button_press_event_t qt_xcb_input_device_event_t
static qreal fixed3232ToReal(xcb_input_fp3232_t val)
obj metaObject() -> className()
bool contains(const AT &t) const noexcept