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