Add Windows to the Lighthouse.
[profile/ivi/qtbase.git] / src / plugins / platforms / windows / qwindowsmousehandler.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (info@qt.nokia.com)
6 **
7 ** This file is part of the plugins of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qwindowsmousehandler.h"
43 #include "qwindowscontext.h"
44 #include "qwindowswindow.h"
45 #include "qwindowsintegration.h"
46
47 #include <QtGui/QWindowSystemInterface>
48 #include <QtGui/QGuiApplication>
49 #include <QtGui/QScreen>
50
51 #include <QtCore/QDebug>
52 #include <QtCore/QScopedArrayPointer>
53
54 #include <windowsx.h>
55
56 QT_BEGIN_NAMESPACE
57
58 static inline void compressMouseMove(MSG *msg)
59 {
60     // Compress mouse move events
61     if (msg->message == WM_MOUSEMOVE) {
62         MSG mouseMsg;
63         while (PeekMessage(&mouseMsg, msg->hwnd, WM_MOUSEFIRST,
64                            WM_MOUSELAST, PM_NOREMOVE)) {
65             if (mouseMsg.message == WM_MOUSEMOVE) {
66 #define PEEKMESSAGE_IS_BROKEN 1
67 #ifdef PEEKMESSAGE_IS_BROKEN
68                 // Since the Windows PeekMessage() function doesn't
69                 // correctly return the wParam for WM_MOUSEMOVE events
70                 // if there is a key release event in the queue
71                 // _before_ the mouse event, we have to also consider
72                 // key release events (kls 2003-05-13):
73                 MSG keyMsg;
74                 bool done = false;
75                 while (PeekMessage(&keyMsg, 0, WM_KEYFIRST, WM_KEYLAST,
76                                    PM_NOREMOVE)) {
77                     if (keyMsg.time < mouseMsg.time) {
78                         if ((keyMsg.lParam & 0xC0000000) == 0x40000000) {
79                             PeekMessage(&keyMsg, 0, keyMsg.message,
80                                         keyMsg.message, PM_REMOVE);
81                         } else {
82                             done = true;
83                             break;
84                         }
85                     } else {
86                         break; // no key event before the WM_MOUSEMOVE event
87                     }
88                 }
89                 if (done)
90                     break;
91 #else
92                 // Actually the following 'if' should work instead of
93                 // the above key event checking, but apparently
94                 // PeekMessage() is broken :-(
95                 if (mouseMsg.wParam != msg.wParam)
96                     break; // leave the message in the queue because
97                 // the key state has changed
98 #endif
99                 // Update the passed in MSG structure with the
100                 // most recent one.
101                 msg->lParam = mouseMsg.lParam;
102                 msg->wParam = mouseMsg.wParam;
103                 // Extract the x,y coordinates from the lParam as we do in the WndProc
104                 msg->pt.x = GET_X_LPARAM(mouseMsg.lParam);
105                 msg->pt.y = GET_Y_LPARAM(mouseMsg.lParam);
106                 ClientToScreen(msg->hwnd, &(msg->pt));
107                 // Remove the mouse move message
108                 PeekMessage(&mouseMsg, msg->hwnd, WM_MOUSEMOVE,
109                             WM_MOUSEMOVE, PM_REMOVE);
110             } else {
111                 break; // there was no more WM_MOUSEMOVE event
112             }
113         }
114     }
115 }
116
117 /*!
118     \class QWindowsMouseHandler
119     \brief Windows mouse handler
120
121     Dispatches mouse and touch events. Separate for code cleanliness.
122
123     \ingroup qt-lighthouse-win
124 */
125
126 QWindowsMouseHandler::QWindowsMouseHandler() :
127     m_windowUnderMouse(0)
128 {
129 }
130
131 bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
132                                                QtWindows::WindowsEventType et,
133                                                MSG msg, LRESULT *result)
134 {
135     if (et & QtWindows::NonClientEventFlag)
136         return false;
137     if (et == QtWindows::MouseWheelEvent)
138         return translateMouseWheelEvent(window, hwnd, msg, result);
139     *result = 0;
140     if (msg.message == WM_MOUSELEAVE) {
141         // When moving out of a child, MouseMove within parent is received first
142         // (see below)
143         if (QWindowsContext::verboseEvents)
144             qDebug() << "WM_MOUSELEAVE for " << window << " current= " << m_windowUnderMouse;
145         if (window == m_windowUnderMouse) {
146             QWindowSystemInterface::handleLeaveEvent(window);
147             m_windowUnderMouse = 0;
148         }
149         return true;
150     }
151     compressMouseMove(&msg);
152     const QPoint client(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
153     // Enter new window: track to generate leave event.
154     if (m_windowUnderMouse != window) {
155         // The tracking on m_windowUnderMouse might still be active and
156         // trigger later on.
157         if (m_windowUnderMouse) {
158             if (QWindowsContext::verboseEvents)
159                 qDebug() << "Synthetic leave for " << m_windowUnderMouse;
160             QWindowSystemInterface::handleLeaveEvent(m_windowUnderMouse);
161         }
162         m_windowUnderMouse = window;
163         if (QWindowsContext::verboseEvents)
164             qDebug() << "Entering " << window;
165         QWindowsWindow::baseWindowOf(window)->applyCursor();
166         QWindowSystemInterface::handleEnterEvent(window);
167         TRACKMOUSEEVENT tme;
168         tme.cbSize = sizeof(TRACKMOUSEEVENT);
169         tme.dwFlags = TME_LEAVE;
170         tme.hwndTrack = hwnd;
171         tme.dwHoverTime = HOVER_DEFAULT; //
172         if (!TrackMouseEvent(&tme))
173             qWarning("TrackMouseEvent failed.");
174     }
175     QWindowSystemInterface::handleMouseEvent(window, client,
176                                              QWindowsGeometryHint::mapToGlobal(hwnd, client),
177                                              keyStateToMouseButtons((int)msg.wParam));
178     return true;
179 }
180
181 bool QWindowsMouseHandler::translateMouseWheelEvent(QWindow *window, HWND,
182                                                     MSG msg, LRESULT *)
183 {
184     const Qt::MouseButtons buttons = keyStateToMouseButtons((int)msg.wParam);
185     int delta;
186     if (msg.message == WM_MOUSEWHEEL || msg.message == WM_MOUSEHWHEEL)
187         delta = (short) HIWORD (msg.wParam);
188     else
189         delta = (int) msg.wParam;
190
191     Qt::Orientation orientation = (msg.message == WM_MOUSEHWHEEL
192                                   || (buttons & Qt::AltModifier)) ?
193                                   Qt::Horizontal : Qt::Vertical;
194
195     // according to the MSDN documentation on WM_MOUSEHWHEEL:
196     // a positive value indicates that the wheel was rotated to the right;
197     // a negative value indicates that the wheel was rotated to the left.
198     // Qt defines this value as the exact opposite, so we have to flip the value!
199     if (msg.message == WM_MOUSEHWHEEL)
200         delta = -delta;
201
202     const QPoint globalPos(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
203     // TODO: if there is a widget under the mouse and it is not shadowed
204     // QWindow *receiver = windowAt(pos);
205     // by modality, we send the event to it first.
206     //synaptics touchpad shows its own widget at this position
207     //so widgetAt() will fail with that HWND, try child of this widget
208     // if (!receiver) receiver = window->childAt(pos);
209     QWindow *receiver = window;
210     QWindowSystemInterface::handleWheelEvent(receiver,
211                                              QWindowsGeometryHint::mapFromGlobal(receiver, globalPos),
212                                              globalPos,
213                                              delta, orientation);
214     return true;
215 }
216
217 // from bool QApplicationPrivate::translateTouchEvent()
218 bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND,
219                                                QtWindows::WindowsEventType,
220                                                MSG msg, LRESULT *)
221 {
222     typedef QWindowSystemInterface::TouchPoint QTouchPoint;
223     typedef QList<QWindowSystemInterface::TouchPoint> QTouchPointList;
224
225     const QRect screenGeometry = window->screen()->geometry();
226
227     const int winTouchPointCount = msg.wParam;
228     QScopedArrayPointer<TOUCHINPUT> winTouchInputs(new TOUCHINPUT[winTouchPointCount]);
229     memset(winTouchInputs.data(), 0, sizeof(TOUCHINPUT) * winTouchPointCount);
230
231     QTouchPointList touchPoints;
232     touchPoints.reserve(winTouchPointCount);
233     Qt::TouchPointStates allStates = 0;
234
235     Q_ASSERT(QWindowsContext::user32dll.getTouchInputInfo);
236
237     QWindowsContext::user32dll.getTouchInputInfo((HANDLE) msg.lParam, msg.wParam, winTouchInputs.data(), sizeof(TOUCHINPUT));
238     for (int i = 0; i < winTouchPointCount; ++i) {
239         const TOUCHINPUT &winTouchInput = winTouchInputs[i];
240         QTouchPoint touchPoint;
241         touchPoint.pressure = 1.0;
242         touchPoint.isPrimary = (winTouchInput.dwFlags & TOUCHEVENTF_PRIMARY) != 0;
243         touchPoint.id = m_touchInputIDToTouchPointID.value(winTouchInput.dwID, -1);
244         if (touchPoint.id == -1) {
245             touchPoint.id = m_touchInputIDToTouchPointID.size();
246             m_touchInputIDToTouchPointID.insert(winTouchInput.dwID, touchPoint.id);
247         }
248
249         QPointF screenPos = QPointF(qreal(winTouchInput.x) / qreal(100.), qreal(winTouchInput.y) / qreal(100.));
250         if (winTouchInput.dwMask & TOUCHINPUTMASKF_CONTACTAREA)
251             touchPoint.area.setSize(QSizeF(qreal(winTouchInput.cxContact) / qreal(100.),
252                                            qreal(winTouchInput.cyContact) / qreal(100.)));
253         touchPoint.area.moveCenter(screenPos);
254
255         if (winTouchInput.dwFlags & TOUCHEVENTF_DOWN) {
256             touchPoint.state = Qt::TouchPointPressed;
257         } else if (winTouchInput.dwFlags & TOUCHEVENTF_UP) {
258             touchPoint.state = Qt::TouchPointReleased;
259         } else {
260             // TODO: Previous code checked"
261             // screenPos == touchPoint.normalPosition -> Qt::TouchPointStationary, but
262             // but touchPoint.normalPosition was never initialized?
263             touchPoint.state = touchPoint.state;
264         }
265
266         touchPoint.normalPosition = QPointF(screenPos.x() / screenGeometry.width(),
267                                  screenPos.y() / screenGeometry.height());
268
269         allStates |= touchPoint.state;
270
271         touchPoints.append(touchPoint);
272     }
273
274     QWindowsContext::user32dll.closeTouchInputHandle((HANDLE) msg.lParam);
275
276     // all touch points released, forget the ids we've seen, they may not be reused
277     if ((allStates & Qt::TouchPointStateMask) == Qt::TouchPointReleased)
278         m_touchInputIDToTouchPointID.clear();
279
280     // TODO: Device used to be hardcoded to screen in previous code.
281     // What is the correct event type? Which parts of translateRawTouchEvent() are required?
282     QWindowSystemInterface::handleTouchEvent(window, QEvent::TouchBegin,
283                                              QTouchEvent::TouchScreen,
284                                              touchPoints);
285     return true;
286 }
287
288 QT_END_NAMESPACE