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 "qmouseqnx_qws.h"
43 #ifndef QT_NO_QWS_TRANSFORMED
44 #include "qscreen_qws.h"
45 #endif
46 
47 #include "qplatformdefs.h"
48 #include "qsocketnotifier.h"
49 #include "private/qcore_unix_p.h"
50 
51 #include <sys/dcmd_input.h>
52 #include <errno.h>
53 
54 QT_BEGIN_NAMESPACE
55 
56 /*!
57     \class QQnxMouseHandler
58     \preliminary
59     \ingroup qws
60     \internal
61     \since 4.6
62 
63     \brief The QQnxMouseHandler class implements a mouse driver
64     for the QNX \c{devi-hid} input manager.
65 
66     To be able to compile this mouse handler, \l{Qt for Embedded Linux}
67     must be configured with the \c -qt-mouse-qnx option, see the
68     \l{Qt for Embedded Linux Pointer Handling}{Pointer Handling} documentation for details.
69 
70     In order to use this mouse handler, the \c{devi-hid} input manager
71     must be set up and run with the resource manager interface (option \c{-r}).
72     Also, Photon must not be running.
73 
74     Example invocation from command line: \c{/usr/photon/bin/devi-hid -Pr kbd mouse}
75     Note that after running \c{devi-hid}, you will not be able to use the local
76     shell anymore. It is suggested to run the command in a shell scrip, that launches
77     a Qt application after invocation of \c{devi-hid}.
78 
79     To make \l{Qt for Embedded Linux} explicitly choose the qnx mouse
80     handler, set the QWS_MOUSE_PROTO environment variable to \c{qnx}. By default,
81     the first mouse device (\c{/dev/devi/mouse0}) is used. To override, pass a device
82     name as the first and only parameter, for example
83     \c{QWS_MOUSE_PROTO=qnx:/dev/devi/mouse1; export QWS_MOUSE_PROTO}.
84 
85     \sa {Qt for Embedded Linux Pointer Handling}{Pointer Handling}, {Qt for Embedded Linux}
86 */
87 
88 /*!
89     Constructs a mouse handler for the specified \a device, defaulting to \c{/dev/devi/mouse0}.
90     The \a driver parameter must be \c{"qnx"}.
91 
92     Note that you should never instanciate this class, instead let QMouseDriverFactory
93     handle the mouse handlers.
94 
95     \sa QMouseDriverFactory
96  */
QQnxMouseHandler(const QString & driver,const QString & device)97 QQnxMouseHandler::QQnxMouseHandler(const QString & driver, const QString &device)
98     : QObject(), QWSMouseHandler(driver, device), mouseButtons(Qt::NoButton)
99 {
100     // open the mouse device with O_NONBLOCK so reading won't block when there's no data
101     mouseFD = QT_OPEN(device.isEmpty() ? "/dev/devi/mouse0" : device.toLatin1().constData(),
102                       QT_OPEN_RDONLY | O_NONBLOCK);
103     if (mouseFD == -1) {
104         qErrnoWarning(errno, "QQnxMouseHandler: Unable to open mouse device");
105     } else {
106         struct _pointer_info data;
107         if (devctl(mouseFD, _POINTERGETINFO, &data, sizeof(data), NULL) == EOK)
108             absolutePositioning = (data.flags & _POINTER_FLAG_ABSOLUTE);
109         else
110             absolutePositioning = !device.isEmpty() && device.contains(QLatin1String("touch"));
111 
112         // register a socket notifier on the file descriptor so we'll wake up whenever
113         // there's a mouse move waiting for us.
114         mouseNotifier = new QSocketNotifier(mouseFD, QSocketNotifier::Read, this);
115         connect(mouseNotifier, SIGNAL(activated(int)), SLOT(socketActivated()));
116 
117         qDebug("QQnxMouseHandler: connected.");
118     }
119 #ifndef QT_NO_QWS_TRANSFORMED
120     transformedMousePos = QPoint(qt_screen->deviceWidth() / 2, qt_screen->deviceHeight() / 2);
121 #endif
122 }
123 
124 /*!
125     Destroys this mouse handler and closes the connection to the mouse device.
126  */
~QQnxMouseHandler()127 QQnxMouseHandler::~QQnxMouseHandler()
128 {
129     if (mouseFD != -1)
130         QT_CLOSE(mouseFD);
131 }
132 
133 /*! \reimp */
resume()134 void QQnxMouseHandler::resume()
135 {
136     if (mouseNotifier)
137         mouseNotifier->setEnabled(true);
138 }
139 
140 /*! \reimp */
suspend()141 void QQnxMouseHandler::suspend()
142 {
143     if (mouseNotifier)
144         mouseNotifier->setEnabled(false);
145 }
146 
147 /*! \internal
148 
149   This function is called whenever there is activity on the mouse device.
150   By default, it reads up to 10 mouse move packets and calls mouseChanged()
151   for each of them.
152 */
socketActivated()153 void QQnxMouseHandler::socketActivated()
154 {
155 #ifndef QT_NO_QWS_TRANSFORMED
156     QPoint queuedPos = transformedMousePos;
157 #else
158     QPoint queuedPos = mousePos;
159 #endif
160 
161     // _mouse_packet is a QNX structure. devi-hid is nice enough to translate
162     // the raw byte data from mouse devices into generic format for us.
163     struct _mouse_packet buffer[32];
164     int n = 0;
165 
166     forever {
167         int bytesRead = QT_READ(mouseFD, reinterpret_cast<char *>(buffer) + n, sizeof(buffer) - n);
168         if (bytesRead == -1) {
169             // EAGAIN means that there are no more mouse events to read
170             if (errno != EAGAIN)
171                 qErrnoWarning(errno, "QQnxMouseHandler: Could not read from input device");
172             break;
173         }
174 
175         n += bytesRead;
176         if (n % sizeof(buffer[0]) == 0)
177             break;
178     }
179     n /= sizeof(buffer[0]);
180 
181     for (int i = 0; i < n; ++i) {
182         const struct _mouse_packet &packet = buffer[i];
183 
184         // translate the coordinates from the QNX data structure to the Qt coordinates
185         if (absolutePositioning) {
186             queuedPos = QPoint(packet.dx, packet.dy);
187         } else {
188             // note the swapped y axis
189             queuedPos += QPoint(packet.dx, -packet.dy);
190 
191             // QNX only tells us relative mouse movements, not absolute ones, so
192             // limit the cursor position manually to the screen
193             limitToScreen(queuedPos);
194         }
195 
196         // translate the QNX mouse button bitmask to Qt buttons
197         int buttons = Qt::NoButton;
198         if (packet.hdr.buttons & _POINTER_BUTTON_LEFT)
199             buttons |= Qt::LeftButton;
200         if (packet.hdr.buttons & _POINTER_BUTTON_MIDDLE)
201             buttons |= Qt::MidButton;
202         if (packet.hdr.buttons & _POINTER_BUTTON_RIGHT)
203             buttons |= Qt::RightButton;
204 
205         if (buttons != mouseButtons) {
206             // send the MouseEvent to avoid missing any clicks
207             mouseChanged(queuedPos, buttons, 0);
208             // mousePos updated by the mouseChanged()
209 #ifndef QT_NO_QWS_TRANSFORMED
210             queuedPos = transformedMousePos;
211 #else
212             queuedPos = mousePos;
213 #endif
214             mouseButtons = buttons;
215         }
216     }
217 
218 #ifndef QT_NO_QWS_TRANSFORMED
219     if (queuedPos != transformedMousePos) {
220         mouseChanged(queuedPos, mouseButtons, 0);
221         transformedMousePos = queuedPos;
222     }
223 #else
224     if (queuedPos != mousePos)
225         mouseChanged(queuedPos, mouseButtons, 0);
226 #endif
227 }
228 
229 QT_END_NAMESPACE
230