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 "qmouse_qws.h"
43 #include "qwindowsystem_qws.h"
44 #include "qscreen_qws.h"
45 #include "qapplication.h"
46 #include "qtextstream.h"
47 #include "qfile.h"
48 #include "qdebug.h"
49 #include "qscreen_qws.h"
50
51 QT_BEGIN_NAMESPACE
52
53 /*!
54 \class QWSPointerCalibrationData
55 \ingroup qws
56
57 \brief The QWSPointerCalibrationData class is a container for
58 mouse calibration data in Qt for Embedded Linux.
59
60 Note that this class is only available in \l{Qt for Embedded Linux}.
61
62 QWSPointerCalibrationData stores device and screen coordinates in
63 the devPoints and screenPoints variables, respectively.
64
65 A calibration program should create a QWSPointerCalibrationData
66 object, fill the devPoints and screenPoints variables with its
67 device and screen coordinates, and pass the object to the mouse
68 driver using the QWSMouseHandler::calibrate() function.
69
70 \sa QWSCalibratedMouseHandler, {Mouse Calibration Example}
71 */
72
73 /*!
74 \variable QWSPointerCalibrationData::devPoints
75 \brief the raw device coordinates for each value of the Location enum.
76 */
77
78 /*!
79 \variable QWSPointerCalibrationData::screenPoints
80 \brief the logical screen coordinates for each value of the Location enum.
81 */
82
83 /*!
84 \enum QWSPointerCalibrationData::Location
85
86 This enum describes the various logical positions that can be
87 specified by the devPoints and screenPoints variables.
88
89 \value TopLeft Index of the top left corner of the screen.
90 \value BottomLeft Index of the bottom left corner of the screen.
91 \value BottomRight Index of the bottom right corner of the screen.
92 \value TopRight Index of the top right corner of the screen.
93 \value Center Index of the center of the screen.
94 \value LastLocation Last index in the pointer arrays.
95 */
96
97 class QWSMouseHandlerPrivate
98 {
99 public:
QWSMouseHandlerPrivate()100 QWSMouseHandlerPrivate() : screen(qt_screen) {}
101
102 const QScreen *screen;
103 };
104
105 /*!
106 \class QWSMouseHandler
107 \ingroup qws
108
109 \brief The QWSMouseHandler class is a base class for mouse drivers in
110 Qt for Embedded Linux.
111
112 Note that this class is only available in \l{Qt for Embedded Linux}.
113
114 \l{Qt for Embedded Linux} provides ready-made drivers for several mouse
115 protocols, see the \l{Qt for Embedded Linux Pointer Handling}{pointer
116 handling} documentation for details. Custom mouse drivers can be
117 implemented by subclassing the QWSMouseHandler class and creating
118 a mouse driver plugin (derived from QMouseDriverPlugin).
119 The default implementation of the QMouseDriverFactory class
120 will automatically detect the plugin, and load the driver into the
121 server application at run-time using Qt's \l {How to Create Qt
122 Plugins}{plugin system}.
123
124 The mouse driver receives mouse events from the system device and
125 encapsulates each event with an instance of the QWSEvent class
126 which it then passes to the server application (the server is
127 responsible for propagating the event to the appropriate
128 client). To receive mouse events, a QWSMouseHandler object will
129 usually create a QSocketNotifier object for the given device. The
130 QSocketNotifier class provides support for monitoring activity on
131 a file descriptor. When the socket notifier receives data, it will
132 call the mouse driver's mouseChanged() function to send the event
133 to the \l{Qt for Embedded Linux} server application for relaying to
134 clients.
135
136 If you are creating a driver for a device that needs calibration
137 or noise reduction, such as a touchscreen, use the
138 QWSCalibratedMouseHandler subclass instead to take advantage of
139 the calibrate() and clearCalibration() functions. The \l
140 {qws/mousecalibration}{Mouse Calibration}
141 demonstrates how to write a simple program using the mechanisms
142 provided by the QWSMouseHandler class to calibrate a mouse driver.
143
144 Note that when deriving from the QWSMouseHandler class, the
145 resume() and suspend() functions must be reimplemented to control
146 the flow of mouse input, i.e., the default implementation does
147 nothing. Reimplementations of these functions typically call the
148 QSocketNotifier::setEnabled() function to enable or disable the
149 socket notifier, respectively.
150
151 In addition, QWSMouseHandler provides the setScreen() function
152 that allows you to specify a screen for your mouse driver and the
153 limitToScreen() function that ensures that a given position is
154 within this screen's boundaries (changing the position if
155 necessary). Finally, QWSMouseHandler provides the pos() function
156 returning the current mouse position.
157
158 \sa QMouseDriverPlugin, QMouseDriverFactory, {Qt for Embedded Linux Pointer
159 Handling}
160 */
161
162
163 /*!
164 \fn void QWSMouseHandler::suspend()
165
166 Implement this function to suspend reading and handling of mouse
167 events, e.g., call the QSocketNotifier::setEnabled() function to
168 disable the socket notifier.
169
170 \sa resume()
171 */
172
173 /*!
174 \fn void QWSMouseHandler::resume()
175
176 Implement this function to resume reading and handling mouse
177 events, e.g., call the QSocketNotifier::setEnabled() function to
178 enable the socket notifier.
179
180 \sa suspend()
181 */
182
183 /*!
184 \fn const QPoint &QWSMouseHandler::pos() const
185
186 Returns the current mouse position.
187
188 \sa mouseChanged(), limitToScreen()
189 */
190
191 /*!
192 Constructs a mouse driver. The \a driver and \a device arguments
193 are passed by the QWS_MOUSE_PROTO environment variable.
194
195 Call the QWSServer::setMouseHandler() function to make the newly
196 created mouse driver, the primary driver. Note that the primary
197 driver is controlled by the system, i.e., the system will delete
198 it upon exit.
199 */
QWSMouseHandler(const QString &,const QString &)200 QWSMouseHandler::QWSMouseHandler(const QString &, const QString &)
201 : mousePos(QWSServer::mousePosition), d_ptr(new QWSMouseHandlerPrivate)
202 {
203 }
204
205 /*!
206 Destroys this mouse driver.
207
208 Do not call this function if this driver is the primary mouse
209 driver, i.e., if QWSServer::setMouseHandler() function has been
210 called passing this driver as argument. The primary mouse
211 driver is deleted by the system.
212 */
~QWSMouseHandler()213 QWSMouseHandler::~QWSMouseHandler()
214 {
215 delete d_ptr;
216 }
217
218 /*!
219 Ensures that the given \a position is within the screen's
220 boundaries, changing the \a position if necessary.
221
222 \sa pos(), setScreen()
223 */
224
limitToScreen(QPoint & position)225 void QWSMouseHandler::limitToScreen(QPoint &position)
226 {
227 position.setX(qMin(d_ptr->screen->deviceWidth() - 1, qMax(0, position.x())));
228 position.setY(qMin(d_ptr->screen->deviceHeight() - 1, qMax(0, position.y())));
229 }
230
231 /*!
232 \since 4.2
233
234 Sets the screen for this mouse driver to be the given \a screen.
235
236 \sa limitToScreen()
237 */
setScreen(const QScreen * screen)238 void QWSMouseHandler::setScreen(const QScreen *screen)
239 {
240 d_ptr->screen = (screen ? screen : qt_screen);
241 }
242
243 /*!
244 Notifies the system of a new mouse event.
245
246 This function updates the current mouse position and sends the
247 event to the \l{Qt for Embedded Linux} server application for
248 delivery to the correct widget. Note that a custom mouse driver must call
249 this function whenever it wants to deliver a new mouse event.
250
251 The given \a position is the global position of the mouse cursor.
252 The \a state parameter is a bitmask of the Qt::MouseButton enum's
253 values, indicating which mouse buttons are pressed. The \a wheel
254 parameter is the delta value of the mouse wheel as returned by
255 QWheelEvent::delta().
256
257 \sa pos()
258 */
mouseChanged(const QPoint & position,int state,int wheel)259 void QWSMouseHandler::mouseChanged(const QPoint &position, int state, int wheel)
260 {
261 mousePos = position + d_ptr->screen->offset();
262 QWSServer::sendMouseEvent(mousePos, state, wheel);
263 }
264
265 /*!
266 \fn QWSMouseHandler::clearCalibration()
267
268 This virtual function allows subclasses of QWSMouseHandler to
269 clear the calibration information. Note that the default
270 implementation does nothing.
271
272 \sa QWSCalibratedMouseHandler::clearCalibration(), calibrate()
273 */
274
275 /*!
276 \fn QWSMouseHandler::calibrate(const QWSPointerCalibrationData *data)
277
278 This virtual function allows subclasses of QWSMouseHandler to set
279 the calibration information passed in the given \a data. Note that
280 the default implementation does nothing.
281
282 \sa QWSCalibratedMouseHandler::calibrate(), clearCalibration()
283 */
284
285 /*! \fn QWSMouseHandler::getCalibration(QWSPointerCalibrationData *data) const
286 This virtual function allows subclasses of QWSMouseHandler
287 to fill in the device coordinates in \a data with values
288 that correspond to screen coordinates that are already in
289 \a data. Note that the default implementation does nothing.
290 */
291
292 /*!
293 \class QWSCalibratedMouseHandler
294 \ingroup qws
295
296 \brief The QWSCalibratedMouseHandler class provides mouse
297 calibration and noise reduction in Qt for Embedded Linux.
298
299 Note that this class is only available in \l{Qt for Embedded Linux}.
300
301 \l{Qt for Embedded Linux} provides ready-made drivers for several mouse
302 protocols, see the \l{Qt for Embedded Linux Pointer Handling}{pointer
303 handling} documentation for details. In general, custom mouse
304 drivers can be implemented by subclassing the QWSMouseHandler
305 class. But when the system device does not have a fixed mapping
306 between device and screen coordinates and/or produces noisy events
307 (e.g., a touchscreen), you should derive from the
308 QWSCalibratedMouseHandler class instead to take advantage of its
309 calibration functionality. As always, you must also create a mouse
310 driver plugin (derived from QMouseDriverPlugin);
311 the implementation of the QMouseDriverFactory class will then
312 automatically detect the plugin, and load the driver into the
313 server application at run-time using Qt's
314 \l{How to Create Qt Plugins}{plugin system}.
315
316 QWSCalibratedMouseHandler provides an implementation of the
317 calibrate() function to update the calibration parameters based on
318 coordinate mapping of the given calibration data. The calibration
319 data is represented by an QWSPointerCalibrationData object. The
320 linear transformation between device coordinates and screen
321 coordinates is performed by calling the transform() function
322 explicitly on the points passed to the
323 QWSMouseHandler::mouseChanged() function. Use the
324 clearCalibration() function to make the mouse driver return mouse
325 events in raw device coordinates and not in screen coordinates.
326
327 The calibration parameters are recalculated whenever calibrate()
328 is called, and they can be stored using the writeCalibration()
329 function. Previously written parameters can be retrieved at any
330 time using the readCalibration() function (calibration parameters
331 are always read when the class is instantiated). Note that the
332 calibration parameters is written to and read from the file
333 currently specified by the POINTERCAL_FILE environment variable;
334 the default file is \c /etc/pointercal.
335
336 To achieve noise reduction, QWSCalibratedMouseHandler provides the
337 sendFiltered() function. Use this function instead of
338 mouseChanged() whenever a mouse event occurs. The filter's size
339 can be manipulated using the setFilterSize() function.
340
341 \sa QWSMouseHandler, QWSPointerCalibrationData,
342 {Mouse Calibration Example}
343 */
344
345
346 /*!
347 \internal
348 */
349
QWSCalibratedMouseHandler(const QString &,const QString &)350 QWSCalibratedMouseHandler::QWSCalibratedMouseHandler(const QString &, const QString &)
351 : samples(5), currSample(0), numSamples(0)
352 {
353 clearCalibration();
354 readCalibration();
355 }
356
357 /*!
358 Fills \a cd with the device coordinates corresponding to the given
359 screen coordinates.
360
361 \internal
362 */
getCalibration(QWSPointerCalibrationData * cd) const363 void QWSCalibratedMouseHandler::getCalibration(QWSPointerCalibrationData *cd) const
364 {
365 const qint64 scale = qint64(a) * qint64(e) - qint64(b) * qint64(d);
366 const qint64 xOff = qint64(b) * qint64(f) - qint64(c) * qint64(e);
367 const qint64 yOff = qint64(c) * qint64(d) - qint64(a) * qint64(f);
368 for (int i = 0; i <= QWSPointerCalibrationData::LastLocation; ++i) {
369 const qint64 sX = cd->screenPoints[i].x();
370 const qint64 sY = cd->screenPoints[i].y();
371 const qint64 dX = (s*(e*sX - b*sY) + xOff) / scale;
372 const qint64 dY = (s*(a*sY - d*sX) + yOff) / scale;
373 cd->devPoints[i] = QPoint(dX, dY);
374 }
375 }
376
377 /*!
378 Clears the current calibration, i.e., makes the mouse
379 driver return mouse events in raw device coordinates instead of
380 screen coordinates.
381
382 \sa calibrate()
383 */
clearCalibration()384 void QWSCalibratedMouseHandler::clearCalibration()
385 {
386 a = 1;
387 b = 0;
388 c = 0;
389 d = 0;
390 e = 1;
391 f = 0;
392 s = 1;
393 }
394
395
396 /*!
397 Saves the current calibration parameters in \c /etc/pointercal
398 (separated by whitespace and in alphabetical order).
399
400 You can override the default \c /etc/pointercal by specifying
401 another file using the POINTERCAL_FILE environment variable.
402
403 \sa readCalibration()
404 */
writeCalibration()405 void QWSCalibratedMouseHandler::writeCalibration()
406 {
407 QString calFile;
408 calFile = QString::fromLocal8Bit(qgetenv("POINTERCAL_FILE"));
409 if (calFile.isEmpty())
410 calFile = QLatin1String("/etc/pointercal");
411
412 #ifndef QT_NO_TEXTSTREAM
413 QFile file(calFile);
414 if (file.open(QIODevice::WriteOnly)) {
415 QTextStream t(&file);
416 t << a << ' ' << b << ' ' << c << ' ';
417 t << d << ' ' << e << ' ' << f << ' ' << s << endl;
418 } else
419 #endif
420 {
421 qCritical("QWSCalibratedMouseHandler::writeCalibration: "
422 "Could not save calibration into %s", qPrintable(calFile));
423 }
424 }
425
426 /*!
427 Reads previously written calibration parameters which are stored
428 in \c /etc/pointercal (separated by whitespace and in alphabetical
429 order).
430
431 You can override the default \c /etc/pointercal by specifying
432 another file using the POINTERCAL_FILE environment variable.
433
434
435 \sa writeCalibration()
436 */
readCalibration()437 void QWSCalibratedMouseHandler::readCalibration()
438 {
439 QString calFile = QString::fromLocal8Bit(qgetenv("POINTERCAL_FILE"));
440 if (calFile.isEmpty())
441 calFile = QLatin1String("/etc/pointercal");
442
443 #ifndef QT_NO_TEXTSTREAM
444 QFile file(calFile);
445 if (file.open(QIODevice::ReadOnly)) {
446 QTextStream t(&file);
447 t >> a >> b >> c >> d >> e >> f >> s;
448 if (s == 0 || t.status() != QTextStream::Ok) {
449 qCritical("Corrupt calibration data");
450 clearCalibration();
451 }
452 } else
453 #endif
454 {
455 qDebug() << "Could not read calibration:" <<calFile;
456 }
457 }
458
ilog2(quint32 n)459 static int ilog2(quint32 n)
460 {
461 int result = 0;
462
463 if (n & 0xffff0000) {
464 n >>= 16;
465 result += 16;
466 }
467 if (n & 0xff00) {
468 n >>= 8;
469 result += 8;}
470 if (n & 0xf0) {
471 n >>= 4;
472 result += 4;
473 }
474 if (n & 0xc) {
475 n >>= 2;
476 result += 2;
477 }
478 if (n & 0x2)
479 result += 1;
480
481 return result;
482 }
483
484 /*!
485 Updates the calibration parameters based on coordinate mapping of
486 the given \a data.
487
488 Create an instance of the QWSPointerCalibrationData class, fill in
489 the device and screen coordinates and pass that object to the mouse
490 driver using this function.
491
492 \sa clearCalibration(), transform()
493 */
calibrate(const QWSPointerCalibrationData * data)494 void QWSCalibratedMouseHandler::calibrate(const QWSPointerCalibrationData *data)
495 {
496 // Algorithm derived from
497 // "How To Calibrate Touch Screens" by Carlos E. Vidales,
498 // printed in Embedded Systems Programming, Vol. 15 no 6, June 2002
499 // URL: http://www.embedded.com/showArticle.jhtml?articleID=9900629
500
501 const QPoint pd0 = data->devPoints[QWSPointerCalibrationData::TopLeft];
502 const QPoint pd1 = data->devPoints[QWSPointerCalibrationData::TopRight];
503 const QPoint pd2 = data->devPoints[QWSPointerCalibrationData::BottomRight];
504 const QPoint p0 = data->screenPoints[QWSPointerCalibrationData::TopLeft];
505 const QPoint p1 = data->screenPoints[QWSPointerCalibrationData::TopRight];
506 const QPoint p2 = data->screenPoints[QWSPointerCalibrationData::BottomRight];
507
508 const qint64 xd0 = pd0.x();
509 const qint64 xd1 = pd1.x();
510 const qint64 xd2 = pd2.x();
511 const qint64 yd0 = pd0.y();
512 const qint64 yd1 = pd1.y();
513 const qint64 yd2 = pd2.y();
514 const qint64 x0 = p0.x();
515 const qint64 x1 = p1.x();
516 const qint64 x2 = p2.x();
517 const qint64 y0 = p0.y();
518 const qint64 y1 = p1.y();
519 const qint64 y2 = p2.y();
520
521 qint64 scale = ((xd0 - xd2)*(yd1 - yd2) - (xd1 - xd2)*(yd0 - yd2));
522 int shift = 0;
523 qint64 absScale = qAbs(scale);
524 // use maximum 16 bit precision to reduce risk of integer overflow
525 if (absScale > (1 << 16)) {
526 shift = ilog2(absScale >> 16) + 1;
527 scale >>= shift;
528 }
529
530 s = scale;
531 a = ((x0 - x2)*(yd1 - yd2) - (x1 - x2)*(yd0 - yd2)) >> shift;
532 b = ((xd0 - xd2)*(x1 - x2) - (x0 - x2)*(xd1 - xd2)) >> shift;
533 c = (yd0*(xd2*x1 - xd1*x2) + yd1*(xd0*x2 - xd2*x0) + yd2*(xd1*x0 - xd0*x1)) >> shift;
534 d = ((y0 - y2)*(yd1 - yd2) - (y1 - y2)*(yd0 - yd2)) >> shift;
535 e = ((xd0 - xd2)*(y1 - y2) - (y0 - y2)*(xd1 - xd2)) >> shift;
536 f = (yd0*(xd2*y1 - xd1*y2) + yd1*(xd0*y2 - xd2*y0) + yd2*(xd1*y0 - xd0*y1)) >> shift;
537
538 writeCalibration();
539 }
540
541 /*!
542 Transforms the given \a position from device coordinates to screen
543 coordinates, and returns the transformed position.
544
545 This function is typically called explicitly on the points passed
546 to the QWSMouseHandler::mouseChanged() function.
547
548 This implementation is a linear transformation using 7 parameters
549 (\c a, \c b, \c c, \c d, \c e, \c f and \c s) to transform the
550 device coordinates (\c Xd, \c Yd) into screen coordinates (\c Xs,
551 \c Ys) using the following equations:
552
553 \snippet doc/src/snippets/code/src_gui_embedded_qmouse_qws.cpp 0
554
555 \sa mouseChanged()
556 */
transform(const QPoint & position)557 QPoint QWSCalibratedMouseHandler::transform(const QPoint &position)
558 {
559 QPoint tp;
560
561 tp.setX((a * position.x() + b * position.y() + c) / s);
562 tp.setY((d * position.x() + e * position.y() + f) / s);
563
564 return tp;
565 }
566
567 /*!
568 Sets the size of the filter used in noise reduction to the given
569 \a size.
570
571 The sendFiltered() function reduces noice by calculating an
572 average position from a collection of mouse event positions. The
573 filter size determines the number of positions that forms the
574 basis for these calculations.
575
576 \sa sendFiltered()
577 */
setFilterSize(int size)578 void QWSCalibratedMouseHandler::setFilterSize(int size)
579 {
580 samples.resize(qMax(1, size));
581 numSamples = 0;
582 currSample = 0;
583 }
584
585 /*!
586 \fn bool QWSCalibratedMouseHandler::sendFiltered(const QPoint &position, int state)
587
588 Notifies the system of a new mouse event \e after applying a noise
589 reduction filter. Returns true if the filtering process is
590 successful; otherwise returns false. Note that if the filtering
591 process failes, the system is not notified about the event.
592
593 The given \a position is the global position of the mouse. The \a
594 state parameter is a bitmask of the Qt::MouseButton enum's values
595 indicating which mouse buttons are pressed.
596
597 The noice is reduced by calculating an average position from a
598 collection of mouse event positions and then calling the
599 mouseChanged() function with the new position. The number of
600 positions that is used is determined by the filter size.
601
602 \sa mouseChanged(), setFilterSize()
603 */
sendFiltered(const QPoint & position,int button)604 bool QWSCalibratedMouseHandler::sendFiltered(const QPoint &position, int button)
605 {
606 if (!button) {
607 if (numSamples >= samples.count())
608 mouseChanged(transform(position), 0);
609 currSample = 0;
610 numSamples = 0;
611 return true;
612 }
613
614 bool sent = false;
615 samples[currSample] = position;
616 numSamples++;
617 if (numSamples >= samples.count()) {
618
619 int ignore = -1;
620 if (samples.count() > 2) { // throw away the "worst" sample
621 int maxd = 0;
622 for (int i = 0; i < samples.count(); i++) {
623 int d = (mousePos - samples[i]).manhattanLength();
624 if (d > maxd) {
625 maxd = d;
626 ignore = i;
627 }
628 }
629 }
630
631 // average the rest
632 QPoint pos(0, 0);
633 int numAveraged = 0;
634 for (int i = 0; i < samples.count(); i++) {
635 if (ignore == i)
636 continue;
637 pos += samples[i];
638 ++numAveraged;
639 }
640 if (numAveraged)
641 pos /= numAveraged;
642
643 mouseChanged(transform(pos), button);
644 sent = true;
645 }
646 currSample++;
647 if (currSample >= samples.count())
648 currSample = 0;
649
650 return sent;
651 }
652
653 QT_END_NAMESPACE
654