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 QtGui 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 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 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qmousetslib_qws.h"
43 
44 #if !defined(QT_NO_QWS_MOUSE_TSLIB) || defined(QT_PLUGIN)
45 
46 #include <QtCore/qregexp.h>
47 #include <QtCore/qstringlist.h>
48 #include "qsocketnotifier.h"
49 #include "qscreen_qws.h"
50 
51 #include <tslib.h>
52 #include <errno.h>
53 
54 QT_BEGIN_NAMESPACE
55 
56 #ifdef TSLIBMOUSEHANDLER_DEBUG
57 #  include <QtCore/QDebug>
58 #endif
59 
60 /*!
61     \internal
62 
63     \class QWSTslibMouseHandler
64     \ingroup qws
65 
66     \brief The QWSTslibMouseHandler class implements a mouse driver
67     for the Universal Touch Screen Library, tslib.
68 
69     QWSTslibMouseHandler inherits the QWSCalibratedMouseHandler class,
70     providing calibration and noise reduction functionality in
71     addition to generating mouse events, for devices using the
72     Universal Touch Screen Library.
73 
74     To be able to compile this mouse handler, \l{Qt for Embedded Linux}
75     must be configured with the \c -qt-mouse-tslib option, see the
76     \l{Pointer Handling} documentation for details. In addition, the tslib
77     headers and library must be present in the build environment.  The
78     tslib sources can be downloaded from \l
79     {http://tslib.berlios.de/}.  Use the \c -L and \c -I options
80     with \c configure to explicitly specify the location of the
81     library and its headers:
82 
83     \snippet doc/src/snippets/code/src_gui_embedded_qmousetslib_qws.cpp 0
84 
85     In order to use this mouse handler, tslib must also be correctly
86     installed on the target machine. This includes providing a \c
87     ts.conf configuration file and setting the necessary environment
88     variables, see the README file provided with tslib for details.
89 
90     The ts.conf file will usually contain the following two lines
91 
92     \snippet doc/src/snippets/code/src_gui_embedded_qmousetslib_qws.cpp 1
93 
94     To make \l{Qt for Embedded Linux} explicitly choose the tslib mouse
95     handler, set the QWS_MOUSE_PROTO environment variable.
96 
97     \sa {Pointer Handling}, {Qt for Embedded Linux}
98 */
99 
100 class QWSTslibMouseHandlerPrivate : public QObject
101 {
102     Q_OBJECT
103 public:
104     QWSTslibMouseHandlerPrivate(QWSTslibMouseHandler *h,
105                                 const QString &device);
106     ~QWSTslibMouseHandlerPrivate();
107 
108     void suspend();
109     void resume();
110 
111     void calibrate(const QWSPointerCalibrationData *data);
112     void clearCalibration();
113 
114 private:
115     QWSTslibMouseHandler *handler;
116     struct tsdev *dev;
117     QSocketNotifier *mouseNotifier;
118     int jitter_limit;
119 
120     struct ts_sample lastSample;
121     bool wasPressed;
122     int lastdx;
123     int lastdy;
124 
125     bool calibrated;
126     QString devName;
127 
128     bool open();
129     void close();
130     inline bool get_sample(struct ts_sample *sample);
131 
132 private slots:
133     void readMouseData();
134 };
135 
QWSTslibMouseHandlerPrivate(QWSTslibMouseHandler * h,const QString & device)136 QWSTslibMouseHandlerPrivate::QWSTslibMouseHandlerPrivate(QWSTslibMouseHandler *h,
137                                                          const QString &device)
138     : handler(h), dev(0), mouseNotifier(0), jitter_limit(3)
139 {
140     QStringList args = device.split(QLatin1Char(':'), QString::SkipEmptyParts);
141     QRegExp jitterRegex(QLatin1String("^jitter_limit=(\\d+)$"));
142     int index = args.indexOf(jitterRegex);
143     if (index >= 0) {
144         jitter_limit = jitterRegex.cap(1).toInt();
145         args.removeAt(index);
146     }
147 
148     devName = args.join(QString());
149 
150     if (devName.isNull()) {
151         const char *str = getenv("TSLIB_TSDEVICE");
152         if (str)
153             devName = QString::fromLocal8Bit(str);
154     }
155 
156     if (devName.isNull())
157         devName = QLatin1String("/dev/ts");
158 
159     if (!open())
160         return;
161 
162     calibrated = true;
163 
164     int fd = ts_fd(dev);
165     mouseNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
166     connect(mouseNotifier, SIGNAL(activated(int)),this, SLOT(readMouseData()));
167     resume();
168 }
169 
~QWSTslibMouseHandlerPrivate()170 QWSTslibMouseHandlerPrivate::~QWSTslibMouseHandlerPrivate()
171 {
172     close();
173 }
174 
open()175 bool QWSTslibMouseHandlerPrivate::open()
176 {
177     dev = ts_open(devName.toLocal8Bit().constData(), 1);
178     if (!dev) {
179         qCritical("QWSTslibMouseHandlerPrivate: ts_open() failed"
180                   " with error: '%s'", strerror(errno));
181         qCritical("Please check your tslib installation!");
182         return false;
183     }
184 
185     if (ts_config(dev)) {
186         qCritical("QWSTslibMouseHandlerPrivate: ts_config() failed"
187                   " with error: '%s'", strerror(errno));
188         qCritical("Please check your tslib installation!");
189         close();
190         return false;
191     }
192 
193     return true;
194 }
195 
close()196 void QWSTslibMouseHandlerPrivate::close()
197 {
198     if (dev)
199         ts_close(dev);
200 }
201 
suspend()202 void QWSTslibMouseHandlerPrivate::suspend()
203 {
204     if (mouseNotifier)
205         mouseNotifier->setEnabled(false);
206 }
207 
resume()208 void QWSTslibMouseHandlerPrivate::resume()
209 {
210     memset(&lastSample, 0, sizeof(lastSample));
211     wasPressed = false;
212     lastdx = 0;
213     lastdy = 0;
214     if (mouseNotifier)
215         mouseNotifier->setEnabled(true);
216 }
217 
get_sample(struct ts_sample * sample)218 bool QWSTslibMouseHandlerPrivate::get_sample(struct ts_sample *sample)
219 {
220     if (!calibrated)
221         return (ts_read_raw(dev, sample, 1) == 1);
222 
223     return (ts_read(dev, sample, 1) == 1);
224 }
225 
readMouseData()226 void QWSTslibMouseHandlerPrivate::readMouseData()
227 {
228     if (!qt_screen)
229         return;
230 
231     for(;;) {
232         struct ts_sample sample = lastSample;
233         bool pressed = wasPressed;
234 
235         // Fast return if there's no events.
236         if (!get_sample(&sample))
237             return;
238         pressed = (sample.pressure > 0);
239 
240         // Only return last sample unless there's a press/release event.
241         while (pressed == wasPressed) {
242             if (!get_sample(&sample))
243                 break;
244             pressed = (sample.pressure > 0);
245         }
246 
247         // work around missing coordinates on mouse release
248         if (!pressed && sample.x == 0 && sample.y == 0) {
249             sample.x = lastSample.x;
250             sample.y = lastSample.y;
251         }
252 
253         int dx = sample.x - lastSample.x;
254         int dy = sample.y - lastSample.y;
255 
256         // Remove small movements in oppsite direction
257         if (dx * lastdx < 0 && qAbs(dx) < jitter_limit) {
258             sample.x = lastSample.x;
259             dx = 0;
260         }
261         if (dy * lastdy < 0 && qAbs(dy) < jitter_limit) {
262             sample.y = lastSample.y;
263             dy = 0;
264         }
265 
266         if (wasPressed == pressed && dx == 0 && dy == 0)
267             return;
268 
269 #ifdef TSLIBMOUSEHANDLER_DEBUG
270         qDebug() << "last" << QPoint(lastSample.x, lastSample.y)
271                  << "curr" << QPoint(sample.x, sample.y)
272                  << "dx,dy" << QPoint(dx, dy)
273                  << "ddx,ddy" << QPoint(dx*lastdx, dy*lastdy)
274                  << "pressed" << wasPressed << pressed;
275 #endif
276 
277         lastSample = sample;
278         wasPressed = pressed;
279         if (dx != 0)
280             lastdx = dx;
281         if (dy != 0)
282             lastdy = dy;
283 
284         const QPoint p(sample.x, sample.y);
285         if (calibrated) {
286             // tslib should do all the translation and filtering, so we send a
287             // "raw" mouse event
288             handler->QWSMouseHandler::mouseChanged(p, pressed);
289         } else {
290             handler->sendFiltered(p, pressed);
291         }
292     }
293 }
294 
clearCalibration()295 void QWSTslibMouseHandlerPrivate::clearCalibration()
296 {
297     suspend();
298     close();
299     handler->QWSCalibratedMouseHandler::clearCalibration();
300     calibrated = false;
301     open();
302     resume();
303 }
304 
calibrate(const QWSPointerCalibrationData * data)305 void QWSTslibMouseHandlerPrivate::calibrate(const QWSPointerCalibrationData *data)
306 {
307     suspend();
308     close();
309     // default implementation writes to /etc/pointercal
310     // using the same format as the tslib linear module.
311     handler->QWSCalibratedMouseHandler::calibrate(data);
312     calibrated = true;
313     open();
314     resume();
315 }
316 
317 /*!
318     \internal
319 */
QWSTslibMouseHandler(const QString & driver,const QString & device)320 QWSTslibMouseHandler::QWSTslibMouseHandler(const QString &driver,
321                                            const QString &device)
322     : QWSCalibratedMouseHandler(driver, device)
323 {
324     d = new QWSTslibMouseHandlerPrivate(this, device);
325 }
326 
327 /*!
328     \internal
329 */
~QWSTslibMouseHandler()330 QWSTslibMouseHandler::~QWSTslibMouseHandler()
331 {
332     delete d;
333 }
334 
335 /*!
336     \reimp
337 */
suspend()338 void QWSTslibMouseHandler::suspend()
339 {
340     d->suspend();
341 }
342 
343 /*!
344     \reimp
345 */
resume()346 void QWSTslibMouseHandler::resume()
347 {
348     d->resume();
349 }
350 
351 /*!
352     \reimp
353 */
clearCalibration()354 void QWSTslibMouseHandler::clearCalibration()
355 {
356     d->clearCalibration();
357 }
358 
359 /*!
360     \reimp
361 */
calibrate(const QWSPointerCalibrationData * data)362 void QWSTslibMouseHandler::calibrate(const QWSPointerCalibrationData *data)
363 {
364     d->calibrate(data);
365 }
366 
367 QT_END_NAMESPACE
368 
369 #include "qmousetslib_qws.moc"
370 
371 #endif //QT_NO_QWS_MOUSE_TSLIB
372