1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qevdevmousehandler_p.h"
44 #include <QSocketNotifier>
45 #include <QStringList>
47 #include <QGuiApplication>
49 #include <QtGui/qwindowsysteminterface.h>
51 #include <qplatformdefs.h>
52 #include <private/qcore_unix_p.h> // overrides QT_OPEN
57 #include <linux/input.h>
61 //#define QT_QPA_MOUSE_HANDLER_DEBUG
65 QEvdevMouseHandler *QEvdevMouseHandler::create(const QString &device, const QString &specification)
67 #ifdef QT_QPA_MOUSE_HANDLER_DEBUG
68 qWarning() << "Try to create mouse handler for" << device << specification;
71 bool compression = true;
74 QStringList args = specification.split(QLatin1Char(':'));
75 foreach (const QString &arg, args) {
76 if (arg == QLatin1String("nocompress"))
78 else if (arg.startsWith(QLatin1String("dejitter=")))
79 jitterLimit = arg.mid(9).toInt();
83 fd = qt_safe_open(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0);
85 return new QEvdevMouseHandler(device, fd, compression, jitterLimit);
87 qWarning("Cannot open mouse input device '%s': %s", qPrintable(device), strerror(errno));
92 QEvdevMouseHandler::QEvdevMouseHandler(const QString &device, int fd, bool compression, int jitterLimit)
93 : m_device(device), m_fd(fd), m_notify(0), m_x(0), m_y(0), m_prevx(0), m_prevy(0),
94 m_compression(compression), m_buttons(0), m_prevInvalid(true)
96 setObjectName(QLatin1String("Evdev Mouse Handler"));
98 m_jitterLimitSquared = jitterLimit * jitterLimit;
100 // socket notifier for events on the mouse device
101 QSocketNotifier *notifier;
102 notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
103 connect(notifier, SIGNAL(activated(int)), this, SLOT(readMouseData()));
106 QEvdevMouseHandler::~QEvdevMouseHandler()
112 void QEvdevMouseHandler::sendMouseEvent()
114 int x = m_x - m_prevx;
115 int y = m_y - m_prevy;
118 m_prevInvalid = false;
121 emit handleMouseEvent(x, y, m_buttons);
127 void QEvdevMouseHandler::readMouseData()
129 struct ::input_event buffer[32];
131 bool posChanged = false, btnChanged = false;
132 bool pendingMouseEvent = false;
133 int eventCompressCount = 0;
135 int result = QT_READ(m_fd, reinterpret_cast<char *>(buffer) + n, sizeof(buffer) - n);
138 qWarning("Got EOF from the input device.");
140 } else if (result < 0) {
141 if (errno != EINTR && errno != EAGAIN) {
142 qWarning("Could not read from input device: %s", strerror(errno));
147 if (n % sizeof(buffer[0]) == 0)
152 n /= sizeof(buffer[0]);
154 for (int i = 0; i < n; ++i) {
155 struct ::input_event *data = &buffer[i];
156 //qDebug() << ">>" << hex << data->type << data->code << dec << data->value;
157 if (data->type == EV_ABS) {
158 // Touchpads: store the absolute position for now, will calculate a relative one later.
159 if (data->code == ABS_X && m_x != data->value) {
162 } else if (data->code == ABS_Y && m_y != data->value) {
166 } else if (data->type == EV_REL) {
167 if (data->code == REL_X) {
170 } else if (data->code == REL_Y) {
173 } else if (data->code == ABS_WHEEL) { // vertical scroll
174 // data->value: 1 == up, -1 == down
175 const int delta = 120 * data->value;
176 emit handleWheelEvent(delta, Qt::Vertical);
177 } else if (data->code == ABS_THROTTLE) { // horizontal scroll
178 // data->value: 1 == right, -1 == left
179 const int delta = 120 * -data->value;
180 emit handleWheelEvent(delta, Qt::Horizontal);
182 } else if (data->type == EV_KEY && data->code == BTN_TOUCH) {
183 // We care about touchpads only, not touchscreens -> don't map to button press.
184 // Need to invalidate prevx/y however to get proper relative pos.
185 m_prevInvalid = true;
186 } else if (data->type == EV_KEY && data->code >= BTN_LEFT && data->code <= BTN_JOYSTICK) {
187 Qt::MouseButton button = Qt::NoButton;
188 // BTN_LEFT == 0x110 in kernel's input.h
189 // The range of possible mouse buttons ends just before BTN_JOYSTICK, value 0x120.
190 switch (data->code) {
191 case 0x110: button = Qt::LeftButton; break; // BTN_LEFT
192 case 0x111: button = Qt::RightButton; break;
193 case 0x112: button = Qt::MiddleButton; break;
194 case 0x113: button = Qt::ExtraButton1; break; // AKA Qt::BackButton
195 case 0x114: button = Qt::ExtraButton2; break; // AKA Qt::ForwardButton
196 case 0x115: button = Qt::ExtraButton3; break; // AKA Qt::TaskButton
197 case 0x116: button = Qt::ExtraButton4; break;
198 case 0x117: button = Qt::ExtraButton5; break;
199 case 0x118: button = Qt::ExtraButton6; break;
200 case 0x119: button = Qt::ExtraButton7; break;
201 case 0x11a: button = Qt::ExtraButton8; break;
202 case 0x11b: button = Qt::ExtraButton9; break;
203 case 0x11c: button = Qt::ExtraButton10; break;
204 case 0x11d: button = Qt::ExtraButton11; break;
205 case 0x11e: button = Qt::ExtraButton12; break;
206 case 0x11f: button = Qt::ExtraButton13; break;
211 m_buttons &= ~button;
213 } else if (data->type == EV_SYN && data->code == SYN_REPORT) {
215 btnChanged = posChanged = false;
217 pendingMouseEvent = false;
218 } else if (posChanged) {
221 pendingMouseEvent = true;
222 eventCompressCount++;
227 } else if (data->type == EV_MSC && data->code == MSC_SCAN) {
228 // kernel encountered an unmapped key - just ignore it
232 if (m_compression && pendingMouseEvent) {
233 int distanceSquared = (m_x - m_prevx)*(m_x - m_prevx) + (m_y - m_prevy)*(m_y - m_prevy);
234 if (distanceSquared > m_jitterLimitSquared)