9aba46c7a6f76d2e28407bed256da8c259a9635b
[profile/ivi/qtbase.git] / src / plugins / platforms / xcb / qxcbconnection_maemo.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qxcbconnection.h"
43
44 #ifdef XCB_USE_XINPUT2_MAEMO
45
46 #include "qxcbwindow.h"
47 #include <QtGui/qwindowsysteminterface.h>
48 #include <X11/extensions/XInput2.h>
49 #include <X11/extensions/XI2proto.h>
50 #include <X11/Xatom.h>
51
52 QT_BEGIN_NAMESPACE
53
54 // Define it here to work around XLib defining Bool and stuff.
55 // We can't declare those variables in the header without facing include order headaches.
56 struct XInput2MaemoData {
57     XInput2MaemoData()
58     : use_xinput(false)
59     , xinput_opcode(0)
60     , xinput_eventbase(0)
61     , xinput_errorbase(0)
62     , xideviceinfo(0)
63     , xibuttonclassinfo(0)
64     , xiMaxContacts(0)
65     , qtTouchDevice(0)
66     {
67     }
68     // true if Qt is compiled w/ XInput2 or Tablet support and we have a tablet.
69     bool use_xinput;
70     int xinput_opcode;
71     int xinput_eventbase;
72     int xinput_errorbase;
73     // device info for the master pointer Qt is using
74     XIDeviceInfo *xideviceinfo;
75     XIButtonClassInfo *xibuttonclassinfo;
76     int xiMaxContacts;
77     QList<QWindowSystemInterface::TouchPoint> allTouchPoints;
78     QTouchDevice *qtTouchDevice;
79 };
80
81 bool QXcbConnection::isUsingXInput2Maemo()
82 {
83     return m_xinputData && m_xinputData->use_xinput && m_xinputData->xiMaxContacts != 0;
84 }
85
86 void QXcbConnection::initializeXInput2Maemo()
87 {
88     Q_ASSERT(!m_xinputData);
89     m_xinputData = new XInput2MaemoData;
90     m_xinputData->use_xinput = XQueryExtension((Display *)m_xlib_display, "XInputExtension", &m_xinputData->xinput_opcode,
91                                       &m_xinputData->xinput_eventbase, &m_xinputData->xinput_errorbase);
92     if (m_xinputData->use_xinput) {
93         // we want XInput2
94         int ximajor = 2, ximinor = 0;
95         if (XIQueryVersion((Display *)m_xlib_display, &ximajor, &ximinor) == BadRequest) {
96             // XInput2 not available
97             m_xinputData->use_xinput = false;
98         } else {
99             // find the first master pointer and use this throughout Qt
100             // when making XI2 calls that need a device id (rationale is that
101             // for the time being, most setups will only have one master
102             // pointer (despite having multiple slaves)
103             int deviceCount = 0;
104             XIDeviceInfo *devices = XIQueryDevice((Display *)m_xlib_display, XIAllMasterDevices, &deviceCount);
105             if (devices) {
106                 for (int i = 0; i < deviceCount; ++i) {
107                     if (devices[i].use == XIMasterPointer) {
108                         int unused = 0;
109                         m_xinputData->xideviceinfo = XIQueryDevice((Display *)m_xlib_display, devices[i].deviceid, &unused);
110                         break;
111                     }
112                 }
113                 XIFreeDeviceInfo(devices);
114             }
115             if (!m_xinputData->xideviceinfo)
116                 qFatal("Qt: Internal error, no XI2 master pointer found.");
117
118             // find the button info
119             m_xinputData->xibuttonclassinfo = 0;
120             for (int i = 0; i < m_xinputData->xideviceinfo->num_classes; ++i) {
121                 if (m_xinputData->xideviceinfo->classes[i]->type == XIButtonClass) {
122                     m_xinputData->xibuttonclassinfo = (XIButtonClassInfo *) m_xinputData->xideviceinfo->classes[i];
123                     break;
124                 }
125             }
126
127             // find the "Max Contacts" property on the device
128             Atom typeReturn;
129             int formatReturn;
130             ulong countReturn, bytesReturn;
131             uchar *data = 0;
132             if (XIGetProperty((Display *)m_xlib_display,
133                               m_xinputData->xibuttonclassinfo->sourceid,
134                               atom(QXcbAtom::MaxContacts),
135                               0, 1,
136                               False,
137                               XA_INTEGER,
138                               &typeReturn,
139                               &formatReturn,
140                               &countReturn,
141                               &bytesReturn,
142                               &data) == Success
143                 && data != 0
144                 && typeReturn == XA_INTEGER
145                 && formatReturn == 8
146                 && countReturn == 1) {
147                 // touch driver reported the max number of touch-points
148                 m_xinputData->xiMaxContacts = data[0];
149             } else {
150                 m_xinputData->xiMaxContacts = 0;
151             }
152             if (data)
153                 XFree(data);
154             XFlush((Display *)m_xlib_display);
155         }
156     }
157 }
158
159 void QXcbConnection::finalizeXInput2Maemo()
160 {
161     if (m_xinputData && m_xinputData->xideviceinfo) {
162         XIFreeDeviceInfo(m_xinputData->xideviceinfo);
163     }
164     delete m_xinputData;
165 }
166
167 void QXcbConnection::handleGenericEventMaemo(xcb_ge_event_t *event)
168 {
169     if (m_xinputData->use_xinput && xi2PrepareXIGenericDeviceEvent(event, m_xinputData->xinput_opcode)) {
170         xXIGenericDeviceEvent* xievent = (xXIGenericDeviceEvent*)event;
171
172         // On Harmattan XInput2 is hacked to give touch points updates into standard mouse button press/motion events.
173         if (m_xinputData->xiMaxContacts != 0
174             && (xievent->evtype == XI_ButtonPress
175                 || xievent->evtype == XI_ButtonRelease
176                 || xievent->evtype == XI_Motion)) {
177             xXIDeviceEvent *xideviceevent = (xXIDeviceEvent *)xievent;
178             QList<QWindowSystemInterface::TouchPoint> touchPoints = m_xinputData->allTouchPoints;
179             if (touchPoints.count() != m_xinputData->xiMaxContacts) {
180                 // initial event, allocate space for all (potential) touch points
181                 touchPoints.reserve(m_xinputData->xiMaxContacts);
182                 for (int i = 0; i < m_xinputData->xiMaxContacts; ++i) {
183                     QWindowSystemInterface::TouchPoint tp;
184                     tp.id = i;
185                     tp.state = Qt::TouchPointReleased;
186                     touchPoints << tp;
187                 }
188             }
189             qreal x, y, nx, ny, w = 0.0, h = 0.0, p = -1.0;
190             int id;
191             uint active = 0;
192             for (int i = 0; i < m_xinputData->xideviceinfo->num_classes; ++i) {
193                 XIAnyClassInfo *classinfo = m_xinputData->xideviceinfo->classes[i];
194                 if (classinfo->type == XIValuatorClass) {
195                     XIValuatorClassInfo *valuatorclassinfo = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
196                     int n = valuatorclassinfo->number;
197                     double value;
198                     if (!xi2GetValuatorValueIfSet(xideviceevent, n, &value))
199                         continue;
200
201                     if (valuatorclassinfo->label == atom(QXcbAtom::AbsMTPositionX)) {
202                         x = value;
203                         nx = (x - valuatorclassinfo->min) / (valuatorclassinfo->max - valuatorclassinfo->min);
204                     } else if (valuatorclassinfo->label == atom(QXcbAtom::AbsMTPositionY)) {
205                         y = value;
206                         ny = (y - valuatorclassinfo->min) / (valuatorclassinfo->max - valuatorclassinfo->min);
207                     } else if (valuatorclassinfo->label == atom(QXcbAtom::AbsMTTouchMajor)) {
208                         w = value;
209                     } else if (valuatorclassinfo->label == atom(QXcbAtom::AbsMTTouchMinor)) {
210                         h = value;
211                     } else if (valuatorclassinfo->label == atom(QXcbAtom::AbsMTPressure)) {
212                         p = (value - valuatorclassinfo->min) / (valuatorclassinfo->max - valuatorclassinfo->min);
213                     } else if (valuatorclassinfo->label == atom(QXcbAtom::AbsMTTrackingID)) {
214                         id = value;
215                         active |= 1 << id;
216                         QWindowSystemInterface::TouchPoint &touchPoint = touchPoints[id];
217
218                         Qt::TouchPointState newstate;
219                         if (touchPoint.state == Qt::TouchPointReleased) {
220                             newstate = Qt::TouchPointPressed;
221                         } else {
222                             if (touchPoint.area.center() != QPoint(x, y))
223                                 newstate = Qt::TouchPointMoved;
224                             else
225                                 newstate = Qt::TouchPointStationary;
226                         }
227
228                         touchPoint.state = newstate;
229                         touchPoint.area = QRectF(x - w/2, y - h/2, w, h);
230                         touchPoint.normalPosition = QPointF(nx, ny);
231                         touchPoint.pressure = p;
232                     }
233                 }
234             }
235
236             // mark previously-active-but-now-inactive touch points as released
237             for (int i = 0; i < touchPoints.count(); ++i)
238                 if (!(active & (1 << i)) && touchPoints.at(i).state != Qt::TouchPointReleased)
239                     touchPoints[i].state = Qt::TouchPointReleased;
240
241             if (QXcbWindow *platformWindow = platformWindowFromId(xideviceevent->event)) {
242                 QTouchDevice *dev = m_xinputData->qtTouchDevice;
243                 if (!dev) {
244                     dev = new QTouchDevice;
245                     dev->setType(QTouchDevice::TouchScreen);
246                     dev->setCapabilities(QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::Pressure | QTouchDevice::NormalizedPosition);
247                     QWindowSystemInterface::registerTouchDevice(dev);
248                     m_xinputData->qtTouchDevice = dev;
249                 }
250                 QWindowSystemInterface::handleTouchEvent(platformWindow->window(), xideviceevent->time, dev, touchPoints);
251             }
252
253             if (xideviceevent->evtype == XI_ButtonRelease) {
254                 // final event, forget touch state
255                 m_xinputData->allTouchPoints.clear();
256             } else {
257                 // save current state so that we have something to reuse later
258                 m_xinputData->allTouchPoints = touchPoints;
259             }
260
261         }
262     }
263 }
264
265 QT_END_NAMESPACE
266
267 #endif // XCB_USE_XINPUT2_MAEMO
268