qevdevtouchhandler.cpp

Absolute File Name:/home/qt/qt5_coco/qt5/qtbase/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp
Source codeSwitch to Preprocessed file
LineSourceCount
1/****************************************************************************-
2**-
3** Copyright (C) 2016 The Qt Company Ltd.-
4** Contact: https://www.qt.io/licensing/-
5**-
6** This file is part of the plugins module of the Qt Toolkit.-
7**-
8** $QT_BEGIN_LICENSE:LGPL$-
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 https://www.qt.io/terms-conditions. For further-
15** information use the contact form at https://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 3 as published by the Free Software-
20** Foundation and appearing in the file LICENSE.LGPL3 included in the-
21** packaging of this file. Please review the following information to-
22** ensure the GNU Lesser General Public License version 3 requirements-
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.-
24**-
25** GNU General Public License Usage-
26** Alternatively, this file may be used under the terms of the GNU-
27** General Public License version 2.0 or (at your option) the GNU General-
28** Public license version 3 or any later version approved by the KDE Free-
29** Qt Foundation. The licenses are as published by the Free Software-
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3-
31** included in the packaging of this file. Please review the following-
32** information to ensure the GNU General Public License requirements will-
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and-
34** https://www.gnu.org/licenses/gpl-3.0.html.-
35**-
36** $QT_END_LICENSE$-
37**-
38****************************************************************************/-
39-
40#include "qevdevtouchhandler_p.h"-
41#include <QStringList>-
42#include <QHash>-
43#include <QSocketNotifier>-
44#include <QGuiApplication>-
45#include <QTouchDevice>-
46#include <QLoggingCategory>-
47#include <QtCore/private/qcore_unix_p.h>-
48#include <QtGui/private/qhighdpiscaling_p.h>-
49#include <QtGui/private/qguiapplication_p.h>-
50#include <linux/input.h>-
51-
52#if !defined(QT_NO_MTDEV)-
53extern "C" {-
54#include <mtdev.h>-
55}-
56#endif-
57-
58QT_BEGIN_NAMESPACE-
59-
60Q_LOGGING_CATEGORY(qLcEvdevTouch, "qt.qpa.input")
never executed: return category;
0
61-
62/* android (and perhaps some other linux-derived stuff) don't define everything-
63 * in linux/input.h, so we'll need to do that ourselves.-
64 */-
65#ifndef ABS_MT_TOUCH_MAJOR-
66#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */-
67#endif-
68#ifndef ABS_MT_POSITION_X-
69#define ABS_MT_POSITION_X 0x35 /* Center X ellipse position */-
70#endif-
71#ifndef ABS_MT_POSITION_Y-
72#define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */-
73#endif-
74#ifndef ABS_MT_SLOT-
75#define ABS_MT_SLOT 0x2f-
76#endif-
77#ifndef ABS_CNT-
78#define ABS_CNT (ABS_MAX+1)-
79#endif-
80#ifndef ABS_MT_TRACKING_ID-
81#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */-
82#endif-
83#ifndef SYN_MT_REPORT-
84#define SYN_MT_REPORT 2-
85#endif-
86-
87class QEvdevTouchScreenData-
88{-
89public:-
90 QEvdevTouchScreenData(QEvdevTouchScreenHandler *q_ptr, const QStringList &args);-
91-
92 void processInputEvent(input_event *data);-
93 void assignIds();-
94-
95 QEvdevTouchScreenHandler *q;-
96 int m_lastEventType;-
97 QList<QWindowSystemInterface::TouchPoint> m_touchPoints;-
98-
99 struct Contact {-
100 int trackingId;-
101 int x;-
102 int y;-
103 int maj;-
104 int pressure;-
105 Qt::TouchPointState state;-
106 QTouchEvent::TouchPoint::InfoFlags flags;-
107 Contact() : trackingId(-1),-
108 x(0), y(0), maj(-1), pressure(0),-
109 state(Qt::TouchPointPressed), flags(0) { }
never executed: end of block
0
110 };-
111 QHash<int, Contact> m_contacts; // The key is a tracking id for type A, slot number for type B.-
112 QHash<int, Contact> m_lastContacts;-
113 Contact m_currentData;-
114 int m_currentSlot;-
115-
116 int findClosestContact(const QHash<int, Contact> &contacts, int x, int y, int *dist);-
117 void addTouchPoint(const Contact &contact, Qt::TouchPointStates *combinedStates);-
118 void reportPoints();-
119-
120 int hw_range_x_min;-
121 int hw_range_x_max;-
122 int hw_range_y_min;-
123 int hw_range_y_max;-
124 int hw_pressure_min;-
125 int hw_pressure_max;-
126 QString hw_name;-
127 bool m_forceToActiveWindow;-
128 bool m_typeB;-
129 QTransform m_rotate;-
130 bool m_singleTouch;-
131};-
132-
133QEvdevTouchScreenData::QEvdevTouchScreenData(QEvdevTouchScreenHandler *q_ptr, const QStringList &args)-
134 : q(q_ptr),-
135 m_lastEventType(-1),-
136 m_currentSlot(0),-
137 hw_range_x_min(0), hw_range_x_max(0),-
138 hw_range_y_min(0), hw_range_y_max(0),-
139 hw_pressure_min(0), hw_pressure_max(0),-
140 m_typeB(false), m_singleTouch(false)-
141{-
142 m_forceToActiveWindow = args.contains(QLatin1String("force_window"));-
143}
never executed: end of block
0
144-
145#define LONG_BITS (sizeof(long) << 3)-
146#define NUM_LONGS(bits) (((bits) + LONG_BITS - 1) / LONG_BITS)-
147-
148#if defined(QT_NO_MTDEV)-
149static inline bool testBit(long bit, const long *array)-
150{-
151 return (array[bit / LONG_BITS] >> bit % LONG_BITS) & 1;-
152}-
153#endif-
154-
155QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const QString &spec, QObject *parent)-
156 : QObject(parent), m_notify(Q_NULLPTR), m_fd(-1), d(Q_NULLPTR), m_device(Q_NULLPTR)-
157#if !defined(QT_NO_MTDEV)-
158 , m_mtdev(Q_NULLPTR)-
159#endif-
160{-
161 setObjectName(QLatin1String("Evdev Touch Handler"));-
162-
163 const QStringList args = spec.split(QLatin1Char(':'));-
164 int rotationAngle = 0;-
165 bool invertx = false;-
166 bool inverty = false;-
167 for (int i = 0; i < args.count(); ++i) {
i < args.count()Description
TRUEnever evaluated
FALSEnever evaluated
0
168 if (args.at(i).startsWith(QLatin1String("rotate"))) {
args.at(i).sta...ing("rotate"))Description
TRUEnever evaluated
FALSEnever evaluated
0
169 QString rotateArg = args.at(i).section(QLatin1Char('='), 1, 1);-
170 bool ok;-
171 uint argValue = rotateArg.toUInt(&ok);-
172 if (ok) {
okDescription
TRUEnever evaluated
FALSEnever evaluated
0
173 switch (argValue) {-
174 case 90:
never executed: case 90:
0
175 case 180:
never executed: case 180:
0
176 case 270:
never executed: case 270:
0
177 rotationAngle = argValue;-
178 default:
code before this statement never executed: default:
never executed: default:
0
179 break;
never executed: break;
0
180 }-
181 }-
182 } else if (args.at(i) == QLatin1String("invertx")) {
never executed: end of block
args.at(i) == ...ing("invertx")Description
TRUEnever evaluated
FALSEnever evaluated
0
183 invertx = true;-
184 } else if (args.at(i) == QLatin1String("inverty")) {
never executed: end of block
args.at(i) == ...ing("inverty")Description
TRUEnever evaluated
FALSEnever evaluated
0
185 inverty = true;-
186 }
never executed: end of block
0
187 }
never executed: end of block
0
188-
189 qCDebug(qLcEvdevTouch, "evdevtouch: Using device %s", qPrintable(device));
never executed: QMessageLogger(__FILE__, 189, __PRETTY_FUNCTION__, qLcEvdevTouch().categoryName()).debug("evdevtouch: Using device %s", QString(device).toLocal8Bit().constData());
qt_category_enabledDescription
TRUEnever evaluated
FALSEnever evaluated
0
190-
191 m_fd = QT_OPEN(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0);-
192-
193 if (m_fd >= 0) {
m_fd >= 0Description
TRUEnever evaluated
FALSEnever evaluated
0
194 m_notify = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);-
195 connect(m_notify, SIGNAL(activated(int)), this, SLOT(readData()));-
196 } else {
never executed: end of block
0
197 qErrnoWarning(errno, "evdevtouch: Cannot open input device %s", qPrintable(device));-
198 return;
never executed: return;
0
199 }-
200-
201#if !defined(QT_NO_MTDEV)-
202 m_mtdev = static_cast<mtdev *>(calloc(1, sizeof(mtdev)));-
203 int mtdeverr = mtdev_open(m_mtdev, m_fd);-
204 if (mtdeverr) {
mtdeverrDescription
TRUEnever evaluated
FALSEnever evaluated
0
205 qWarning("evdevtouch: mtdev_open failed: %d", mtdeverr);-
206 QT_CLOSE(m_fd);-
207 return;
never executed: return;
0
208 }-
209#endif-
210-
211 d = new QEvdevTouchScreenData(this, args);-
212-
213#if !defined(QT_NO_MTDEV)-
214 const char *mtdevStr = "(mtdev)";-
215 d->m_typeB = true;-
216#else-
217 const char *mtdevStr = "";-
218 long absbits[NUM_LONGS(ABS_CNT)];-
219 if (ioctl(m_fd, EVIOCGBIT(EV_ABS, sizeof(absbits)), absbits) >= 0) {-
220 d->m_typeB = testBit(ABS_MT_SLOT, absbits);-
221 d->m_singleTouch = !testBit(ABS_MT_POSITION_X, absbits);-
222 }-
223#endif-
224-
225 qCDebug(qLcEvdevTouch, "evdevtouch: %s: Protocol type %c %s (%s)", qPrintable(device),
never executed: QMessageLogger( __FILE__ , 226 , __PRETTY_FUNCTION__, qLcEvdevTouch().categoryName()).debug("evdevtouch: %s: Protocol type %c %s (%s)", QString(device).toLocal8Bit().constData(), d->m_typeB ? 'B' : 'A', mtdevStr, d->m_singleTouch ? "single" : "multi") ;
qt_category_enabledDescription
TRUEnever evaluated
FALSEnever evaluated
0
226 d->m_typeB ? 'B' : 'A', mtdevStr, d->m_singleTouch ? "single" : "multi");
never executed: QMessageLogger( __FILE__ , 226 , __PRETTY_FUNCTION__, qLcEvdevTouch().categoryName()).debug("evdevtouch: %s: Protocol type %c %s (%s)", QString(device).toLocal8Bit().constData(), d->m_typeB ? 'B' : 'A', mtdevStr, d->m_singleTouch ? "single" : "multi") ;
0
227-
228 input_absinfo absInfo;-
229 memset(&absInfo, 0, sizeof(input_absinfo));-
230 bool has_x_range = false, has_y_range = false;-
231-
232 if (ioctl(m_fd, EVIOCGABS((d->m_singleTouch ? ABS_X : ABS_MT_POSITION_X)), &absInfo) >= 0) {
ioctl(m_fd, ((...&absInfo) >= 0Description
TRUEnever evaluated
FALSEnever evaluated
0
233 qCDebug(qLcEvdevTouch, "evdevtouch: %s: min X: %d max X: %d", qPrintable(device),
never executed: QMessageLogger( __FILE__ , 234 , __PRETTY_FUNCTION__, qLcEvdevTouch().categoryName()).debug("evdevtouch: %s: min X: %d max X: %d", QString(device).toLocal8Bit().constData(), absInfo.minimum, absInfo.maximum) ;
qt_category_enabledDescription
TRUEnever evaluated
FALSEnever evaluated
0
234 absInfo.minimum, absInfo.maximum);
never executed: QMessageLogger( __FILE__ , 234 , __PRETTY_FUNCTION__, qLcEvdevTouch().categoryName()).debug("evdevtouch: %s: min X: %d max X: %d", QString(device).toLocal8Bit().constData(), absInfo.minimum, absInfo.maximum) ;
0
235 d->hw_range_x_min = absInfo.minimum;-
236 d->hw_range_x_max = absInfo.maximum;-
237 has_x_range = true;-
238 }
never executed: end of block
0
239-
240 if (ioctl(m_fd, EVIOCGABS((d->m_singleTouch ? ABS_Y : ABS_MT_POSITION_Y)), &absInfo) >= 0) {
ioctl(m_fd, ((...&absInfo) >= 0Description
TRUEnever evaluated
FALSEnever evaluated
0
241 qCDebug(qLcEvdevTouch, "evdevtouch: %s: min Y: %d max Y: %d", qPrintable(device),
never executed: QMessageLogger( __FILE__ , 242 , __PRETTY_FUNCTION__, qLcEvdevTouch().categoryName()).debug("evdevtouch: %s: min Y: %d max Y: %d", QString(device).toLocal8Bit().constData(), absInfo.minimum, absInfo.maximum) ;
qt_category_enabledDescription
TRUEnever evaluated
FALSEnever evaluated
0
242 absInfo.minimum, absInfo.maximum);
never executed: QMessageLogger( __FILE__ , 242 , __PRETTY_FUNCTION__, qLcEvdevTouch().categoryName()).debug("evdevtouch: %s: min Y: %d max Y: %d", QString(device).toLocal8Bit().constData(), absInfo.minimum, absInfo.maximum) ;
0
243 d->hw_range_y_min = absInfo.minimum;-
244 d->hw_range_y_max = absInfo.maximum;-
245 has_y_range = true;-
246 }
never executed: end of block
0
247-
248 if (!has_x_range || !has_y_range)
!has_x_rangeDescription
TRUEnever evaluated
FALSEnever evaluated
!has_y_rangeDescription
TRUEnever evaluated
FALSEnever evaluated
0
249 qWarning("evdevtouch: %s: Invalid ABS limits, behavior unspecified", qPrintable(device));
never executed: QMessageLogger(__FILE__, 249, __PRETTY_FUNCTION__).warning("evdevtouch: %s: Invalid ABS limits, behavior unspecified", QString(device).toLocal8Bit().constData());
0
250-
251 if (ioctl(m_fd, EVIOCGABS(ABS_PRESSURE), &absInfo) >= 0) {
ioctl(m_fd, ((...&absInfo) >= 0Description
TRUEnever evaluated
FALSEnever evaluated
0
252 qCDebug(qLcEvdevTouch, "evdevtouch: %s: min pressure: %d max pressure: %d", qPrintable(device),
never executed: QMessageLogger( __FILE__ , 253 , __PRETTY_FUNCTION__, qLcEvdevTouch().categoryName()).debug("evdevtouch: %s: min pressure: %d max pressure: %d", QString(device).toLocal8Bit().constData(), absInfo.minimum, absInfo.maximum) ;
qt_category_enabledDescription
TRUEnever evaluated
FALSEnever evaluated
0
253 absInfo.minimum, absInfo.maximum);
never executed: QMessageLogger( __FILE__ , 253 , __PRETTY_FUNCTION__, qLcEvdevTouch().categoryName()).debug("evdevtouch: %s: min pressure: %d max pressure: %d", QString(device).toLocal8Bit().constData(), absInfo.minimum, absInfo.maximum) ;
0
254 if (absInfo.maximum > absInfo.minimum) {
absInfo.maximu...bsInfo.minimumDescription
TRUEnever evaluated
FALSEnever evaluated
0
255 d->hw_pressure_min = absInfo.minimum;-
256 d->hw_pressure_max = absInfo.maximum;-
257 }
never executed: end of block
0
258 }
never executed: end of block
0
259-
260 char name[1024];-
261 if (ioctl(m_fd, EVIOCGNAME(sizeof(name) - 1), name) >= 0) {
ioctl(m_fd, ((...)), name) >= 0Description
TRUEnever evaluated
FALSEnever evaluated
0
262 d->hw_name = QString::fromLocal8Bit(name);-
263 qCDebug(qLcEvdevTouch, "evdevtouch: %s: device name: %s", qPrintable(device), name);
never executed: QMessageLogger(__FILE__, 263, __PRETTY_FUNCTION__, qLcEvdevTouch().categoryName()).debug("evdevtouch: %s: device name: %s", QString(device).toLocal8Bit().constData(), name);
qt_category_enabledDescription
TRUEnever evaluated
FALSEnever evaluated
0
264 }
never executed: end of block
0
265-
266 // Fix up the coordinate ranges for am335x in case the kernel driver does not have them fixed.-
267 if (d->hw_name == QLatin1String("ti-tsc")) {
d->hw_name == ...ring("ti-tsc")Description
TRUEnever evaluated
FALSEnever evaluated
0
268 if (d->hw_range_x_min == 0 && d->hw_range_x_max == 4095) {
d->hw_range_x_min == 0Description
TRUEnever evaluated
FALSEnever evaluated
d->hw_range_x_max == 4095Description
TRUEnever evaluated
FALSEnever evaluated
0
269 d->hw_range_x_min = 165;-
270 d->hw_range_x_max = 4016;-
271 }
never executed: end of block
0
272 if (d->hw_range_y_min == 0 && d->hw_range_y_max == 4095) {
d->hw_range_y_min == 0Description
TRUEnever evaluated
FALSEnever evaluated
d->hw_range_y_max == 4095Description
TRUEnever evaluated
FALSEnever evaluated
0
273 d->hw_range_y_min = 220;-
274 d->hw_range_y_max = 3907;-
275 }
never executed: end of block
0
276 qCDebug(qLcEvdevTouch, "evdevtouch: found ti-tsc, overriding: min X: %d max X: %d min Y: %d max Y: %d",
never executed: QMessageLogger( __FILE__ , 277 , __PRETTY_FUNCTION__, qLcEvdevTouch().categoryName()).debug("evdevtouch: found ti-tsc, overriding: min X: %d max X: %d min Y: %d max Y: %d", d->hw_range_x_min, d->hw_range_x_max, d->hw_range_y_min, d->hw_range_y_max) ;
qt_category_enabledDescription
TRUEnever evaluated
FALSEnever evaluated
0
277 d->hw_range_x_min, d->hw_range_x_max, d->hw_range_y_min, d->hw_range_y_max);
never executed: QMessageLogger( __FILE__ , 277 , __PRETTY_FUNCTION__, qLcEvdevTouch().categoryName()).debug("evdevtouch: found ti-tsc, overriding: min X: %d max X: %d min Y: %d max Y: %d", d->hw_range_x_min, d->hw_range_x_max, d->hw_range_y_min, d->hw_range_y_max) ;
0
278 }
never executed: end of block
0
279-
280 bool grabSuccess = !ioctl(m_fd, EVIOCGRAB, (void *) 1);-
281 if (grabSuccess)
grabSuccessDescription
TRUEnever evaluated
FALSEnever evaluated
0
282 ioctl(m_fd, EVIOCGRAB, (void *) 0);
never executed: ioctl(m_fd, (((1U) << (((0 +8)+8)+14)) | ((('E')) << (0 +8)) | (((0x90)) << 0) | ((((sizeof(int)))) << ((0 +8)+8))), (void *) 0);
0
283 else-
284 qWarning("evdevtouch: The device is grabbed by another process. No events will be read.");
never executed: QMessageLogger(__FILE__, 284, __PRETTY_FUNCTION__).warning("evdevtouch: The device is grabbed by another process. No events will be read.");
0
285-
286 if (rotationAngle)
rotationAngleDescription
TRUEnever evaluated
FALSEnever evaluated
0
287 d->m_rotate = QTransform::fromTranslate(0.5, 0.5).rotate(rotationAngle).translate(-0.5, -0.5);
never executed: d->m_rotate = QTransform::fromTranslate(0.5, 0.5).rotate(rotationAngle).translate(-0.5, -0.5);
0
288-
289 if (invertx)
invertxDescription
TRUEnever evaluated
FALSEnever evaluated
0
290 d->m_rotate *= QTransform::fromTranslate(0.5, 0.5).scale(-1.0, 1.0).translate(-0.5, -0.5);
never executed: d->m_rotate *= QTransform::fromTranslate(0.5, 0.5).scale(-1.0, 1.0).translate(-0.5, -0.5);
0
291-
292 if (inverty)
invertyDescription
TRUEnever evaluated
FALSEnever evaluated
0
293 d->m_rotate *= QTransform::fromTranslate(0.5, 0.5).scale(1.0, -1.0).translate(-0.5, -0.5);
never executed: d->m_rotate *= QTransform::fromTranslate(0.5, 0.5).scale(1.0, -1.0).translate(-0.5, -0.5);
0
294-
295 registerTouchDevice();-
296}
never executed: end of block
0
297-
298QEvdevTouchScreenHandler::~QEvdevTouchScreenHandler()-
299{-
300#if !defined(QT_NO_MTDEV)-
301 if (m_mtdev) {
m_mtdevDescription
TRUEnever evaluated
FALSEnever evaluated
0
302 mtdev_close(m_mtdev);-
303 free(m_mtdev);-
304 }
never executed: end of block
0
305#endif-
306-
307 if (m_fd >= 0)
m_fd >= 0Description
TRUEnever evaluated
FALSEnever evaluated
0
308 QT_CLOSE(m_fd);
never executed: qt_safe_close(m_fd);
0
309-
310 delete d;-
311-
312 unregisterTouchDevice();-
313}
never executed: end of block
0
314-
315QTouchDevice *QEvdevTouchScreenHandler::touchDevice() const-
316{-
317 return m_device;
never executed: return m_device;
0
318}-
319-
320void QEvdevTouchScreenHandler::readData()-
321{-
322 ::input_event buffer[32];-
323 int events = 0;-
324-
325#if !defined(QT_NO_MTDEV)-
326 forever {-
327 do {-
328 events = mtdev_get(m_mtdev, m_fd, buffer, sizeof(buffer) / sizeof(::input_event));-
329 // keep trying mtdev_get if we get interrupted. note that we do not-
330 // (and should not) handle EAGAIN; EAGAIN means that reading would-
331 // block and we'll get back here later to try again anyway.-
332 } while (events == -1 && errno == EINTR);
never executed: end of block
events == -1Description
TRUEnever evaluated
FALSEnever evaluated
(*__errno_location ()) == 4Description
TRUEnever evaluated
FALSEnever evaluated
0
333-
334 // 0 events is EOF, -1 means error, handle both in the same place-
335 if (events <= 0)
events <= 0Description
TRUEnever evaluated
FALSEnever evaluated
0
336 goto err;
never executed: goto err;
0
337-
338 // process our shiny new events-
339 for (int i = 0; i < events; ++i)
i < eventsDescription
TRUEnever evaluated
FALSEnever evaluated
0
340 d->processInputEvent(&buffer[i]);
never executed: d->processInputEvent(&buffer[i]);
0
341-
342 // and try to get more-
343 }
never executed: end of block
0
344#else-
345 int n = 0;-
346 for (; ;) {-
347 events = QT_READ(m_fd, reinterpret_cast<char*>(buffer) + n, sizeof(buffer) - n);-
348 if (events <= 0)-
349 goto err;-
350 n += events;-
351 if (n % sizeof(::input_event) == 0)-
352 break;-
353 }-
354-
355 n /= sizeof(::input_event);-
356-
357 for (int i = 0; i < n; ++i)-
358 d->processInputEvent(&buffer[i]);-
359#endif-
360 return;
never executed: return;
0
361-
362err:-
363 if (!events) {
!eventsDescription
TRUEnever evaluated
FALSEnever evaluated
0
364 qWarning("evdevtouch: Got EOF from input device");-
365 return;
never executed: return;
0
366 } else if (events < 0) {
events < 0Description
TRUEnever evaluated
FALSEnever evaluated
0
367 if (errno != EINTR && errno != EAGAIN) {
(*__errno_location ()) != 4Description
TRUEnever evaluated
FALSEnever evaluated
(*__errno_location ()) != 11Description
TRUEnever evaluated
FALSEnever evaluated
0
368 qErrnoWarning(errno, "evdevtouch: Could not read from input device");-
369 if (errno == ENODEV) { // device got disconnected -> stop reading
(*__errno_location ()) == 19Description
TRUEnever evaluated
FALSEnever evaluated
0
370 delete m_notify;-
371 m_notify = Q_NULLPTR;-
372-
373 QT_CLOSE(m_fd);-
374 m_fd = -1;-
375-
376 unregisterTouchDevice();-
377 }
never executed: end of block
0
378 return;
never executed: return;
0
379 }-
380 }
never executed: end of block
0
381}
never executed: end of block
0
382-
383void QEvdevTouchScreenHandler::registerTouchDevice()-
384{-
385 if (m_device)
m_deviceDescription
TRUEnever evaluated
FALSEnever evaluated
0
386 return;
never executed: return;
0
387-
388 m_device = new QTouchDevice;-
389 m_device->setName(d->hw_name);-
390 m_device->setType(QTouchDevice::TouchScreen);-
391 m_device->setCapabilities(QTouchDevice::Position | QTouchDevice::Area);-
392 if (d->hw_pressure_max > d->hw_pressure_min)
d->hw_pressure...w_pressure_minDescription
TRUEnever evaluated
FALSEnever evaluated
0
393 m_device->setCapabilities(m_device->capabilities() | QTouchDevice::Pressure);
never executed: m_device->setCapabilities(m_device->capabilities() | QTouchDevice::Pressure);
0
394-
395 QWindowSystemInterface::registerTouchDevice(m_device);-
396}
never executed: end of block
0
397-
398void QEvdevTouchScreenHandler::unregisterTouchDevice()-
399{-
400 if (!m_device)
!m_deviceDescription
TRUEnever evaluated
FALSEnever evaluated
0
401 return;
never executed: return;
0
402-
403 // At app exit the cleanup may have already been done, avoid-
404 // double delete by checking the list first.-
405 if (QWindowSystemInterface::isTouchDeviceRegistered(m_device)) {
QWindowSystemI...ered(m_device)Description
TRUEnever evaluated
FALSEnever evaluated
0
406 QWindowSystemInterface::unregisterTouchDevice(m_device);-
407 delete m_device;-
408 }
never executed: end of block
0
409-
410 m_device = Q_NULLPTR;-
411}
never executed: end of block
0
412-
413void QEvdevTouchScreenData::addTouchPoint(const Contact &contact, Qt::TouchPointStates *combinedStates)-
414{-
415 QWindowSystemInterface::TouchPoint tp;-
416 tp.id = contact.trackingId;-
417 tp.flags = contact.flags;-
418 tp.state = contact.state;-
419 *combinedStates |= tp.state;-
420-
421 // Store the HW coordinates for now, will be updated later.-
422 tp.area = QRectF(0, 0, contact.maj, contact.maj);-
423 tp.area.moveCenter(QPoint(contact.x, contact.y));-
424 tp.pressure = contact.pressure;-
425-
426 // Get a normalized position in range 0..1.-
427 tp.normalPosition = QPointF((contact.x - hw_range_x_min) / qreal(hw_range_x_max - hw_range_x_min),-
428 (contact.y - hw_range_y_min) / qreal(hw_range_y_max - hw_range_y_min));-
429-
430 if (!m_rotate.isIdentity())
!m_rotate.isIdentity()Description
TRUEnever evaluated
FALSEnever evaluated
0
431 tp.normalPosition = m_rotate.map(tp.normalPosition);
never executed: tp.normalPosition = m_rotate.map(tp.normalPosition);
0
432-
433 tp.rawPositions.append(QPointF(contact.x, contact.y));-
434-
435 m_touchPoints.append(tp);-
436}
never executed: end of block
0
437-
438void QEvdevTouchScreenData::processInputEvent(input_event *data)-
439{-
440 if (data->type == EV_ABS) {
data->type == 0x03Description
TRUEnever evaluated
FALSEnever evaluated
0
441-
442 if (data->code == ABS_MT_POSITION_X || (m_singleTouch && data->code == ABS_X)) {
data->code == 0x35Description
TRUEnever evaluated
FALSEnever evaluated
m_singleTouchDescription
TRUEnever evaluated
FALSEnever evaluated
data->code == 0x00Description
TRUEnever evaluated
FALSEnever evaluated
0
443 m_currentData.x = qBound(hw_range_x_min, data->value, hw_range_x_max);-
444 if (m_singleTouch)
m_singleTouchDescription
TRUEnever evaluated
FALSEnever evaluated
0
445 m_contacts[m_currentSlot].x = m_currentData.x;
never executed: m_contacts[m_currentSlot].x = m_currentData.x;
0
446 if (m_typeB) {
m_typeBDescription
TRUEnever evaluated
FALSEnever evaluated
0
447 m_contacts[m_currentSlot].x = m_currentData.x;-
448 if (m_contacts[m_currentSlot].state == Qt::TouchPointStationary)
m_contacts[m_c...ointStationaryDescription
TRUEnever evaluated
FALSEnever evaluated
0
449 m_contacts[m_currentSlot].state = Qt::TouchPointMoved;
never executed: m_contacts[m_currentSlot].state = Qt::TouchPointMoved;
0
450 }
never executed: end of block
0
451 } else if (data->code == ABS_MT_POSITION_Y || (m_singleTouch && data->code == ABS_Y)) {
never executed: end of block
data->code == 0x36Description
TRUEnever evaluated
FALSEnever evaluated
m_singleTouchDescription
TRUEnever evaluated
FALSEnever evaluated
data->code == 0x01Description
TRUEnever evaluated
FALSEnever evaluated
0
452 m_currentData.y = qBound(hw_range_y_min, data->value, hw_range_y_max);-
453 if (m_singleTouch)
m_singleTouchDescription
TRUEnever evaluated
FALSEnever evaluated
0
454 m_contacts[m_currentSlot].y = m_currentData.y;
never executed: m_contacts[m_currentSlot].y = m_currentData.y;
0
455 if (m_typeB) {
m_typeBDescription
TRUEnever evaluated
FALSEnever evaluated
0
456 m_contacts[m_currentSlot].y = m_currentData.y;-
457 if (m_contacts[m_currentSlot].state == Qt::TouchPointStationary)
m_contacts[m_c...ointStationaryDescription
TRUEnever evaluated
FALSEnever evaluated
0
458 m_contacts[m_currentSlot].state = Qt::TouchPointMoved;
never executed: m_contacts[m_currentSlot].state = Qt::TouchPointMoved;
0
459 }
never executed: end of block
0
460 } else if (data->code == ABS_MT_TRACKING_ID) {
never executed: end of block
data->code == 0x39Description
TRUEnever evaluated
FALSEnever evaluated
0
461 m_currentData.trackingId = data->value;-
462 if (m_typeB) {
m_typeBDescription
TRUEnever evaluated
FALSEnever evaluated
0
463 if (m_currentData.trackingId == -1) {
m_currentData.trackingId == -1Description
TRUEnever evaluated
FALSEnever evaluated
0
464 m_contacts[m_currentSlot].state = Qt::TouchPointReleased;-
465 } else {
never executed: end of block
0
466 m_contacts[m_currentSlot].state = Qt::TouchPointPressed;-
467 m_contacts[m_currentSlot].trackingId = m_currentData.trackingId;-
468 }
never executed: end of block
0
469 }-
470 } else if (data->code == ABS_MT_TOUCH_MAJOR) {
never executed: end of block
data->code == 0x30Description
TRUEnever evaluated
FALSEnever evaluated
0
471 m_currentData.maj = data->value;-
472 if (data->value == 0)
data->value == 0Description
TRUEnever evaluated
FALSEnever evaluated
0
473 m_currentData.state = Qt::TouchPointReleased;
never executed: m_currentData.state = Qt::TouchPointReleased;
0
474 if (m_typeB)
m_typeBDescription
TRUEnever evaluated
FALSEnever evaluated
0
475 m_contacts[m_currentSlot].maj = m_currentData.maj;
never executed: m_contacts[m_currentSlot].maj = m_currentData.maj;
0
476 } else if (data->code == ABS_PRESSURE) {
never executed: end of block
data->code == 0x18Description
TRUEnever evaluated
FALSEnever evaluated
0
477 m_currentData.pressure = qBound(hw_pressure_min, data->value, hw_pressure_max);-
478 if (m_typeB || m_singleTouch)
m_typeBDescription
TRUEnever evaluated
FALSEnever evaluated
m_singleTouchDescription
TRUEnever evaluated
FALSEnever evaluated
0
479 m_contacts[m_currentSlot].pressure = m_currentData.pressure;
never executed: m_contacts[m_currentSlot].pressure = m_currentData.pressure;
0
480 } else if (data->code == ABS_MT_SLOT) {
never executed: end of block
data->code == 0x2fDescription
TRUEnever evaluated
FALSEnever evaluated
0
481 m_currentSlot = data->value;-
482 }
never executed: end of block
0
483-
484 } else if (data->type == EV_KEY && !m_typeB) {
never executed: end of block
data->type == 0x01Description
TRUEnever evaluated
FALSEnever evaluated
!m_typeBDescription
TRUEnever evaluated
FALSEnever evaluated
0
485 if (data->code == BTN_TOUCH && data->value == 0)
data->code == 0x14aDescription
TRUEnever evaluated
FALSEnever evaluated
data->value == 0Description
TRUEnever evaluated
FALSEnever evaluated
0
486 m_contacts[m_currentSlot].state = Qt::TouchPointReleased;
never executed: m_contacts[m_currentSlot].state = Qt::TouchPointReleased;
0
487 } else if (data->type == EV_SYN && data->code == SYN_MT_REPORT && m_lastEventType != EV_SYN) {
never executed: end of block
data->type == 0x00Description
TRUEnever evaluated
FALSEnever evaluated
data->code == 2Description
TRUEnever evaluated
FALSEnever evaluated
m_lastEventType != 0x00Description
TRUEnever evaluated
FALSEnever evaluated
0
488-
489 // If there is no tracking id, one will be generated later.-
490 // Until that use a temporary key.-
491 int key = m_currentData.trackingId;-
492 if (key == -1)
key == -1Description
TRUEnever evaluated
FALSEnever evaluated
0
493 key = m_contacts.count();
never executed: key = m_contacts.count();
0
494-
495 m_contacts.insert(key, m_currentData);-
496 m_currentData = Contact();-
497-
498 } else if (data->type == EV_SYN && data->code == SYN_REPORT) {
never executed: end of block
data->type == 0x00Description
TRUEnever evaluated
FALSEnever evaluated
data->code == 0Description
TRUEnever evaluated
FALSEnever evaluated
0
499-
500 // Ensure valid IDs even when the driver does not report ABS_MT_TRACKING_ID.-
501 if (!m_contacts.isEmpty() && m_contacts.constBegin().value().trackingId == -1)
!m_contacts.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
m_contacts.con...ackingId == -1Description
TRUEnever evaluated
FALSEnever evaluated
0
502 assignIds();
never executed: assignIds();
0
503-
504 m_touchPoints.clear();-
505 Qt::TouchPointStates combinedStates;-
506-
507 QMutableHashIterator<int, Contact> it(m_contacts);-
508 while (it.hasNext()) {
it.hasNext()Description
TRUEnever evaluated
FALSEnever evaluated
0
509 it.next();-
510 Contact &contact(it.value());-
511-
512 if (!contact.state)
!contact.stateDescription
TRUEnever evaluated
FALSEnever evaluated
0
513 continue;
never executed: continue;
0
514-
515 int key = m_typeB ? it.key() : contact.trackingId;
m_typeBDescription
TRUEnever evaluated
FALSEnever evaluated
0
516 if (!m_typeB && m_lastContacts.contains(key)) {
!m_typeBDescription
TRUEnever evaluated
FALSEnever evaluated
m_lastContacts.contains(key)Description
TRUEnever evaluated
FALSEnever evaluated
0
517 const Contact &prev(m_lastContacts.value(key));-
518 if (contact.state == Qt::TouchPointReleased) {
contact.state ...hPointReleasedDescription
TRUEnever evaluated
FALSEnever evaluated
0
519 // Copy over the previous values for released points, just in case.-
520 contact.x = prev.x;-
521 contact.y = prev.y;-
522 contact.maj = prev.maj;-
523 } else {
never executed: end of block
0
524 contact.state = (prev.x == contact.x && prev.y == contact.y)
prev.x == contact.xDescription
TRUEnever evaluated
FALSEnever evaluated
prev.y == contact.yDescription
TRUEnever evaluated
FALSEnever evaluated
0
525 ? Qt::TouchPointStationary : Qt::TouchPointMoved;-
526 }
never executed: end of block
0
527 }-
528-
529 // Avoid reporting a contact in released state more than once.-
530 if (!m_typeB && contact.state == Qt::TouchPointReleased
!m_typeBDescription
TRUEnever evaluated
FALSEnever evaluated
contact.state ...hPointReleasedDescription
TRUEnever evaluated
FALSEnever evaluated
0
531 && !m_lastContacts.contains(key)) {
!m_lastContacts.contains(key)Description
TRUEnever evaluated
FALSEnever evaluated
0
532 it.remove();-
533 continue;
never executed: continue;
0
534 }-
535-
536 addTouchPoint(contact, &combinedStates);-
537 }
never executed: end of block
0
538-
539 // Now look for contacts that have disappeared since the last sync.-
540 it = m_lastContacts;-
541 while (it.hasNext()) {
it.hasNext()Description
TRUEnever evaluated
FALSEnever evaluated
0
542 it.next();-
543 Contact &contact(it.value());-
544 int key = m_typeB ? it.key() : contact.trackingId;
m_typeBDescription
TRUEnever evaluated
FALSEnever evaluated
0
545 if (m_typeB) {
m_typeBDescription
TRUEnever evaluated
FALSEnever evaluated
0
546 if (contact.trackingId != m_contacts[key].trackingId && contact.state) {
contact.tracki...ey].trackingIdDescription
TRUEnever evaluated
FALSEnever evaluated
contact.stateDescription
TRUEnever evaluated
FALSEnever evaluated
0
547 contact.state = Qt::TouchPointReleased;-
548 addTouchPoint(contact, &combinedStates);-
549 }
never executed: end of block
0
550 } else {
never executed: end of block
0
551 if (!m_contacts.contains(key)) {
!m_contacts.contains(key)Description
TRUEnever evaluated
FALSEnever evaluated
0
552 contact.state = Qt::TouchPointReleased;-
553 addTouchPoint(contact, &combinedStates);-
554 }
never executed: end of block
0
555 }
never executed: end of block
0
556 }-
557-
558 // Remove contacts that have just been reported as released.-
559 it = m_contacts;-
560 while (it.hasNext()) {
it.hasNext()Description
TRUEnever evaluated
FALSEnever evaluated
0
561 it.next();-
562 Contact &contact(it.value());-
563-
564 if (!contact.state)
!contact.stateDescription
TRUEnever evaluated
FALSEnever evaluated
0
565 continue;
never executed: continue;
0
566-
567 if (contact.state == Qt::TouchPointReleased) {
contact.state ...hPointReleasedDescription
TRUEnever evaluated
FALSEnever evaluated
0
568 if (m_typeB)
m_typeBDescription
TRUEnever evaluated
FALSEnever evaluated
0
569 contact.state = static_cast<Qt::TouchPointState>(0);
never executed: contact.state = static_cast<Qt::TouchPointState>(0);
0
570 else-
571 it.remove();
never executed: it.remove();
0
572 } else {-
573 contact.state = Qt::TouchPointStationary;-
574 }
never executed: end of block
0
575 }-
576-
577 m_lastContacts = m_contacts;-
578 if (!m_typeB && !m_singleTouch)
!m_typeBDescription
TRUEnever evaluated
FALSEnever evaluated
!m_singleTouchDescription
TRUEnever evaluated
FALSEnever evaluated
0
579 m_contacts.clear();
never executed: m_contacts.clear();
0
580-
581 if (!m_touchPoints.isEmpty() && combinedStates != Qt::TouchPointStationary)
!m_touchPoints.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
combinedStates...ointStationaryDescription
TRUEnever evaluated
FALSEnever evaluated
0
582 reportPoints();
never executed: reportPoints();
0
583 }
never executed: end of block
0
584-
585 m_lastEventType = data->type;-
586}
never executed: end of block
0
587-
588int QEvdevTouchScreenData::findClosestContact(const QHash<int, Contact> &contacts, int x, int y, int *dist)-
589{-
590 int minDist = -1, id = -1;-
591 for (QHash<int, Contact>::const_iterator it = contacts.constBegin(), ite = contacts.constEnd();-
592 it != ite; ++it) {
it != iteDescription
TRUEnever evaluated
FALSEnever evaluated
0
593 const Contact &contact(it.value());-
594 int dx = x - contact.x;-
595 int dy = y - contact.y;-
596 int dist = dx * dx + dy * dy;-
597 if (minDist == -1 || dist < minDist) {
minDist == -1Description
TRUEnever evaluated
FALSEnever evaluated
dist < minDistDescription
TRUEnever evaluated
FALSEnever evaluated
0
598 minDist = dist;-
599 id = contact.trackingId;-
600 }
never executed: end of block
0
601 }
never executed: end of block
0
602 if (dist)
distDescription
TRUEnever evaluated
FALSEnever evaluated
0
603 *dist = minDist;
never executed: *dist = minDist;
0
604 return id;
never executed: return id;
0
605}-
606-
607void QEvdevTouchScreenData::assignIds()-
608{-
609 QHash<int, Contact> candidates = m_lastContacts, pending = m_contacts, newContacts;-
610 int maxId = -1;-
611 QHash<int, Contact>::iterator it, ite, bestMatch;-
612 while (!pending.isEmpty() && !candidates.isEmpty()) {
!pending.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
!candidates.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
613 int bestDist = -1, bestId = 0;-
614 for (it = pending.begin(), ite = pending.end(); it != ite; ++it) {
it != iteDescription
TRUEnever evaluated
FALSEnever evaluated
0
615 int dist;-
616 int id = findClosestContact(candidates, it->x, it->y, &dist);-
617 if (id >= 0 && (bestDist == -1 || dist < bestDist)) {
id >= 0Description
TRUEnever evaluated
FALSEnever evaluated
bestDist == -1Description
TRUEnever evaluated
FALSEnever evaluated
dist < bestDistDescription
TRUEnever evaluated
FALSEnever evaluated
0
618 bestDist = dist;-
619 bestId = id;-
620 bestMatch = it;-
621 }
never executed: end of block
0
622 }
never executed: end of block
0
623 if (bestDist >= 0) {
bestDist >= 0Description
TRUEnever evaluated
FALSEnever evaluated
0
624 bestMatch->trackingId = bestId;-
625 newContacts.insert(bestId, *bestMatch);-
626 candidates.remove(bestId);-
627 pending.erase(bestMatch);-
628 if (bestId > maxId)
bestId > maxIdDescription
TRUEnever evaluated
FALSEnever evaluated
0
629 maxId = bestId;
never executed: maxId = bestId;
0
630 }
never executed: end of block
0
631 }
never executed: end of block
0
632 if (candidates.isEmpty()) {
candidates.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
633 for (it = pending.begin(), ite = pending.end(); it != ite; ++it) {
it != iteDescription
TRUEnever evaluated
FALSEnever evaluated
0
634 it->trackingId = ++maxId;-
635 newContacts.insert(it->trackingId, *it);-
636 }
never executed: end of block
0
637 }
never executed: end of block
0
638 m_contacts = newContacts;-
639}
never executed: end of block
0
640-
641void QEvdevTouchScreenData::reportPoints()-
642{-
643 QRect winRect;-
644 if (m_forceToActiveWindow) {
m_forceToActiveWindowDescription
TRUEnever evaluated
FALSEnever evaluated
0
645 QWindow *win = QGuiApplication::focusWindow();-
646 if (!win)
!winDescription
TRUEnever evaluated
FALSEnever evaluated
0
647 return;
never executed: return;
0
648 winRect = QHighDpi::toNativePixels(win->geometry(), win);-
649 } else {
never executed: end of block
0
650 QScreen *primary = QGuiApplication::primaryScreen();-
651 winRect = QHighDpi::toNativePixels(primary->geometry(), primary);-
652 }
never executed: end of block
0
653-
654 const int hw_w = hw_range_x_max - hw_range_x_min;-
655 const int hw_h = hw_range_y_max - hw_range_y_min;-
656-
657 // Map the coordinates based on the normalized position. QPA expects 'area'-
658 // to be in screen coordinates.-
659 const int pointCount = m_touchPoints.count();-
660 for (int i = 0; i < pointCount; ++i) {
i < pointCountDescription
TRUEnever evaluated
FALSEnever evaluated
0
661 QWindowSystemInterface::TouchPoint &tp(m_touchPoints[i]);-
662-
663 // Generate a screen position that is always inside the active window-
664 // or the primary screen. Even though we report this as a QRectF, internally-
665 // Qt uses QRect/QPoint so we need to bound the size to winRect.size() - QSize(1, 1)-
666 const qreal wx = winRect.left() + tp.normalPosition.x() * (winRect.width() - 1);-
667 const qreal wy = winRect.top() + tp.normalPosition.y() * (winRect.height() - 1);-
668 const qreal sizeRatio = (winRect.width() + winRect.height()) / qreal(hw_w + hw_h);-
669 if (tp.area.width() == -1) // touch major was not provided
tp.area.width() == -1Description
TRUEnever evaluated
FALSEnever evaluated
0
670 tp.area = QRectF(0, 0, 8, 8);
never executed: tp.area = QRectF(0, 0, 8, 8);
0
671 else-
672 tp.area = QRectF(0, 0, tp.area.width() * sizeRatio, tp.area.height() * sizeRatio);
never executed: tp.area = QRectF(0, 0, tp.area.width() * sizeRatio, tp.area.height() * sizeRatio);
0
673 tp.area.moveCenter(QPointF(wx, wy));-
674-
675 // Calculate normalized pressure.-
676 if (!hw_pressure_min && !hw_pressure_max)
!hw_pressure_minDescription
TRUEnever evaluated
FALSEnever evaluated
!hw_pressure_maxDescription
TRUEnever evaluated
FALSEnever evaluated
0
677 tp.pressure = tp.state == Qt::TouchPointReleased ? 0 : 1;
never executed: tp.pressure = tp.state == Qt::TouchPointReleased ? 0 : 1;
tp.state == Qt...hPointReleasedDescription
TRUEnever evaluated
FALSEnever evaluated
0
678 else-
679 tp.pressure = (tp.pressure - hw_pressure_min) / qreal(hw_pressure_max - hw_pressure_min);
never executed: tp.pressure = (tp.pressure - hw_pressure_min) / qreal(hw_pressure_max - hw_pressure_min);
0
680 }-
681-
682 QWindowSystemInterface::handleTouchEvent(Q_NULLPTR, q->touchDevice(), m_touchPoints);-
683}
never executed: end of block
0
684-
685-
686QEvdevTouchScreenHandlerThread::QEvdevTouchScreenHandlerThread(const QString &device, const QString &spec, QObject *parent)-
687 : QDaemonThread(parent), m_device(device), m_spec(spec), m_handler(Q_NULLPTR), m_touchDeviceRegistered(false)-
688{-
689 start();-
690}
never executed: end of block
0
691-
692QEvdevTouchScreenHandlerThread::~QEvdevTouchScreenHandlerThread()-
693{-
694 quit();-
695 wait();-
696}
never executed: end of block
0
697-
698void QEvdevTouchScreenHandlerThread::run()-
699{-
700 m_handler = new QEvdevTouchScreenHandler(m_device, m_spec);-
701 // Report the registration to the parent thread by invoking the method asynchronously-
702 QMetaObject::invokeMethod(this, "notifyTouchDeviceRegistered", Qt::QueuedConnection);-
703-
704 exec();-
705-
706 delete m_handler;-
707 m_handler = Q_NULLPTR;-
708}
never executed: end of block
0
709-
710bool QEvdevTouchScreenHandlerThread::isTouchDeviceRegistered() const-
711{-
712 return m_touchDeviceRegistered;
never executed: return m_touchDeviceRegistered;
0
713}-
714-
715void QEvdevTouchScreenHandlerThread::notifyTouchDeviceRegistered()-
716{-
717 m_touchDeviceRegistered = true;-
718 emit touchDeviceRegistered();-
719}
never executed: end of block
0
720-
721-
722QT_END_NAMESPACE-
Source codeSwitch to Preprocessed file

Generated by Squish Coco Non-Commercial 4.3.0-BETA-master-30-08-2018-4cb69e9