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;
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)
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;
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") ) {
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) {
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) {
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) {
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) {
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);
757#if QT_CONFIG(tabletevent)
760 QXcbConnection::TabletData *tablet = tabletDataForDevice(sourceDeviceId);
761 if (tablet && xi2HandleTabletEvent(
event, tablet))
769 qCWarning(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))
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:
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 const bool firstTouch = dev->touchPoints.isEmpty();
818 if (xiDeviceEvent->event_type == XCB_INPUT_TOUCH_BEGIN) {
820 tp.
id = xiDeviceEvent->detail % INT_MAX;
823 dev->touchPoints[tp.
id] = tp;
832 for (
const TouchDeviceData::ValuatorClassInfo &vci :
std::as_const(dev->valuatorInfo)) {
834 if (!xi2GetValuatorValueIfSet(xiDeviceEvent, vci.number, &
value))
836 if (
Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
837 qCDebug(lcQpaXInputEvents,
" valuator %20s value %lf from range %lf -> %lf",
843 qreal valuatorNormalized = (
value - vci.min) / (vci.max - vci.min);
845 nx = valuatorNormalized;
847 ny = valuatorNormalized;
849 nx = valuatorNormalized;
851 ny = valuatorNormalized;
853 nx = valuatorNormalized;
855 ny = valuatorNormalized;
859 w = valuatorNormalized *
qHypot(sw, sh);
863 h = valuatorNormalized *
qHypot(sw, sh);
871 while (
value > vci.max)
872 value -= 2 * vci.max;
876 touchPoint.
pressure = valuatorNormalized;
889 if (xiDeviceEvent->event_type != XCB_INPUT_TOUCH_END) {
890 if (!dev->providesTouchOrientation) {
904 switch (xiDeviceEvent->event_type) {
905 case XCB_INPUT_TOUCH_BEGIN:
907 dev->firstPressedPosition =
QPointF(
x,
y);
910 dev->pointPressedPosition.insert(touchPoint.
id,
QPointF(
x,
y));
915 xcb_input_xi_allow_events(
xcb_connection(), XCB_CURRENT_TIME, xiDeviceEvent->deviceid,
916 XCB_INPUT_EVENT_MODE_ACCEPT_TOUCH,
917 xiDeviceEvent->detail, xiDeviceEvent->event);
920 case XCB_INPUT_TOUCH_UPDATE:
922 qreal dx = (
nx - dev->firstPressedNormalPosition.x()) *
924 qreal dy = (
ny - dev->firstPressedNormalPosition.y()) *
926 x = dev->firstPressedPosition.x() + dx;
927 y = dev->firstPressedPosition.y() + dy;
932 dev->pointPressedPosition[touchPoint.
id] =
QPointF(
x,
y);
936 xiDeviceEvent->event == m_startSystemMoveResizeInfo.window &&
937 xiDeviceEvent->sourceid == m_startSystemMoveResizeInfo.deviceid &&
938 xiDeviceEvent->detail == m_startSystemMoveResizeInfo.pointid) {
941 xcb_input_xi_allow_events(
xcb_connection(), XCB_CURRENT_TIME, xiDeviceEvent->deviceid,
942 XCB_INPUT_EVENT_MODE_REJECT_TOUCH,
943 xiDeviceEvent->detail, xiDeviceEvent->event);
944 window->doStartSystemMoveResize(
QPoint(
x,
y), m_startSystemMoveResizeInfo.edges);
945 m_startSystemMoveResizeInfo.window = XCB_NONE;
949 case XCB_INPUT_TOUCH_END:
952 qreal dx = (
nx - dev->firstPressedNormalPosition.x()) *
954 qreal dy = (
ny - dev->firstPressedNormalPosition.y()) *
956 x = dev->firstPressedPosition.x() + dx;
957 y = dev->firstPressedPosition.y() + dy;
959 dev->pointPressedPosition.remove(touchPoint.
id);
964 if (
Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
965 qCDebug(lcQpaXInputEvents) <<
" touchpoint " << touchPoint.
id <<
" state " << touchPoint.
state <<
" pos norm " << touchPoint.
normalPosition <<
966 " area " << touchPoint.
area <<
" pressure " << touchPoint.
pressure;
971 dev->touchPoints.remove(touchPoint.
id);
981 for (; devIt != m_touchDevices.
constEnd(); ++devIt) {
982 TouchDeviceData deviceData = devIt.
value();
984 auto pointIt = deviceData.touchPoints.constBegin();
985 for (; pointIt != deviceData.touchPoints.constEnd(); ++pointIt) {
988 m_startSystemMoveResizeInfo.window =
window;
989 m_startSystemMoveResizeInfo.deviceid = devIt.
key();
990 m_startSystemMoveResizeInfo.pointid = pointIt.key();
991 m_startSystemMoveResizeInfo.edges = edges;
993 qCDebug(lcQpaXInputDevices) <<
"triggered system move or resize from touch";
1004 qCDebug(lcQpaXInputDevices) <<
"sending client message NET_WM_MOVERESIZE_CANCEL to window: " <<
window;
1005 m_startSystemMoveResizeInfo.window = XCB_NONE;
1008 xcb_client_message_event_t xev;
1009 xev.response_type = XCB_CLIENT_MESSAGE;
1010 xev.type = moveResize;
1014 xev.data.data32[0] = 0;
1015 xev.data.data32[1] = 0;
1016 xev.data.data32[2] = 11;
1017 xev.data.data32[3] = 0;
1018 xev.data.data32[4] = 0;
1020 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY,
1021 (
const char *)&xev);
1023 m_duringSystemMoveResize =
false;
1028 return m_duringSystemMoveResize;
1033 m_duringSystemMoveResize = during;
1041 uint8_t
mask[8] = {};
1052#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
1063 for (
int id : std::as_const(m_xiMasterPointerIds)) {
1064 xcb_generic_error_t *
error =
nullptr;
1065 auto cookie = xcb_input_xi_grab_device(
xcb_connection(),
w, XCB_CURRENT_TIME, XCB_CURSOR_NONE,
id,
1066 XCB_INPUT_GRAB_MODE_22_ASYNC, XCB_INPUT_GRAB_MODE_22_ASYNC,
1067 false, 2,
reinterpret_cast<uint32_t *
>(
mask));
1070 qCDebug(lcQpaXInput,
"failed to grab events for device %d on window %x"
1071 "(error code %d)",
id,
w,
error->error_code);
1081 for (
int id : std::as_const(m_xiMasterPointerIds)) {
1082 auto cookie = xcb_input_xi_ungrab_device_checked(
xcb_connection(), XCB_CURRENT_TIME,
id);
1085 qCDebug(lcQpaXInput,
"XIUngrabDevice failed - id: %d (error code %d)",
id,
error->error_code);
1102void QXcbConnection::xi2HandleHierarchyEvent(
void *
event)
1104 auto *xiEvent =
reinterpret_cast<xcb_input_hierarchy_event_t *
>(
event);
1109 if (xiEvent->flags & (XCB_INPUT_HIERARCHY_MASK_MASTER_ADDED |
1110 XCB_INPUT_HIERARCHY_MASK_MASTER_REMOVED |
1111 XCB_INPUT_HIERARCHY_MASK_SLAVE_REMOVED |
1112 XCB_INPUT_HIERARCHY_MASK_SLAVE_ADDED))
1116#if QT_XCB_HAS_TOUCHPAD_GESTURES
1117void QXcbConnection::xi2HandleGesturePinchEvent(
void *
event)
1119 auto *xiEvent =
reinterpret_cast<qt_xcb_input_pinch_event_t *
>(
event);
1121 if (
Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) {
1122 qCDebug(lcQpaXInputEvents,
"XI2 gesture event type %d seq %d fingers %d pos %6.1f, "
1123 "%6.1f root pos %6.1f, %6.1f delta_angle %6.1f scale %6.1f on window %x",
1124 xiEvent->event_type, xiEvent->sequence, xiEvent->detail,
1131 if (!platformWindow)
1136 TouchDeviceData *dev = touchDeviceForId(xiEvent->sourceid);
1139 uint32_t fingerCount = xiEvent->detail;
1141 switch (xiEvent->event_type) {
1142 case XCB_INPUT_GESTURE_PINCH_BEGIN:
1146 xcb_input_xi_allow_events(
xcb_connection(), XCB_CURRENT_TIME, xiEvent->deviceid,
1147 XCB_INPUT_EVENT_MODE_ASYNC_DEVICE, 0, xiEvent->event);
1149 m_lastPinchScale = 1.0;
1158 case XCB_INPUT_GESTURE_PINCH_UPDATE: {
1162 m_lastPinchScale =
scale;
1169 platformWindow->
window(), xiEvent->time, dev->qtTouchDevice,
1175 if (rotationDelta != 0) {
1177 platformWindow->
window(), xiEvent->time, dev->qtTouchDevice,
1184 if (scaleDelta != 0) {
1186 platformWindow->
window(), xiEvent->time, dev->qtTouchDevice,
1195 case XCB_INPUT_GESTURE_PINCH_END:
1206void QXcbConnection::xi2HandleGestureSwipeEvent(
void *
event)
1208 auto *xiEvent =
reinterpret_cast<qt_xcb_input_swipe_event_t *
>(
event);
1210 if (
Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) {
1211 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",
1212 xiEvent->event_type, xiEvent->sequence, xiEvent->detail,
1218 if (!platformWindow)
1223 TouchDeviceData *dev = touchDeviceForId(xiEvent->sourceid);
1226 uint32_t fingerCount = xiEvent->detail;
1228 switch (xiEvent->event_type) {
1229 case XCB_INPUT_GESTURE_SWIPE_BEGIN:
1233 xcb_input_xi_allow_events(
xcb_connection(), XCB_CURRENT_TIME, xiEvent->deviceid,
1234 XCB_INPUT_EVENT_MODE_ASYNC_DEVICE, 0, xiEvent->event);
1243 case XCB_INPUT_GESTURE_SWIPE_UPDATE: {
1247 if (xiEvent->delta_x != 0 || xiEvent->delta_y != 0) {
1249 platformWindow->
window(), xiEvent->time, dev->qtTouchDevice,
1257 case XCB_INPUT_GESTURE_SWIPE_END:
1269void QXcbConnection::xi2HandleGesturePinchEvent(
void*) {}
1270void QXcbConnection::xi2HandleGestureSwipeEvent(
void*) {}
1273void QXcbConnection::xi2HandleDeviceChangedEvent(
void *
event)
1275 auto *xiEvent =
reinterpret_cast<xcb_input_device_changed_event_t *
>(
event);
1276 switch (xiEvent->reason) {
1277 case XCB_INPUT_CHANGE_REASON_DEVICE_CHANGE: {
1279 if (m_xiMasterPointerIds.
contains(xiEvent->deviceid) || m_xiSlavePointerIds.
contains(xiEvent->deviceid))
1284 auto it = xcb_input_xi_query_device_infos_iterator(
reply.get());
1285 xi2SetupSlavePointerDevice(
it.data);
1288 case XCB_INPUT_CHANGE_REASON_SLAVE_SWITCH: {
1289 if (
auto *scrollingDevice = scrollingDeviceForId(xiEvent->sourceid))
1290 xi2UpdateScrollingDevice(scrollingDevice);
1294 qCDebug(lcQpaXInputEvents,
"unknown device-changed-event (device %d)", xiEvent->sourceid);
1299void QXcbConnection::xi2UpdateScrollingDevice(
QInputDevice *dev)
1308 qCDebug(lcQpaXInputDevices,
"scrolling device %lld no longer present", scrollingDevice->
systemId);
1312 if (lcQpaXInputEvents().isDebugEnabled())
1315 xcb_input_xi_device_info_t *deviceInfo = xcb_input_xi_query_device_infos_iterator(
reply.get()).data;
1316 auto classes_it = xcb_input_xi_device_info_classes_iterator(deviceInfo);
1317 for (; classes_it.rem; xcb_input_device_class_next(&classes_it)) {
1318 xcb_input_device_class_t *
classInfo = classes_it.data;
1319 if (
classInfo->type == XCB_INPUT_DEVICE_CLASS_TYPE_VALUATOR) {
1320 auto *vci =
reinterpret_cast<xcb_input_valuator_class_t *
>(
classInfo);
1321 const int valuatorAtom =
qatom(vci->label);
1329 qCDebug(lcQpaXInputEvents,
"scrolling device %lld moved from (%f, %f) to (%f, %f)", scrollingDevice->
systemId,
1330 lastScrollPosition.
x(), lastScrollPosition.
y(),
1340 xi2UpdateScrollingDevice(
const_cast<QInputDevice *
>(dev));
1349 return qobject_cast<QXcbScrollingDevice *>(
const_cast<QPointingDevice *
>(dev));
1361 if (xiDeviceEvent->event_type == XCB_INPUT_MOTION && scrollingDevice->
orientations) {
1367 if (xi2GetValuatorValueIfSet(xiDeviceEvent, scrollingDevice->
verticalIndex, &
value)) {
1375 rawDelta.
setY(delta);
1377 rawDelta.
setY(-delta);
1387 rawDelta.
setX(delta);
1389 rawDelta.
setX(-delta);
1392 if (!angleDelta.
isNull()) {
1400 qCDebug(lcQpaXInputEvents) <<
"scroll wheel from device" << scrollingDevice->
systemId
1401 <<
"@ window pos" << local <<
"delta px" << rawDelta <<
"angle" << angleDelta;
1406 }
else if (xiDeviceEvent->event_type == XCB_INPUT_BUTTON_RELEASE && scrollingDevice->
legacyOrientations) {
1410 if (xiDeviceEvent->detail == 4)
1411 angleDelta.
setY(120);
1412 else if (xiDeviceEvent->detail == 5)
1413 angleDelta.
setY(-120);
1416 if (xiDeviceEvent->detail == 6)
1417 angleDelta.
setX(120);
1418 else if (xiDeviceEvent->detail == 7)
1419 angleDelta.
setX(-120);
1421 if (!angleDelta.
isNull()) {
1427 qCDebug(lcQpaXInputEvents) <<
"scroll wheel (button" << xiDeviceEvent->detail <<
") @ window pos" << local <<
"delta angle" << angleDelta;
1438 for (
int i = 0;
i < maskLen;
i++) {
1440 if ((maskPtr[
i] & (1 <<
number)) == 0)
1443 for (
int j = 0;
j < 8;
j++) {
1446 if (maskPtr[
i] & (1 <<
j))
1454bool QXcbConnection::xi2GetValuatorValueIfSet(
const void *
event,
int valuatorNum,
double *
value)
1457 auto *buttonsMaskAddr =
reinterpret_cast<const unsigned char *
>(&xideviceevent[1]);
1458 auto *valuatorsMaskAddr = buttonsMaskAddr + xideviceevent->buttons_len * 4;
1459 auto *valuatorsValuesAddr =
reinterpret_cast<const xcb_input_fp3232_t *
>(valuatorsMaskAddr + xideviceevent->valuators_len * 4);
1461 int valuatorOffset =
xi2ValuatorOffset(valuatorsMaskAddr, xideviceevent->valuators_len, valuatorNum);
1462 if (valuatorOffset < 0)
1465 *
value = valuatorsValuesAddr[valuatorOffset].integral;
1466 *
value += ((double)valuatorsValuesAddr[valuatorOffset].frac / (1 << 16) / (1 << 16));
1484#if QT_CONFIG(tabletevent)
1485bool QXcbConnection::xi2HandleTabletEvent(
const void *
event, TabletData *tabletData)
1487 bool handled =
true;
1490 switch (xiDeviceEvent->event_type) {
1491 case XCB_INPUT_BUTTON_PRESS: {
1493 tabletData->buttons |=
b;
1494 xi2ReportTabletEvent(
event, tabletData);
1497 case XCB_INPUT_BUTTON_RELEASE: {
1499 tabletData->buttons ^=
b;
1500 xi2ReportTabletEvent(
event, tabletData);
1503 case XCB_INPUT_MOTION:
1504 xi2ReportTabletEvent(
event, tabletData);
1506 case XCB_INPUT_PROPERTY: {
1509 const auto *ev =
reinterpret_cast<const xcb_input_property_event_t *
>(
event);
1510 if (ev->what == XCB_INPUT_PROPERTY_FLAG_MODIFIED) {
1512 enum WacomSerialIndex {
1514 _WACSER_LAST_TOOL_SERIAL,
1515 _WACSER_LAST_TOOL_ID,
1516 _WACSER_TOOL_SERIAL,
1522 ev->property, XCB_GET_PROPERTY_TYPE_ANY, 0, 100);
1529 if (!tool &&
ptr[_WACSER_TOOL_SERIAL])
1530 tool =
ptr[_WACSER_TOOL_SERIAL];
1535 const QPointingDevice *dev = tabletToolInstance(
nullptr, tabletData->name,
1536 tabletData->deviceId,
ptr[_WACSER_USB_ID], tool,
1538 tabletData->inProximity =
true;
1539 tabletData->tool = dev->
type();
1540 tabletData->serialId =
qint64(
ptr[_WACSER_TOOL_SERIAL]);
1543 tool =
ptr[_WACSER_LAST_TOOL_ID];
1547 tool =
ptr[_WACSER_LAST_TOOL_SERIAL];
1550 tabletData->tool = dev->
type();
1551 tabletData->inProximity =
false;
1552 tabletData->serialId =
qint64(
ptr[_WACSER_LAST_TOOL_SERIAL]);
1557 qCDebug(lcQpaXInputDevices,
"XI2 proximity change on tablet %d %s (USB %x): last tool: %x id %x current tool: %x id %x %s",
1558 tabletData->deviceId,
qPrintable(tabletData->name),
ptr[_WACSER_USB_ID],
1559 ptr[_WACSER_LAST_TOOL_SERIAL],
ptr[_WACSER_LAST_TOOL_ID],
1560 ptr[_WACSER_TOOL_SERIAL],
ptr[_WACSER_TOOL_ID], toolName(tabletData->tool));
1577 return screenMin + normValue * screenSize;
1581void QXcbConnection::xi2ReportTabletEvent(
const void *
event, TabletData *tabletData)
1591 double pressure = 0, rotation = 0, tangentialPressure = 0;
1592 int xTilt = 0, yTilt = 0;
1601 QRect physicalScreenArea;
1605 physicalScreenArea |=
screen->geometry();
1609 ite = tabletData->valuatorInfo.end();
it != ite; ++
it) {
1610 int valuator =
it.key();
1611 TabletData::ValuatorClassInfo &
classInfo(
it.value());
1617 const qreal value = scaleOneValuator(normalizedValue, physicalScreenArea.
x(), physicalScreenArea.
width());
1624 qreal value = scaleOneValuator(normalizedValue, physicalScreenArea.
y(), physicalScreenArea.
height());
1630 pressure = normalizedValue;
1639 switch (tabletData->tool) {
1641 tangentialPressure = normalizedValue * 2.0 - 1.0;
1645 rotation = normalizedValue * 360.0 - 180.0;
1656 if (
Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
1657 qCDebug(lcQpaXInputEvents,
"XI2 event on tablet %d with tool %s %llx type %s seq %d detail %d time %d "
1658 "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",
1659 tabletData->deviceId, toolName(tabletData->tool), tabletData->serialId, pointerTypeName(tabletData->pointerType),
1660 ev->sequence, ev->detail, ev->time,
1662 (
int)tabletData->buttons, pressure, xTilt, yTilt, rotation, (
int)
modifiers);
1665 tabletData->buttons, pressure,
1666 xTilt, yTilt, tangentialPressure,
1670QXcbConnection::TabletData *QXcbConnection::tabletDataForDevice(
int id)
1672 for (
int i = 0;
i < m_tabletData.size(); ++
i) {
1673 if (m_tabletData.at(
i).deviceId ==
id)
1674 return &m_tabletData[
i];
IOBluetoothDevice * device
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
bool contains(char c) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
QByteArray toLower() const &
State
Specifies the state of this event point.
const T & value() const noexcept
Returns the current item's value.
const Key & key() const noexcept
Returns the current item's key.
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
const_iterator constBegin() const noexcept
qsizetype removeIf(Predicate pred)
qsizetype removeAll(const AT &t)
void append(parameter_type t)
const_iterator constEnd() const noexcept
\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.
constexpr void setY(qreal y) noexcept
Sets the y coordinate of this point to the given finite y coordinate.
constexpr void setX(qreal x) noexcept
Sets the x coordinate of this point to the given finite x coordinate.
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.
QPointingDevice::PointerType pointerType
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 x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
constexpr int width() const noexcept
Returns the width of the rectangle.
constexpr int y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
QSizeF physicalSize
the screen's physical size (in millimeters)
QRect geometry
the screen's geometry in pixels
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...
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
virtual void handleXIMouseEvent(xcb_ge_event_t *, Qt::MouseEventSource=Qt::MouseEventNotSynthesized)
virtual void handleXIEnterLeave(xcb_ge_event_t *)
virtual bool handleNativeEvent(xcb_generic_event_t *)
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)
struct QT_BEGIN_NAMESPACE::Holder holder
char qt_getEnumMetaObject(const T &)
DBusConnection const char DBusError * error
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
auto qHypot(qfloat16 x, qfloat16 y)
#define qCWarning(category,...)
#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)
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)
QFileInfo info(fileName)
[8]
obj metaObject() -> className()
bool contains(const AT &t) const noexcept