1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL21$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** $QT_END_LICENSE$
31 **
32 ****************************************************************************/
33
34 #include "debug.h"
35 #include "qxcbconnection_xi2.h"
36
37 #include <QX11Info>
38 #include <QWidget>
39 #include <QPointer>
40 #include <QElapsedTimer>
41 #include <QGuiApplication>
42 #include <QApplication>
43
44
45 #include <X11/extensions/XI2proto.h>
46 #include <xcb/xproto.h>
47
48 namespace kis_tablet {
49
50 Q_LOGGING_CATEGORY(lcQpaXInput, "qt.qpa.input")
51 Q_LOGGING_CATEGORY(lcQpaXInputDevices, "qt.qpa.input.devices")
52 Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen")
53
QXcbConnection(bool canGrabServer,const char * displayName)54 QXcbConnection::QXcbConnection(bool canGrabServer, const char *displayName)
55 : m_connection(0)
56 , m_canGrabServer(canGrabServer)
57 , m_displayName(displayName ? QByteArray(displayName) : qgetenv("DISPLAY"))
58 #ifdef XCB_USE_XLIB
59 , m_xlib_display(0)
60 #endif
61 {
62 m_connection = QX11Info::connection();
63 m_xlib_display = QX11Info::display();
64
65 if (!m_connection || xcb_connection_has_error(m_connection)) {
66 qFatal("QXcbConnection: Could not connect to display %s", m_displayName.constData());
67 }
68
69 initializeAllAtoms();
70
71 #if defined(XCB_USE_XINPUT2)
72 initializeXInput2();
73 #endif
74 }
75
~QXcbConnection()76 QXcbConnection::~QXcbConnection()
77 {
78 #if defined(XCB_USE_XINPUT2)
79 finalizeXInput2();
80 #endif
81 }
82
qatom(xcb_atom_t xatom) const83 QXcbAtom::Atom QXcbConnection::qatom(xcb_atom_t xatom) const
84 {
85 return static_cast<QXcbAtom::Atom>(std::find(m_allAtoms, m_allAtoms + QXcbAtom::NAtoms, xatom) - m_allAtoms);
86 }
87
xlib_display() const88 void *QXcbConnection::xlib_display() const
89 {
90 return m_xlib_display;
91 }
92
atomName(xcb_atom_t atom)93 QByteArray QXcbConnection::atomName(xcb_atom_t atom)
94 {
95 if (!atom)
96 return QByteArray();
97
98 xcb_generic_error_t *error = 0;
99 xcb_get_atom_name_cookie_t cookie = Q_XCB_CALL(xcb_get_atom_name(xcb_connection(), atom));
100 xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(xcb_connection(), cookie, &error);
101 if (error) {
102 qWarning() << "QXcbConnection::atomName: bad Atom" << atom;
103 free(error);
104 }
105 if (reply) {
106 QByteArray result(xcb_get_atom_name_name(reply), xcb_get_atom_name_name_length(reply));
107 free(reply);
108 return result;
109 }
110 return QByteArray();
111 }
112
113 static const char * xcb_atomnames = {
114 // window-manager <-> client protocols
115 "WM_PROTOCOLS\0"
116 "WM_DELETE_WINDOW\0"
117 "WM_TAKE_FOCUS\0"
118 "_NET_WM_PING\0"
119 "_NET_WM_CONTEXT_HELP\0"
120 "_NET_WM_SYNC_REQUEST\0"
121 "_NET_WM_SYNC_REQUEST_COUNTER\0"
122 "MANAGER\0"
123 "_NET_SYSTEM_TRAY_OPCODE\0"
124
125 // ICCCM window state
126 "WM_STATE\0"
127 "WM_CHANGE_STATE\0"
128 "WM_CLASS\0"
129 "WM_NAME\0"
130
131 // Session management
132 "WM_CLIENT_LEADER\0"
133 "WM_WINDOW_ROLE\0"
134 "SM_CLIENT_ID\0"
135
136 // Clipboard
137 "CLIPBOARD\0"
138 "INCR\0"
139 "TARGETS\0"
140 "MULTIPLE\0"
141 "TIMESTAMP\0"
142 "SAVE_TARGETS\0"
143 "CLIP_TEMPORARY\0"
144 "_QT_SELECTION\0"
145 "_QT_CLIPBOARD_SENTINEL\0"
146 "_QT_SELECTION_SENTINEL\0"
147 "CLIPBOARD_MANAGER\0"
148
149 "RESOURCE_MANAGER\0"
150
151 "_XSETROOT_ID\0"
152
153 "_QT_SCROLL_DONE\0"
154 "_QT_INPUT_ENCODING\0"
155
156 "_QT_CLOSE_CONNECTION\0"
157
158 "_MOTIF_WM_HINTS\0"
159
160 "DTWM_IS_RUNNING\0"
161 "ENLIGHTENMENT_DESKTOP\0"
162 "_DT_SAVE_MODE\0"
163 "_SGI_DESKS_MANAGER\0"
164
165 // EWMH (aka NETWM)
166 "_NET_SUPPORTED\0"
167 "_NET_VIRTUAL_ROOTS\0"
168 "_NET_WORKAREA\0"
169
170 "_NET_MOVERESIZE_WINDOW\0"
171 "_NET_WM_MOVERESIZE\0"
172
173 "_NET_WM_NAME\0"
174 "_NET_WM_ICON_NAME\0"
175 "_NET_WM_ICON\0"
176
177 "_NET_WM_PID\0"
178
179 "_NET_WM_WINDOW_OPACITY\0"
180
181 "_NET_WM_STATE\0"
182 "_NET_WM_STATE_ABOVE\0"
183 "_NET_WM_STATE_BELOW\0"
184 "_NET_WM_STATE_FULLSCREEN\0"
185 "_NET_WM_STATE_MAXIMIZED_HORZ\0"
186 "_NET_WM_STATE_MAXIMIZED_VERT\0"
187 "_NET_WM_STATE_MODAL\0"
188 "_NET_WM_STATE_STAYS_ON_TOP\0"
189 "_NET_WM_STATE_DEMANDS_ATTENTION\0"
190
191 "_NET_WM_USER_TIME\0"
192 "_NET_WM_USER_TIME_WINDOW\0"
193 "_NET_WM_FULL_PLACEMENT\0"
194
195 "_NET_WM_WINDOW_TYPE\0"
196 "_NET_WM_WINDOW_TYPE_DESKTOP\0"
197 "_NET_WM_WINDOW_TYPE_DOCK\0"
198 "_NET_WM_WINDOW_TYPE_TOOLBAR\0"
199 "_NET_WM_WINDOW_TYPE_MENU\0"
200 "_NET_WM_WINDOW_TYPE_UTILITY\0"
201 "_NET_WM_WINDOW_TYPE_SPLASH\0"
202 "_NET_WM_WINDOW_TYPE_DIALOG\0"
203 "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0"
204 "_NET_WM_WINDOW_TYPE_POPUP_MENU\0"
205 "_NET_WM_WINDOW_TYPE_TOOLTIP\0"
206 "_NET_WM_WINDOW_TYPE_NOTIFICATION\0"
207 "_NET_WM_WINDOW_TYPE_COMBO\0"
208 "_NET_WM_WINDOW_TYPE_DND\0"
209 "_NET_WM_WINDOW_TYPE_NORMAL\0"
210 "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE\0"
211
212 "_KDE_NET_WM_FRAME_STRUT\0"
213 "_NET_FRAME_EXTENTS\0"
214
215 "_NET_STARTUP_INFO\0"
216 "_NET_STARTUP_INFO_BEGIN\0"
217
218 "_NET_SUPPORTING_WM_CHECK\0"
219
220 "_NET_WM_CM_S0\0"
221
222 "_NET_SYSTEM_TRAY_VISUAL\0"
223
224 "_NET_ACTIVE_WINDOW\0"
225
226 // Property formats
227 "TEXT\0"
228 "UTF8_STRING\0"
229 "CARDINAL\0"
230
231 // xdnd
232 "XdndEnter\0"
233 "XdndPosition\0"
234 "XdndStatus\0"
235 "XdndLeave\0"
236 "XdndDrop\0"
237 "XdndFinished\0"
238 "XdndTypeList\0"
239 "XdndActionList\0"
240
241 "XdndSelection\0"
242
243 "XdndAware\0"
244 "XdndProxy\0"
245
246 "XdndActionCopy\0"
247 "XdndActionLink\0"
248 "XdndActionMove\0"
249 "XdndActionPrivate\0"
250
251 // Motif DND
252 "_MOTIF_DRAG_AND_DROP_MESSAGE\0"
253 "_MOTIF_DRAG_INITIATOR_INFO\0"
254 "_MOTIF_DRAG_RECEIVER_INFO\0"
255 "_MOTIF_DRAG_WINDOW\0"
256 "_MOTIF_DRAG_TARGETS\0"
257
258 "XmTRANSFER_SUCCESS\0"
259 "XmTRANSFER_FAILURE\0"
260
261 // Xkb
262 "_XKB_RULES_NAMES\0"
263
264 // XEMBED
265 "_XEMBED\0"
266 "_XEMBED_INFO\0"
267
268 // XInput2
269 "Button Left\0"
270 "Button Middle\0"
271 "Button Right\0"
272 "Button Wheel Up\0"
273 "Button Wheel Down\0"
274 "Button Horiz Wheel Left\0"
275 "Button Horiz Wheel Right\0"
276 "Abs MT Position X\0"
277 "Abs MT Position Y\0"
278 "Abs MT Touch Major\0"
279 "Abs MT Touch Minor\0"
280 "Abs MT Pressure\0"
281 "Abs MT Tracking ID\0"
282 "Max Contacts\0"
283 "Rel X\0"
284 "Rel Y\0"
285 // XInput2 tablet
286 "Abs X\0"
287 "Abs Y\0"
288 "Abs Pressure\0"
289 "Abs Tilt X\0"
290 "Abs Tilt Y\0"
291 "Abs Wheel\0"
292 "Abs Distance\0"
293 "Wacom Serial IDs\0"
294 "INTEGER\0"
295 "Rel Horiz Wheel\0"
296 "Rel Vert Wheel\0"
297 "Rel Horiz Scroll\0"
298 "Rel Vert Scroll\0"
299 "_XSETTINGS_SETTINGS\0"
300 "_COMPIZ_DECOR_PENDING\0"
301 "_COMPIZ_DECOR_REQUEST\0"
302 "_COMPIZ_DECOR_DELETE_PIXMAP\0" // \0\0 terminates loop.
303 };
304
initializeAllAtoms()305 void QXcbConnection::initializeAllAtoms() {
306 const char *names[QXcbAtom::NAtoms];
307 const char *ptr = xcb_atomnames;
308
309 int i = 0;
310 while (*ptr) {
311 names[i++] = ptr;
312 while (*ptr)
313 ++ptr;
314 ++ptr;
315 }
316
317 Q_ASSERT(i == QXcbAtom::NPredefinedAtoms);
318
319 QByteArray settings_atom_name("_QT_SETTINGS_TIMESTAMP_");
320 settings_atom_name += m_displayName;
321 names[i++] = settings_atom_name;
322
323 xcb_intern_atom_cookie_t cookies[QXcbAtom::NAtoms];
324
325 Q_ASSERT(i == QXcbAtom::NAtoms);
326 for (i = 0; i < QXcbAtom::NAtoms; ++i)
327 cookies[i] = xcb_intern_atom(xcb_connection(), false, strlen(names[i]), names[i]);
328
329 for (i = 0; i < QXcbAtom::NAtoms; ++i) {
330 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xcb_connection(), cookies[i], 0);
331 m_allAtoms[i] = reply->atom;
332 free(reply);
333 }
334 }
335
xi2MouseEvents() const336 bool QXcbConnection::xi2MouseEvents() const
337 {
338 static bool mouseViaXI2 = !qEnvironmentVariableIsSet("QT_XCB_NO_XI2_MOUSE");
339 return mouseViaXI2;
340 }
341
notifyEnterEvent(xcb_enter_notify_event_t * event)342 void QXcbConnection::notifyEnterEvent(xcb_enter_notify_event_t *event)
343 {
344 xcb_window_t window;
345
346 // first cleaning up deleted windows: assuming 0 is not a valid window id
347
348 while ((window = m_windowMapper.key(0,0)) != 0) {
349 m_windowMapper.remove(window);
350 }
351
352 addWindowFromXi2Id(event->event);
353 }
354
addWindowFromXi2Id(xcb_window_t id)355 void QXcbConnection::addWindowFromXi2Id(xcb_window_t id)
356 {
357 if (!m_windowMapper.contains(id)) {
358 QWidget *widget = QWidget::find(id);
359 if (widget) {
360 QWindow *windowHandle = widget->windowHandle();
361 m_windowMapper.insert(id, windowHandle);
362 }
363 }
364 }
365
windowFromId(xcb_window_t id)366 QWindow* QXcbConnection::windowFromId(xcb_window_t id)
367 {
368 QWindow *window = m_windowMapper.value(id, 0);
369
370 // Try to fetch the window Id lazily. It is needed when the cursor gets under
371 // a popup window or a popup dialog, which doesn't produce any enter event on
372 // some systems
373 if (!window) {
374 addWindowFromXi2Id(id);
375 window = m_windowMapper.value(id, 0);
376 }
377
378 return window;
379 }
380
xi2ValuatorOffset(unsigned char * maskPtr,int maskLen,int number)381 static int xi2ValuatorOffset(unsigned char *maskPtr, int maskLen, int number)
382 {
383 int offset = 0;
384 for (int i = 0; i < maskLen; i++) {
385 if (number < 8) {
386 if ((maskPtr[i] & (1 << number)) == 0)
387 return -1;
388 }
389 for (int j = 0; j < 8; j++) {
390 if (j == number)
391 return offset;
392 if (maskPtr[i] & (1 << j))
393 offset++;
394 }
395 number -= 8;
396 }
397 return -1;
398 }
399
xi2GetValuatorValueIfSet(void * event,int valuatorNum,double * value)400 bool QXcbConnection::xi2GetValuatorValueIfSet(void *event, int valuatorNum, double *value)
401 {
402 xXIDeviceEvent *xideviceevent = static_cast<xXIDeviceEvent *>(event);
403 unsigned char *buttonsMaskAddr = (unsigned char*)&xideviceevent[1];
404 unsigned char *valuatorsMaskAddr = buttonsMaskAddr + xideviceevent->buttons_len * 4;
405 FP3232 *valuatorsValuesAddr = (FP3232*)(valuatorsMaskAddr + xideviceevent->valuators_len * 4);
406
407 int valuatorOffset = xi2ValuatorOffset(valuatorsMaskAddr, xideviceevent->valuators_len, valuatorNum);
408 if (valuatorOffset < 0)
409 return false;
410
411 *value = valuatorsValuesAddr[valuatorOffset].integral;
412 *value += ((double)valuatorsValuesAddr[valuatorOffset].frac / (1 << 16) / (1 << 16));
413 return true;
414 }
415
416 // Starting from the xcb version 1.9.3 struct xcb_ge_event_t has changed:
417 // - "pad0" became "extension"
418 // - "pad1" and "pad" became "pad0"
419 // New and old version of this struct share the following fields:
420 // NOTE: API might change again in the next release of xcb in which case this comment will
421 // need to be updated to reflect the reality.
422 typedef struct qt_xcb_ge_event_t {
423 uint8_t response_type;
424 uint8_t extension;
425 uint16_t sequence;
426 uint32_t length;
427 uint16_t event_type;
428 } qt_xcb_ge_event_t;
429
xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t * ev,int opCode)430 bool QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *ev, int opCode)
431 {
432 qt_xcb_ge_event_t *event = (qt_xcb_ge_event_t *)ev;
433 // xGenericEvent has "extension" on the second byte, the same is true for xcb_ge_event_t starting from
434 // the xcb version 1.9.3, prior to that it was called "pad0".
435 if (event->extension == opCode) {
436 // xcb event structs contain stuff that wasn't on the wire, the full_sequence field
437 // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes.
438 // Move this data back to have the same layout in memory as it was on the wire
439 // and allow casting, overwriting the full_sequence field.
440 memmove((char*) event + 32, (char*) event + 36, event->length * 4);
441 return true;
442 }
443 return false;
444 }
445
446 class Q_GUI_EXPORT QWindowSystemInterfacePrivate {
447 public:
448 enum EventType {
449 UserInputEvent = 0x100,
450 Close = UserInputEvent | 0x01,
451 GeometryChange = 0x02,
452 Enter = UserInputEvent | 0x03,
453 Leave = UserInputEvent | 0x04,
454 ActivatedWindow = 0x05,
455 WindowStateChanged = 0x06,
456 Mouse = UserInputEvent | 0x07,
457 FrameStrutMouse = UserInputEvent | 0x08,
458 Wheel = UserInputEvent | 0x09,
459 Key = UserInputEvent | 0x0a,
460 Touch = UserInputEvent | 0x0b,
461 ScreenOrientation = 0x0c,
462 ScreenGeometry = 0x0d,
463 ScreenAvailableGeometry = 0x0e,
464 ScreenLogicalDotsPerInch = 0x0f,
465 ScreenRefreshRate = 0x10,
466 ThemeChange = 0x11,
467 Expose_KRITA_XXX = 0x12,
468 FileOpen = UserInputEvent | 0x13,
469 Tablet = UserInputEvent | 0x14,
470 TabletEnterProximity = UserInputEvent | 0x15,
471 TabletLeaveProximity = UserInputEvent | 0x16,
472 PlatformPanel = UserInputEvent | 0x17,
473 ContextMenu = UserInputEvent | 0x18,
474 EnterWhatsThisMode = UserInputEvent | 0x19,
475 #ifndef QT_NO_GESTURES
476 Gesture = UserInputEvent | 0x1a,
477 #endif
478 ApplicationStateChanged = 0x19,
479 FlushEvents = 0x20,
480 WindowScreenChanged = 0x21
481 };
482
483 class WindowSystemEvent {
484 public:
485 enum {
486 Synthetic = 0x1,
487 NullWindow = 0x2
488 };
489
WindowSystemEvent(EventType t)490 explicit WindowSystemEvent(EventType t)
491 : type(t), flags(0) { }
~WindowSystemEvent()492 virtual ~WindowSystemEvent() { }
493
synthetic() const494 bool synthetic() const { return flags & Synthetic; }
nullWindow() const495 bool nullWindow() const { return flags & NullWindow; }
496
497 EventType type;
498 int flags;
499 };
500
501 class UserEvent : public WindowSystemEvent {
502 public:
UserEvent(QWindow * w,ulong time,EventType t)503 UserEvent(QWindow * w, ulong time, EventType t)
504 : WindowSystemEvent(t), window(w), timestamp(time)
505 {
506 if (!w)
507 flags |= NullWindow;
508 }
509 QPointer<QWindow> window;
510 unsigned long timestamp;
511 };
512
513 class InputEvent: public UserEvent {
514 public:
InputEvent(QWindow * w,ulong time,EventType t,Qt::KeyboardModifiers mods)515 InputEvent(QWindow * w, ulong time, EventType t, Qt::KeyboardModifiers mods)
516 : UserEvent(w, time, t), modifiers(mods) {}
517 Qt::KeyboardModifiers modifiers;
518 };
519
520 class TabletEvent : public InputEvent {
521 public:
522 static void handleTabletEvent(QWindow *w, const QPointF &local, const QPointF &global,
523 int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt,
524 qreal tangentialPressure, qreal rotation, int z, qint64 uid,
525 Qt::KeyboardModifiers modifiers = Qt::NoModifier);
526
TabletEvent(QWindow * w,ulong time,const QPointF & local,const QPointF & global,int device,int pointerType,Qt::MouseButtons b,qreal pressure,int xTilt,int yTilt,qreal tpressure,qreal rotation,int z,qint64 uid,Qt::KeyboardModifiers mods)527 TabletEvent(QWindow *w, ulong time, const QPointF &local, const QPointF &global,
528 int device, int pointerType, Qt::MouseButtons b, qreal pressure, int xTilt, int yTilt, qreal tpressure,
529 qreal rotation, int z, qint64 uid, Qt::KeyboardModifiers mods)
530 : InputEvent(w, time, Tablet, mods),
531 buttons(b), local(local), global(global), device(device), pointerType(pointerType),
532 pressure(pressure), xTilt(xTilt), yTilt(yTilt), tangentialPressure(tpressure),
533 rotation(rotation), z(z), uid(uid) { }
534 Qt::MouseButtons buttons;
535 QPointF local;
536 QPointF global;
537 int device;
538 int pointerType;
539 qreal pressure;
540 int xTilt;
541 int yTilt;
542 qreal tangentialPressure;
543 qreal rotation;
544 int z;
545 qint64 uid;
546 };
547
548 class WheelEvent : public InputEvent {
549 public:
550 #if QT_VERSION >= 0x050700
WheelEvent(QWindow * w,ulong time,const QPointF & local,const QPointF & global,QPoint pixelD,QPoint angleD,int qt4D,Qt::Orientation qt4O,Qt::KeyboardModifiers mods,Qt::ScrollPhase phase=Qt::NoScrollPhase,Qt::MouseEventSource src=Qt::MouseEventNotSynthesized)551 WheelEvent(QWindow *w, ulong time, const QPointF & local, const QPointF & global, QPoint pixelD, QPoint angleD, int qt4D, Qt::Orientation qt4O,
552 Qt::KeyboardModifiers mods, Qt::ScrollPhase phase = Qt::NoScrollPhase, Qt::MouseEventSource src = Qt::MouseEventNotSynthesized)
553 #else
554 WheelEvent(QWindow *w, ulong time, const QPointF & local, const QPointF & global, QPoint pixelD, QPoint angleD, int qt4D, Qt::Orientation qt4O,
555 Qt::KeyboardModifiers mods, Qt::ScrollPhase phase = Qt::ScrollUpdate, Qt::MouseEventSource src = Qt::MouseEventNotSynthesized)
556 #endif
557 : InputEvent(w, time, Wheel, mods), pixelDelta(pixelD), angleDelta(angleD), qt4Delta(qt4D), qt4Orientation(qt4O), localPos(local), globalPos(global), phase(phase), source(src) { }
558 QPoint pixelDelta;
559 QPoint angleDelta;
560 int qt4Delta;
561 Qt::Orientation qt4Orientation;
562 QPointF localPos;
563 QPointF globalPos;
564 Qt::ScrollPhase phase;
565 Qt::MouseEventSource source;
566 };
567 };
568
569 void processTabletEvent(QWindowSystemInterfacePrivate::TabletEvent *e);
570
571 static QElapsedTimer g_eventTimer;
572 struct EventTimerStaticInitializer
573 {
EventTimerStaticInitializerkis_tablet::EventTimerStaticInitializer574 EventTimerStaticInitializer() {
575 g_eventTimer.start();
576 }
577 };
578 EventTimerStaticInitializer __timerStaticInitializer;
579
580 Qt::MouseButtons tabletState = Qt::NoButton;
581 QPointer<QWidget> tabletPressWidget = 0;
582
handleTabletEvent(QWindow * w,const QPointF & local,const QPointF & global,int device,int pointerType,Qt::MouseButtons buttons,qreal pressure,int xTilt,int yTilt,qreal tangentialPressure,qreal rotation,int z,qint64 uid,Qt::KeyboardModifiers modifiers)583 void QWindowSystemInterface::handleTabletEvent(QWindow *w, const QPointF &local, const QPointF &global,
584 int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt,
585 qreal tangentialPressure, qreal rotation, int z, qint64 uid,
586 Qt::KeyboardModifiers modifiers)
587 {
588 qint64 timestamp = g_eventTimer.msecsSinceReference() + g_eventTimer.elapsed();
589
590 QWindowSystemInterfacePrivate::TabletEvent *e =
591 new QWindowSystemInterfacePrivate::TabletEvent(w, timestamp, local, global, device, pointerType, buttons, pressure,
592 xTilt, yTilt, tangentialPressure, rotation, z, uid, modifiers);
593
594 processTabletEvent(e);
595 }
596
processTabletEvent(QWindowSystemInterfacePrivate::TabletEvent * e)597 void processTabletEvent(QWindowSystemInterfacePrivate::TabletEvent *e)
598 {
599 #ifndef QT_NO_TABLETEVENT
600 QEvent::Type type = QEvent::TabletMove;
601 if (e->buttons != tabletState)
602 type = (e->buttons > tabletState) ? QEvent::TabletPress : QEvent::TabletRelease;
603
604 bool localValid = true;
605
606 // It can happen that we got no tablet release event. Just catch it here
607 // and clean up the state.
608 if (type == QEvent::TabletMove && e->buttons == Qt::NoButton) {
609 tabletPressWidget = 0;
610 }
611
612 QWidget *targetWidget = 0;
613
614 if (tabletPressWidget) {
615 targetWidget = tabletPressWidget;
616 localValid = false;
617 } else if (e->window) {
618 /**
619 * Here we use a weird way of converting QWindow into a
620 * QWidget. The problem is that the Qt itself does it by just
621 * converting QWindow into QWidgetWindow. But the latter one
622 * is private, so we cannot use it.
623 *
624 * We also cannot use QApplication::widegtAt(). We *MUST NOT*!
625 * There is some but in XCB: if we call
626 * QApplication::topLevelAt() during the event processing, the
627 * Enter/Leave events stop arriving. Or, more precisely, they
628 * start to errive at random points in time. Which makes
629 * KisShortcutMatcher go crazy of course.
630 *
631 * So instead of just fetching the toplevel window we decrypt
632 * the pointer using WinId mapping.
633 */
634
635 targetWidget = QWidget::find(e->window->winId());
636
637 if (targetWidget) {
638 QWidget *childWidget = targetWidget->childAt(e->local.toPoint());
639 if (childWidget) {
640 targetWidget = childWidget;
641 localValid = false;
642 }
643 }
644 }
645
646 if (!targetWidget) {
647 targetWidget = QApplication::widgetAt(e->global.toPoint());
648 localValid = false;
649
650 if (!targetWidget) return;
651 }
652
653 if (type == QEvent::TabletPress) {
654 tabletPressWidget = targetWidget;
655 } else if (type == QEvent::TabletRelease) {
656 tabletPressWidget = 0;
657 }
658
659 QPointF local = e->local;
660 if (!localValid) {
661 QPointF delta = e->global - e->global.toPoint();
662 local = targetWidget->mapFromGlobal(e->global.toPoint()) + delta;
663 }
664 Qt::MouseButtons stateChange = e->buttons ^ tabletState;
665 Qt::MouseButton button = Qt::NoButton;
666 for (int check = Qt::LeftButton; check <= int(Qt::MaxMouseButton); check = check << 1) {
667 if (check & stateChange) {
668 button = Qt::MouseButton(check);
669 break;
670 }
671 }
672 QTabletEvent ev(type, local, e->global,
673 e->device, e->pointerType, e->pressure, e->xTilt, e->yTilt,
674 e->tangentialPressure, e->rotation, e->z,
675 e->modifiers, e->uid, button, e->buttons);
676 ev.setTimestamp(e->timestamp);
677
678 QGuiApplication::sendEvent(targetWidget, &ev);
679
680 tabletState = e->buttons;
681 #else
682 Q_UNUSED(e)
683 #endif
684 }
685
handleTabletEnterProximityEvent(int device,int pointerType,qint64 uid)686 void QWindowSystemInterface::handleTabletEnterProximityEvent(int device, int pointerType, qint64 uid)
687 {
688 qint64 timestamp = g_eventTimer.msecsSinceReference() + g_eventTimer.elapsed();
689
690 QTabletEvent ev(QEvent::TabletEnterProximity, QPointF(), QPointF(),
691 device, pointerType, 0, 0, 0,
692 0, 0, 0,
693 Qt::NoModifier, uid, Qt::NoButton, tabletState);
694 ev.setTimestamp(timestamp);
695 QGuiApplication::sendEvent(qGuiApp, &ev);
696 }
697
handleTabletLeaveProximityEvent(int device,int pointerType,qint64 uid)698 void QWindowSystemInterface::handleTabletLeaveProximityEvent(int device, int pointerType, qint64 uid)
699 {
700 qint64 timestamp = g_eventTimer.msecsSinceReference() + g_eventTimer.elapsed();
701
702 QTabletEvent ev(QEvent::TabletLeaveProximity, QPointF(), QPointF(),
703 device, pointerType, 0, 0, 0,
704 0, 0, 0,
705 Qt::NoModifier, uid, Qt::NoButton, tabletState);
706 ev.setTimestamp(timestamp);
707 QGuiApplication::sendEvent(qGuiApp, &ev);
708 }
709
710 void processWheelEvent(QWindowSystemInterfacePrivate::WheelEvent *e);
711
handleWheelEvent(QWindow * tlw,ulong timestamp,const QPointF & local,const QPointF & global,QPoint pixelDelta,QPoint angleDelta,Qt::KeyboardModifiers mods,Qt::ScrollPhase phase,Qt::MouseEventSource source)712 void QWindowSystemInterface::handleWheelEvent(QWindow *tlw, ulong timestamp, const QPointF & local, const QPointF & global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase, Qt::MouseEventSource source)
713 {
714 // Qt 4 sends two separate wheel events for horizontal and vertical
715 // deltas. For Qt 5 we want to send the deltas in one event, but at the
716 // same time preserve source and behavior compatibility with Qt 4.
717 //
718 // In addition high-resolution pixel-based deltas are also supported.
719 // Platforms that does not support these may pass a null point here.
720 // Angle deltas must always be sent in addition to pixel deltas.
721 QScopedPointer<QWindowSystemInterfacePrivate::WheelEvent> e;
722
723 // Pass Qt::ScrollBegin and Qt::ScrollEnd through
724 // even if the wheel delta is null.
725 if (angleDelta.isNull() && phase == Qt::ScrollUpdate)
726 return;
727
728 // Simple case: vertical deltas only:
729 if (angleDelta.y() != 0 && angleDelta.x() == 0) {
730 e.reset(new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, local, global, pixelDelta, angleDelta, angleDelta.y(), Qt::Vertical, mods, phase, source));
731 processWheelEvent(e.data());
732 return;
733 }
734
735 // Simple case: horizontal deltas only:
736 if (angleDelta.y() == 0 && angleDelta.x() != 0) {
737 e.reset(new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, local, global, pixelDelta, angleDelta, angleDelta.x(), Qt::Horizontal, mods, phase, source));
738 processWheelEvent(e.data());
739 return;
740 }
741
742 // Both horizontal and vertical deltas: Send two wheel events.
743 // The first event contains the Qt 5 pixel and angle delta as points,
744 // and in addition the Qt 4 compatibility vertical angle delta.
745 e.reset(new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, local, global, pixelDelta, angleDelta, angleDelta.y(), Qt::Vertical, mods, phase, source));
746 processWheelEvent(e.data());
747
748 // The second event contains null pixel and angle points and the
749 // Qt 4 compatibility horizontal angle delta.
750 e.reset(new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, local, global, QPoint(), QPoint(), angleDelta.x(), Qt::Horizontal, mods, phase, source));
751 processWheelEvent(e.data());
752 }
753
processWheelEvent(QWindowSystemInterfacePrivate::WheelEvent * e)754 void processWheelEvent(QWindowSystemInterfacePrivate::WheelEvent *e)
755 {
756 #ifndef QT_NO_WHEELEVENT
757 QWindow *window = e->window.data();
758 QPointF globalPoint = e->globalPos;
759 QPointF localPoint = e->localPos;
760
761 if (e->nullWindow()) {
762 window = QGuiApplication::topLevelAt(globalPoint.toPoint());
763 if (window) {
764 QPointF delta = globalPoint - globalPoint.toPoint();
765 localPoint = window->mapFromGlobal(globalPoint.toPoint()) + delta;
766 }
767 }
768
769 if (!window)
770 return;
771
772 // Cut off in Krita...
773 //
774 // QGuiApplicationPrivate::lastCursorPosition = globalPoint;
775 // modifier_buttons = e->modifiers;
776
777 //if (window->d_func()->blockedByModalWindow) {
778 if (QGuiApplication::modalWindow() &&
779 QGuiApplication::modalWindow() != window) {
780
781 // a modal window is blocking this window, don't allow wheel events through
782 return;
783 }
784
785 #if QT_VERSION >= 0x050500
786 QWheelEvent ev(localPoint, globalPoint, e->pixelDelta, e->angleDelta, e->qt4Delta, e->qt4Orientation, QGuiApplication::mouseButtons(), e->modifiers, e->phase, e->source);
787 #else
788 QWheelEvent ev(localPoint, globalPoint, e->pixelDelta, e->angleDelta, e->qt4Delta, e->qt4Orientation, QGuiApplication::mouseButtons(), e->modifiers, e->phase);
789 #endif
790 ev.setTimestamp(e->timestamp);
791 QGuiApplication::sendEvent(window, &ev);
792 #endif /* ifndef QT_NO_WHEELEVENT */
793 }
794
795 }
796
797