554384dfe9a583ff520474843e585f9ce0fdaffb
[profile/ivi/qtbase.git] / src / platformsupport / input / evdevmouse / qevdevmousehandler.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qevdevmousehandler_p.h"
43
44 #include <QSocketNotifier>
45 #include <QStringList>
46 #include <QPoint>
47 #include <QGuiApplication>
48 #include <QScreen>
49 #include <QtGui/qwindowsysteminterface.h>
50
51 #include <qplatformdefs.h>
52 #include <private/qcore_unix_p.h> // overrides QT_OPEN
53
54 #include <errno.h>
55
56 #include <linux/kd.h>
57 #include <linux/input.h>
58
59 #include <qdebug.h>
60
61 //#define QT_QPA_MOUSE_HANDLER_DEBUG
62
63 QT_BEGIN_NAMESPACE
64
65 QEvdevMouseHandler *QEvdevMouseHandler::create(const QString &device, const QString &specification)
66 {
67 #ifdef QT_QPA_MOUSE_HANDLER_DEBUG
68     qWarning() << "Try to create mouse handler for" << device << specification;
69 #endif
70
71     bool compression = true;
72     int jitterLimit = 0;
73
74     QStringList args = specification.split(QLatin1Char(':'));
75     foreach (const QString &arg, args) {
76         if (arg == QLatin1String("nocompress"))
77             compression = false;
78         else if (arg.startsWith(QLatin1String("dejitter=")))
79             jitterLimit = arg.mid(9).toInt();
80     }
81
82     int fd;
83     fd = qt_safe_open(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0);
84     if (fd >= 0) {
85         return new QEvdevMouseHandler(device, fd, compression, jitterLimit);
86     } else {
87         qWarning("Cannot open mouse input device '%s': %s", qPrintable(device), strerror(errno));
88         return 0;
89     }
90 }
91
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)
95 {
96     setObjectName(QLatin1String("Evdev Mouse Handler"));
97
98     m_jitterLimitSquared = jitterLimit * jitterLimit;
99
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()));
104 }
105
106 QEvdevMouseHandler::~QEvdevMouseHandler()
107 {
108     if (m_fd >= 0)
109         qt_safe_close(m_fd);
110 }
111
112 void QEvdevMouseHandler::sendMouseEvent()
113 {
114     int x = m_x - m_prevx;
115     int y = m_y - m_prevy;
116     if (m_prevInvalid) {
117         x = y = 0;
118         m_prevInvalid = false;
119     }
120
121     emit handleMouseEvent(x, y, m_buttons);
122
123     m_prevx = m_x;
124     m_prevy = m_y;
125 }
126
127 void QEvdevMouseHandler::readMouseData()
128 {
129     struct ::input_event buffer[32];
130     int n = 0;
131     bool posChanged = false, btnChanged = false;
132     bool pendingMouseEvent = false;
133     int eventCompressCount = 0;
134     forever {
135         int result = QT_READ(m_fd, reinterpret_cast<char *>(buffer) + n, sizeof(buffer) - n);
136
137         if (result == 0) {
138             qWarning("Got EOF from the input device.");
139             return;
140         } else if (result < 0) {
141             if (errno != EINTR && errno != EAGAIN) {
142                 qWarning("Could not read from input device: %s", strerror(errno));
143                 return;
144             }
145         } else {
146             n += result;
147             if (n % sizeof(buffer[0]) == 0)
148                 break;
149         }
150     }
151
152     n /= sizeof(buffer[0]);
153
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) {
160                 m_x = data->value;
161                 posChanged = true;
162             } else if (data->code == ABS_Y && m_y != data->value) {
163                 m_y = data->value;
164                 posChanged = true;
165             }
166         } else if (data->type == EV_REL) {
167             if (data->code == REL_X) {
168                 m_x += data->value;
169                 posChanged = true;
170             } else if (data->code == REL_Y) {
171                 m_y += data->value;
172                 posChanged = true;
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);
181             }
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;
207             }
208             if (data->value)
209                 m_buttons |= button;
210             else
211                 m_buttons &= ~button;
212             btnChanged = true;
213         } else if (data->type == EV_SYN && data->code == SYN_REPORT) {
214             if (btnChanged) {
215                 btnChanged = posChanged = false;
216                 sendMouseEvent();
217                 pendingMouseEvent = false;
218             } else if (posChanged) {
219                 posChanged = false;
220                 if (m_compression) {
221                     pendingMouseEvent = true;
222                     eventCompressCount++;
223                 } else {
224                     sendMouseEvent();
225                 }
226             }
227         } else if (data->type == EV_MSC && data->code == MSC_SCAN) {
228             // kernel encountered an unmapped key - just ignore it
229             continue;
230         }
231     }
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)
235             sendMouseEvent();
236     }
237 }
238
239 QT_END_NAMESPACE