summaryrefslogtreecommitdiffstats
path: root/src/gui/embedded/qmouse_qws.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/embedded/qmouse_qws.cpp')
-rw-r--r--src/gui/embedded/qmouse_qws.cpp653
1 files changed, 653 insertions, 0 deletions
diff --git a/src/gui/embedded/qmouse_qws.cpp b/src/gui/embedded/qmouse_qws.cpp
new file mode 100644
index 0000000000..c373b916ca
--- /dev/null
+++ b/src/gui/embedded/qmouse_qws.cpp
@@ -0,0 +1,653 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmouse_qws.h"
+#include "qwindowsystem_qws.h"
+#include "qscreen_qws.h"
+#include "qapplication.h"
+#include "qtextstream.h"
+#include "qfile.h"
+#include "qdebug.h"
+#include "qscreen_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QWSPointerCalibrationData
+ \ingroup qws
+
+ \brief The QWSPointerCalibrationData class is a container for
+ mouse calibration data in Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ QWSPointerCalibrationData stores device and screen coordinates in
+ the devPoints and screenPoints variables, respectively.
+
+ A calibration program should create a QWSPointerCalibrationData
+ object, fill the devPoints and screenPoints variables with its
+ device and screen coordinates, and pass the object to the mouse
+ driver using the QWSMouseHandler::calibrate() function.
+
+ \sa QWSCalibratedMouseHandler, {Mouse Calibration Example}
+*/
+
+/*!
+ \variable QWSPointerCalibrationData::devPoints
+ \brief the raw device coordinates for each value of the Location enum.
+*/
+
+/*!
+ \variable QWSPointerCalibrationData::screenPoints
+ \brief the logical screen coordinates for each value of the Location enum.
+*/
+
+/*!
+ \enum QWSPointerCalibrationData::Location
+
+ This enum describes the various logical positions that can be
+ specified by the devPoints and screenPoints variables.
+
+ \value TopLeft Index of the top left corner of the screen.
+ \value BottomLeft Index of the bottom left corner of the screen.
+ \value BottomRight Index of the bottom right corner of the screen.
+ \value TopRight Index of the top right corner of the screen.
+ \value Center Index of the center of the screen.
+ \value LastLocation Last index in the pointer arrays.
+*/
+
+class QWSMouseHandlerPrivate
+{
+public:
+ QWSMouseHandlerPrivate() : screen(qt_screen) {}
+
+ const QScreen *screen;
+};
+
+/*!
+ \class QWSMouseHandler
+ \ingroup qws
+
+ \brief The QWSMouseHandler class is a base class for mouse drivers in
+ Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ \l{Qt for Embedded Linux} provides ready-made drivers for several mouse
+ protocols, see the \l{Qt for Embedded Linux Pointer Handling}{pointer
+ handling} documentation for details. Custom mouse drivers can be
+ implemented by subclassing the QWSMouseHandler class and creating
+ a mouse driver plugin (derived from QMouseDriverPlugin).
+ The default implementation of the QMouseDriverFactory class
+ will automatically detect the plugin, and load the driver into the
+ server application at run-time using Qt's \l {How to Create Qt
+ Plugins}{plugin system}.
+
+ The mouse driver receives mouse events from the system device and
+ encapsulates each event with an instance of the QWSEvent class
+ which it then passes to the server application (the server is
+ responsible for propagating the event to the appropriate
+ client). To receive mouse events, a QWSMouseHandler object will
+ usually create a QSocketNotifier object for the given device. The
+ QSocketNotifier class provides support for monitoring activity on
+ a file descriptor. When the socket notifier receives data, it will
+ call the mouse driver's mouseChanged() function to send the event
+ to the \l{Qt for Embedded Linux} server application for relaying to
+ clients.
+
+ If you are creating a driver for a device that needs calibration
+ or noise reduction, such as a touchscreen, use the
+ QWSCalibratedMouseHandler subclass instead to take advantage of
+ the calibrate() and clearCalibration() functions. The \l
+ {qws/mousecalibration}{Mouse Calibration}
+ demonstrates how to write a simple program using the mechanisms
+ provided by the QWSMouseHandler class to calibrate a mouse driver.
+
+ Note that when deriving from the QWSMouseHandler class, the
+ resume() and suspend() functions must be reimplemented to control
+ the flow of mouse input, i.e., the default implementation does
+ nothing. Reimplementations of these functions typically call the
+ QSocketNotifier::setEnabled() function to enable or disable the
+ socket notifier, respectively.
+
+ In addition, QWSMouseHandler provides the setScreen() function
+ that allows you to specify a screen for your mouse driver and the
+ limitToScreen() function that ensures that a given position is
+ within this screen's boundaries (changing the position if
+ necessary). Finally, QWSMouseHandler provides the pos() function
+ returning the current mouse position.
+
+ \sa QMouseDriverPlugin, QMouseDriverFactory, {Qt for Embedded Linux Pointer
+ Handling}
+*/
+
+
+/*!
+ \fn void QWSMouseHandler::suspend()
+
+ Implement this function to suspend reading and handling of mouse
+ events, e.g., call the QSocketNotifier::setEnabled() function to
+ disable the socket notifier.
+
+ \sa resume()
+*/
+
+/*!
+ \fn void QWSMouseHandler::resume()
+
+ Implement this function to resume reading and handling mouse
+ events, e.g., call the QSocketNotifier::setEnabled() function to
+ enable the socket notifier.
+
+ \sa suspend()
+*/
+
+/*!
+ \fn const QPoint &QWSMouseHandler::pos() const
+
+ Returns the current mouse position.
+
+ \sa mouseChanged(), limitToScreen()
+*/
+
+/*!
+ Constructs a mouse driver. The \a driver and \a device arguments
+ are passed by the QWS_MOUSE_PROTO environment variable.
+
+ Call the QWSServer::setMouseHandler() function to make the newly
+ created mouse driver, the primary driver. Note that the primary
+ driver is controlled by the system, i.e., the system will delete
+ it upon exit.
+*/
+QWSMouseHandler::QWSMouseHandler(const QString &, const QString &)
+ : mousePos(QWSServer::mousePosition), d_ptr(new QWSMouseHandlerPrivate)
+{
+}
+
+/*!
+ Destroys this mouse driver.
+
+ Do not call this function if this driver is the primary mouse
+ driver, i.e., if QWSServer::setMouseHandler() function has been
+ called passing this driver as argument. The primary mouse
+ driver is deleted by the system.
+*/
+QWSMouseHandler::~QWSMouseHandler()
+{
+ delete d_ptr;
+}
+
+/*!
+ Ensures that the given \a position is within the screen's
+ boundaries, changing the \a position if necessary.
+
+ \sa pos(), setScreen()
+*/
+
+void QWSMouseHandler::limitToScreen(QPoint &position)
+{
+ position.setX(qMin(d_ptr->screen->deviceWidth() - 1, qMax(0, position.x())));
+ position.setY(qMin(d_ptr->screen->deviceHeight() - 1, qMax(0, position.y())));
+}
+
+/*!
+ \since 4.2
+
+ Sets the screen for this mouse driver to be the given \a screen.
+
+ \sa limitToScreen()
+*/
+void QWSMouseHandler::setScreen(const QScreen *screen)
+{
+ d_ptr->screen = (screen ? screen : qt_screen);
+}
+
+/*!
+ Notifies the system of a new mouse event.
+
+ This function updates the current mouse position and sends the
+ event to the \l{Qt for Embedded Linux} server application for
+ delivery to the correct widget. Note that a custom mouse driver must call
+ this function whenever it wants to deliver a new mouse event.
+
+ The given \a position is the global position of the mouse cursor.
+ The \a state parameter is a bitmask of the Qt::MouseButton enum's
+ values, indicating which mouse buttons are pressed. The \a wheel
+ parameter is the delta value of the mouse wheel as returned by
+ QWheelEvent::delta().
+
+ \sa pos()
+*/
+void QWSMouseHandler::mouseChanged(const QPoint &position, int state, int wheel)
+{
+ mousePos = position + d_ptr->screen->offset();
+ QWSServer::sendMouseEvent(mousePos, state, wheel);
+}
+
+/*!
+ \fn QWSMouseHandler::clearCalibration()
+
+ This virtual function allows subclasses of QWSMouseHandler to
+ clear the calibration information. Note that the default
+ implementation does nothing.
+
+ \sa QWSCalibratedMouseHandler::clearCalibration(), calibrate()
+*/
+
+/*!
+ \fn QWSMouseHandler::calibrate(const QWSPointerCalibrationData *data)
+
+ This virtual function allows subclasses of QWSMouseHandler to set
+ the calibration information passed in the given \a data. Note that
+ the default implementation does nothing.
+
+ \sa QWSCalibratedMouseHandler::calibrate(), clearCalibration()
+*/
+
+/*! \fn QWSMouseHandler::getCalibration(QWSPointerCalibrationData *data) const
+ This virtual function allows subclasses of QWSMouseHandler
+ to fill in the device coordinates in \a data with values
+ that correspond to screen coordinates that are already in
+ \a data. Note that the default implementation does nothing.
+ */
+
+/*!
+ \class QWSCalibratedMouseHandler
+ \ingroup qws
+
+ \brief The QWSCalibratedMouseHandler class provides mouse
+ calibration and noise reduction in Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ \l{Qt for Embedded Linux} provides ready-made drivers for several mouse
+ protocols, see the \l{Qt for Embedded Linux Pointer Handling}{pointer
+ handling} documentation for details. In general, custom mouse
+ drivers can be implemented by subclassing the QWSMouseHandler
+ class. But when the system device does not have a fixed mapping
+ between device and screen coordinates and/or produces noisy events
+ (e.g., a touchscreen), you should derive from the
+ QWSCalibratedMouseHandler class instead to take advantage of its
+ calibration functionality. As always, you must also create a mouse
+ driver plugin (derived from QMouseDriverPlugin);
+ the implementation of the QMouseDriverFactory class will then
+ automatically detect the plugin, and load the driver into the
+ server application at run-time using Qt's
+ \l{How to Create Qt Plugins}{plugin system}.
+
+ QWSCalibratedMouseHandler provides an implementation of the
+ calibrate() function to update the calibration parameters based on
+ coordinate mapping of the given calibration data. The calibration
+ data is represented by an QWSPointerCalibrationData object. The
+ linear transformation between device coordinates and screen
+ coordinates is performed by calling the transform() function
+ explicitly on the points passed to the
+ QWSMouseHandler::mouseChanged() function. Use the
+ clearCalibration() function to make the mouse driver return mouse
+ events in raw device coordinates and not in screen coordinates.
+
+ The calibration parameters are recalculated whenever calibrate()
+ is called, and they can be stored using the writeCalibration()
+ function. Previously written parameters can be retrieved at any
+ time using the readCalibration() function (calibration parameters
+ are always read when the class is instantiated). Note that the
+ calibration parameters is written to and read from the file
+ currently specified by the POINTERCAL_FILE environment variable;
+ the default file is \c /etc/pointercal.
+
+ To achieve noise reduction, QWSCalibratedMouseHandler provides the
+ sendFiltered() function. Use this function instead of
+ mouseChanged() whenever a mouse event occurs. The filter's size
+ can be manipulated using the setFilterSize() function.
+
+ \sa QWSMouseHandler, QWSPointerCalibrationData,
+ {Mouse Calibration Example}
+*/
+
+
+/*!
+ \internal
+ */
+
+QWSCalibratedMouseHandler::QWSCalibratedMouseHandler(const QString &, const QString &)
+ : samples(5), currSample(0), numSamples(0)
+{
+ clearCalibration();
+ readCalibration();
+}
+
+/*!
+ Fills \a cd with the device coordinates corresponding to the given
+ screen coordinates.
+
+ \internal
+*/
+void QWSCalibratedMouseHandler::getCalibration(QWSPointerCalibrationData *cd) const
+{
+ const qint64 scale = qint64(a) * qint64(e) - qint64(b) * qint64(d);
+ const qint64 xOff = qint64(b) * qint64(f) - qint64(c) * qint64(e);
+ const qint64 yOff = qint64(c) * qint64(d) - qint64(a) * qint64(f);
+ for (int i = 0; i <= QWSPointerCalibrationData::LastLocation; ++i) {
+ const qint64 sX = cd->screenPoints[i].x();
+ const qint64 sY = cd->screenPoints[i].y();
+ const qint64 dX = (s*(e*sX - b*sY) + xOff) / scale;
+ const qint64 dY = (s*(a*sY - d*sX) + yOff) / scale;
+ cd->devPoints[i] = QPoint(dX, dY);
+ }
+}
+
+/*!
+ Clears the current calibration, i.e., makes the mouse
+ driver return mouse events in raw device coordinates instead of
+ screen coordinates.
+
+ \sa calibrate()
+*/
+void QWSCalibratedMouseHandler::clearCalibration()
+{
+ a = 1;
+ b = 0;
+ c = 0;
+ d = 0;
+ e = 1;
+ f = 0;
+ s = 1;
+}
+
+
+/*!
+ Saves the current calibration parameters in \c /etc/pointercal
+ (separated by whitespace and in alphabetical order).
+
+ You can override the default \c /etc/pointercal by specifying
+ another file using the POINTERCAL_FILE environment variable.
+
+ \sa readCalibration()
+*/
+void QWSCalibratedMouseHandler::writeCalibration()
+{
+ QString calFile;
+ calFile = QString::fromLocal8Bit(qgetenv("POINTERCAL_FILE"));
+ if (calFile.isEmpty())
+ calFile = QLatin1String("/etc/pointercal");
+
+#ifndef QT_NO_TEXTSTREAM
+ QFile file(calFile);
+ if (file.open(QIODevice::WriteOnly)) {
+ QTextStream t(&file);
+ t << a << ' ' << b << ' ' << c << ' ';
+ t << d << ' ' << e << ' ' << f << ' ' << s << endl;
+ } else
+#endif
+ {
+ qCritical("QWSCalibratedMouseHandler::writeCalibration: "
+ "Could not save calibration into %s", qPrintable(calFile));
+ }
+}
+
+/*!
+ Reads previously written calibration parameters which are stored
+ in \c /etc/pointercal (separated by whitespace and in alphabetical
+ order).
+
+ You can override the default \c /etc/pointercal by specifying
+ another file using the POINTERCAL_FILE environment variable.
+
+
+ \sa writeCalibration()
+*/
+void QWSCalibratedMouseHandler::readCalibration()
+{
+ QString calFile = QString::fromLocal8Bit(qgetenv("POINTERCAL_FILE"));
+ if (calFile.isEmpty())
+ calFile = QLatin1String("/etc/pointercal");
+
+#ifndef QT_NO_TEXTSTREAM
+ QFile file(calFile);
+ if (file.open(QIODevice::ReadOnly)) {
+ QTextStream t(&file);
+ t >> a >> b >> c >> d >> e >> f >> s;
+ if (s == 0 || t.status() != QTextStream::Ok) {
+ qCritical("Corrupt calibration data");
+ clearCalibration();
+ }
+ } else
+#endif
+ {
+ qDebug() << "Could not read calibration:" <<calFile;
+ }
+}
+
+static int ilog2(quint32 n)
+{
+ int result = 0;
+
+ if (n & 0xffff0000) {
+ n >>= 16;
+ result += 16;
+ }
+ if (n & 0xff00) {
+ n >>= 8;
+ result += 8;}
+ if (n & 0xf0) {
+ n >>= 4;
+ result += 4;
+ }
+ if (n & 0xc) {
+ n >>= 2;
+ result += 2;
+ }
+ if (n & 0x2)
+ result += 1;
+
+ return result;
+}
+
+/*!
+ Updates the calibration parameters based on coordinate mapping of
+ the given \a data.
+
+ Create an instance of the QWSPointerCalibrationData class, fill in
+ the device and screen coordinates and pass that object to the mouse
+ driver using this function.
+
+ \sa clearCalibration(), transform()
+*/
+void QWSCalibratedMouseHandler::calibrate(const QWSPointerCalibrationData *data)
+{
+ // Algorithm derived from
+ // "How To Calibrate Touch Screens" by Carlos E. Vidales,
+ // printed in Embedded Systems Programming, Vol. 15 no 6, June 2002
+ // URL: http://www.embedded.com/showArticle.jhtml?articleID=9900629
+
+ const QPoint pd0 = data->devPoints[QWSPointerCalibrationData::TopLeft];
+ const QPoint pd1 = data->devPoints[QWSPointerCalibrationData::TopRight];
+ const QPoint pd2 = data->devPoints[QWSPointerCalibrationData::BottomRight];
+ const QPoint p0 = data->screenPoints[QWSPointerCalibrationData::TopLeft];
+ const QPoint p1 = data->screenPoints[QWSPointerCalibrationData::TopRight];
+ const QPoint p2 = data->screenPoints[QWSPointerCalibrationData::BottomRight];
+
+ const qint64 xd0 = pd0.x();
+ const qint64 xd1 = pd1.x();
+ const qint64 xd2 = pd2.x();
+ const qint64 yd0 = pd0.y();
+ const qint64 yd1 = pd1.y();
+ const qint64 yd2 = pd2.y();
+ const qint64 x0 = p0.x();
+ const qint64 x1 = p1.x();
+ const qint64 x2 = p2.x();
+ const qint64 y0 = p0.y();
+ const qint64 y1 = p1.y();
+ const qint64 y2 = p2.y();
+
+ qint64 scale = ((xd0 - xd2)*(yd1 - yd2) - (xd1 - xd2)*(yd0 - yd2));
+ int shift = 0;
+ qint64 absScale = qAbs(scale);
+ // use maximum 16 bit precision to reduce risk of integer overflow
+ if (absScale > (1 << 16)) {
+ shift = ilog2(absScale >> 16) + 1;
+ scale >>= shift;
+ }
+
+ s = scale;
+ a = ((x0 - x2)*(yd1 - yd2) - (x1 - x2)*(yd0 - yd2)) >> shift;
+ b = ((xd0 - xd2)*(x1 - x2) - (x0 - x2)*(xd1 - xd2)) >> shift;
+ c = (yd0*(xd2*x1 - xd1*x2) + yd1*(xd0*x2 - xd2*x0) + yd2*(xd1*x0 - xd0*x1)) >> shift;
+ d = ((y0 - y2)*(yd1 - yd2) - (y1 - y2)*(yd0 - yd2)) >> shift;
+ e = ((xd0 - xd2)*(y1 - y2) - (y0 - y2)*(xd1 - xd2)) >> shift;
+ f = (yd0*(xd2*y1 - xd1*y2) + yd1*(xd0*y2 - xd2*y0) + yd2*(xd1*y0 - xd0*y1)) >> shift;
+
+ writeCalibration();
+}
+
+/*!
+ Transforms the given \a position from device coordinates to screen
+ coordinates, and returns the transformed position.
+
+ This function is typically called explicitly on the points passed
+ to the QWSMouseHandler::mouseChanged() function.
+
+ This implementation is a linear transformation using 7 parameters
+ (\c a, \c b, \c c, \c d, \c e, \c f and \c s) to transform the
+ device coordinates (\c Xd, \c Yd) into screen coordinates (\c Xs,
+ \c Ys) using the following equations:
+
+ \snippet doc/src/snippets/code/src_gui_embedded_qmouse_qws.cpp 0
+
+ \sa mouseChanged()
+*/
+QPoint QWSCalibratedMouseHandler::transform(const QPoint &position)
+{
+ QPoint tp;
+
+ tp.setX((a * position.x() + b * position.y() + c) / s);
+ tp.setY((d * position.x() + e * position.y() + f) / s);
+
+ return tp;
+}
+
+/*!
+ Sets the size of the filter used in noise reduction to the given
+ \a size.
+
+ The sendFiltered() function reduces noice by calculating an
+ average position from a collection of mouse event positions. The
+ filter size determines the number of positions that forms the
+ basis for these calculations.
+
+ \sa sendFiltered()
+*/
+void QWSCalibratedMouseHandler::setFilterSize(int size)
+{
+ samples.resize(qMax(1, size));
+ numSamples = 0;
+ currSample = 0;
+}
+
+/*!
+ \fn bool QWSCalibratedMouseHandler::sendFiltered(const QPoint &position, int state)
+
+ Notifies the system of a new mouse event \e after applying a noise
+ reduction filter. Returns true if the filtering process is
+ successful; otherwise returns false. Note that if the filtering
+ process failes, the system is not notified about the event.
+
+ The given \a position is the global position of the mouse. The \a
+ state parameter is a bitmask of the Qt::MouseButton enum's values
+ indicating which mouse buttons are pressed.
+
+ The noice is reduced by calculating an average position from a
+ collection of mouse event positions and then calling the
+ mouseChanged() function with the new position. The number of
+ positions that is used is determined by the filter size.
+
+ \sa mouseChanged(), setFilterSize()
+*/
+bool QWSCalibratedMouseHandler::sendFiltered(const QPoint &position, int button)
+{
+ if (!button) {
+ if (numSamples >= samples.count())
+ mouseChanged(transform(position), 0);
+ currSample = 0;
+ numSamples = 0;
+ return true;
+ }
+
+ bool sent = false;
+ samples[currSample] = position;
+ numSamples++;
+ if (numSamples >= samples.count()) {
+
+ int ignore = -1;
+ if (samples.count() > 2) { // throw away the "worst" sample
+ int maxd = 0;
+ for (int i = 0; i < samples.count(); i++) {
+ int d = (mousePos - samples[i]).manhattanLength();
+ if (d > maxd) {
+ maxd = d;
+ ignore = i;
+ }
+ }
+ }
+
+ // average the rest
+ QPoint pos(0, 0);
+ int numAveraged = 0;
+ for (int i = 0; i < samples.count(); i++) {
+ if (ignore == i)
+ continue;
+ pos += samples[i];
+ ++numAveraged;
+ }
+ if (numAveraged)
+ pos /= numAveraged;
+
+ mouseChanged(transform(pos), button);
+ sent = true;
+ }
+ currSample++;
+ if (currSample >= samples.count())
+ currSample = 0;
+
+ return sent;
+}
+
+QT_END_NAMESPACE