4f60bbe29218e34d290cd8875d8271ca96955ca2
[profile/ivi/qtbase.git] / src / plugins / platforms / windows / qwindowsmousehandler.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 plugins 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 "qwindowsmousehandler.h"
43 #include "qwindowskeymapper.h"
44 #include "qwindowscontext.h"
45 #include "qwindowswindow.h"
46 #include "qwindowsintegration.h"
47
48 #include <QtGui/qwindowsysteminterface.h>
49 #include <QtGui/QGuiApplication>
50 #include <QtGui/QScreen>
51
52 #include <QtCore/QDebug>
53 #include <QtCore/QScopedArrayPointer>
54
55 #include <windowsx.h>
56
57 QT_BEGIN_NAMESPACE
58
59 static inline void compressMouseMove(MSG *msg)
60 {
61     // Compress mouse move events
62     if (msg->message == WM_MOUSEMOVE) {
63         MSG mouseMsg;
64         while (PeekMessage(&mouseMsg, msg->hwnd, WM_MOUSEFIRST,
65                            WM_MOUSELAST, PM_NOREMOVE)) {
66             if (mouseMsg.message == WM_MOUSEMOVE) {
67 #define PEEKMESSAGE_IS_BROKEN 1
68 #ifdef PEEKMESSAGE_IS_BROKEN
69                 // Since the Windows PeekMessage() function doesn't
70                 // correctly return the wParam for WM_MOUSEMOVE events
71                 // if there is a key release event in the queue
72                 // _before_ the mouse event, we have to also consider
73                 // key release events (kls 2003-05-13):
74                 MSG keyMsg;
75                 bool done = false;
76                 while (PeekMessage(&keyMsg, 0, WM_KEYFIRST, WM_KEYLAST,
77                                    PM_NOREMOVE)) {
78                     if (keyMsg.time < mouseMsg.time) {
79                         if ((keyMsg.lParam & 0xC0000000) == 0x40000000) {
80                             PeekMessage(&keyMsg, 0, keyMsg.message,
81                                         keyMsg.message, PM_REMOVE);
82                         } else {
83                             done = true;
84                             break;
85                         }
86                     } else {
87                         break; // no key event before the WM_MOUSEMOVE event
88                     }
89                 }
90                 if (done)
91                     break;
92 #else
93                 // Actually the following 'if' should work instead of
94                 // the above key event checking, but apparently
95                 // PeekMessage() is broken :-(
96                 if (mouseMsg.wParam != msg.wParam)
97                     break; // leave the message in the queue because
98                 // the key state has changed
99 #endif
100                 // Update the passed in MSG structure with the
101                 // most recent one.
102                 msg->lParam = mouseMsg.lParam;
103                 msg->wParam = mouseMsg.wParam;
104                 // Extract the x,y coordinates from the lParam as we do in the WndProc
105                 msg->pt.x = GET_X_LPARAM(mouseMsg.lParam);
106                 msg->pt.y = GET_Y_LPARAM(mouseMsg.lParam);
107                 ClientToScreen(msg->hwnd, &(msg->pt));
108                 // Remove the mouse move message
109                 PeekMessage(&mouseMsg, msg->hwnd, WM_MOUSEMOVE,
110                             WM_MOUSEMOVE, PM_REMOVE);
111             } else {
112                 break; // there was no more WM_MOUSEMOVE event
113             }
114         }
115     }
116 }
117
118 /*!
119     \class QWindowsMouseHandler
120     \brief Windows mouse handler
121
122     Dispatches mouse and touch events. Separate for code cleanliness.
123
124     \internal
125     \ingroup qt-lighthouse-win
126 */
127
128 QWindowsMouseHandler::QWindowsMouseHandler() :
129     m_windowUnderMouse(0),
130     m_touchDevice(0)
131 {
132 }
133
134 Qt::MouseButtons QWindowsMouseHandler::queryMouseButtons()
135 {
136     Qt::MouseButtons result = 0;
137     const bool mouseSwapped = GetSystemMetrics(SM_SWAPBUTTON);
138     if (GetAsyncKeyState(VK_LBUTTON) < 0)
139         result |= mouseSwapped ? Qt::RightButton: Qt::LeftButton;
140     if (GetAsyncKeyState(VK_RBUTTON) < 0)
141         result |= mouseSwapped ? Qt::LeftButton : Qt::RightButton;
142     if (GetAsyncKeyState(VK_MBUTTON) < 0)
143         result |= Qt::MidButton;
144     return result;
145 }
146
147 bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
148                                                QtWindows::WindowsEventType et,
149                                                MSG msg, LRESULT *result)
150 {
151     if (et == QtWindows::MouseWheelEvent)
152         return translateMouseWheelEvent(window, hwnd, msg, result);
153
154     const QPoint winEventPosition(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
155     if (et & QtWindows::NonClientEventFlag) {
156         const QPoint globalPosition = winEventPosition;
157         const QPoint clientPosition = QWindowsGeometryHint::mapFromGlobal(hwnd, globalPosition);
158         const Qt::MouseButtons buttons = QWindowsMouseHandler::queryMouseButtons();
159         QWindowSystemInterface::handleFrameStrutMouseEvent(window, clientPosition,
160                                                            globalPosition, buttons,
161                                                            QWindowsKeyMapper::queryKeyboardModifiers());
162         return false; // Allow further event processing (dragging of windows).
163     }
164
165     *result = 0;
166     if (msg.message == WM_MOUSELEAVE) {
167         // When moving out of a child, MouseMove within parent is received first
168         // (see below)
169         if (QWindowsContext::verboseEvents)
170             qDebug() << "WM_MOUSELEAVE for " << window << " current= " << m_windowUnderMouse;
171         if (window == m_windowUnderMouse) {
172             QWindowSystemInterface::handleLeaveEvent(window);
173             m_windowUnderMouse = 0;
174         }
175         return true;
176     }
177     compressMouseMove(&msg);
178     // Eat mouse move after size grip drag.
179     if (msg.message == WM_MOUSEMOVE) {
180         QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window->handle());
181         if (platformWindow->testFlag(QWindowsWindow::SizeGripOperation)) {
182             MSG mouseMsg;
183             while (PeekMessage(&mouseMsg, platformWindow->handle(), WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE)) ;
184             platformWindow->clearFlag(QWindowsWindow::SizeGripOperation);
185             return true;
186         }
187     }
188     // Enter new window: track to generate leave event.
189     if (m_windowUnderMouse != window) {
190         // The tracking on m_windowUnderMouse might still be active and
191         // trigger later on.
192         if (m_windowUnderMouse) {
193             if (QWindowsContext::verboseEvents)
194                 qDebug() << "Synthetic leave for " << m_windowUnderMouse;
195             QWindowSystemInterface::handleLeaveEvent(m_windowUnderMouse);
196         }
197         m_windowUnderMouse = window;
198         if (QWindowsContext::verboseEvents)
199             qDebug() << "Entering " << window;
200         QWindowsWindow::baseWindowOf(window)->applyCursor();
201 //#ifndef Q_OS_WINCE
202         QWindowSystemInterface::handleEnterEvent(window);
203 #ifndef Q_OS_WINCE
204         TRACKMOUSEEVENT tme;
205         tme.cbSize = sizeof(TRACKMOUSEEVENT);
206         tme.dwFlags = TME_LEAVE;
207         tme.hwndTrack = hwnd;
208         tme.dwHoverTime = HOVER_DEFAULT; //
209         if (!TrackMouseEvent(&tme))
210             qWarning("TrackMouseEvent failed.");
211 #endif // !Q_OS_WINCE
212     }
213     const QPoint clientPosition = winEventPosition;
214     QWindowSystemInterface::handleMouseEvent(window, clientPosition,
215                                              QWindowsGeometryHint::mapToGlobal(hwnd, clientPosition),
216                                              keyStateToMouseButtons((int)msg.wParam),
217                                              QWindowsKeyMapper::queryKeyboardModifiers());
218     return true;
219 }
220
221 bool QWindowsMouseHandler::translateMouseWheelEvent(QWindow *window, HWND,
222                                                     MSG msg, LRESULT *)
223 {
224     const Qt::MouseButtons buttons = keyStateToMouseButtons((int)msg.wParam);
225     const Qt::KeyboardModifiers mods = keyStateToModifiers((int)msg.wParam);
226
227     int delta;
228     if (msg.message == WM_MOUSEWHEEL || msg.message == WM_MOUSEHWHEEL)
229         delta = (short) HIWORD (msg.wParam);
230     else
231         delta = (int) msg.wParam;
232
233     Qt::Orientation orientation = (msg.message == WM_MOUSEHWHEEL
234                                   || (buttons & Qt::AltModifier)) ?
235                                   Qt::Horizontal : Qt::Vertical;
236
237     // according to the MSDN documentation on WM_MOUSEHWHEEL:
238     // a positive value indicates that the wheel was rotated to the right;
239     // a negative value indicates that the wheel was rotated to the left.
240     // Qt defines this value as the exact opposite, so we have to flip the value!
241     if (msg.message == WM_MOUSEHWHEEL)
242         delta = -delta;
243
244     const QPoint globalPos(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
245     // TODO: if there is a widget under the mouse and it is not shadowed
246     // QWindow *receiver = windowAt(pos);
247     // by modality, we send the event to it first.
248     //synaptics touchpad shows its own widget at this position
249     //so widgetAt() will fail with that HWND, try child of this widget
250     // if (!receiver) receiver = window->childAt(pos);
251     QWindow *receiver = window;
252     QWindowSystemInterface::handleWheelEvent(receiver,
253                                              QWindowsGeometryHint::mapFromGlobal(receiver, globalPos),
254                                              globalPos,
255                                              delta, orientation, mods);
256     return true;
257 }
258
259 // from bool QApplicationPrivate::translateTouchEvent()
260 bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND,
261                                                QtWindows::WindowsEventType,
262                                                MSG msg, LRESULT *)
263 {
264 #ifndef Q_OS_WINCE
265     typedef QWindowSystemInterface::TouchPoint QTouchPoint;
266     typedef QList<QWindowSystemInterface::TouchPoint> QTouchPointList;
267
268     const QRect screenGeometry = window->screen()->geometry();
269
270     const int winTouchPointCount = msg.wParam;
271     QScopedArrayPointer<TOUCHINPUT> winTouchInputs(new TOUCHINPUT[winTouchPointCount]);
272     memset(winTouchInputs.data(), 0, sizeof(TOUCHINPUT) * winTouchPointCount);
273
274     QTouchPointList touchPoints;
275     touchPoints.reserve(winTouchPointCount);
276     Qt::TouchPointStates allStates = 0;
277
278     Q_ASSERT(QWindowsContext::user32dll.getTouchInputInfo);
279
280     QWindowsContext::user32dll.getTouchInputInfo((HANDLE) msg.lParam, msg.wParam, winTouchInputs.data(), sizeof(TOUCHINPUT));
281     for (int i = 0; i < winTouchPointCount; ++i) {
282         const TOUCHINPUT &winTouchInput = winTouchInputs[i];
283         QTouchPoint touchPoint;
284         touchPoint.pressure = 1.0;
285         touchPoint.id = m_touchInputIDToTouchPointID.value(winTouchInput.dwID, -1);
286         if (touchPoint.id == -1) {
287             touchPoint.id = m_touchInputIDToTouchPointID.size();
288             m_touchInputIDToTouchPointID.insert(winTouchInput.dwID, touchPoint.id);
289         }
290
291         QPointF screenPos = QPointF(qreal(winTouchInput.x) / qreal(100.), qreal(winTouchInput.y) / qreal(100.));
292         if (winTouchInput.dwMask & TOUCHINPUTMASKF_CONTACTAREA)
293             touchPoint.area.setSize(QSizeF(qreal(winTouchInput.cxContact) / qreal(100.),
294                                            qreal(winTouchInput.cyContact) / qreal(100.)));
295         touchPoint.area.moveCenter(screenPos);
296
297         if (winTouchInput.dwFlags & TOUCHEVENTF_DOWN) {
298             touchPoint.state = Qt::TouchPointPressed;
299         } else if (winTouchInput.dwFlags & TOUCHEVENTF_UP) {
300             touchPoint.state = Qt::TouchPointReleased;
301         } else {
302             // TODO: Previous code checked"
303             // screenPos == touchPoint.normalPosition -> Qt::TouchPointStationary, but
304             // but touchPoint.normalPosition was never initialized?
305             touchPoint.state = touchPoint.state;
306         }
307
308         touchPoint.normalPosition = QPointF(screenPos.x() / screenGeometry.width(),
309                                  screenPos.y() / screenGeometry.height());
310
311         allStates |= touchPoint.state;
312
313         touchPoints.append(touchPoint);
314     }
315
316     QWindowsContext::user32dll.closeTouchInputHandle((HANDLE) msg.lParam);
317
318     // all touch points released, forget the ids we've seen, they may not be reused
319     if (allStates == Qt::TouchPointReleased)
320         m_touchInputIDToTouchPointID.clear();
321
322     if (!m_touchDevice) {
323         m_touchDevice = new QTouchDevice;
324         // TODO: Device used to be hardcoded to screen in previous code.
325         m_touchDevice->setType(QTouchDevice::TouchScreen);
326         m_touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::NormalizedPosition);
327         QWindowSystemInterface::registerTouchDevice(m_touchDevice);
328     }
329
330     QWindowSystemInterface::handleTouchEvent(window,
331                                              m_touchDevice,
332                                              touchPoints);
333     return true;
334 #else
335     return false;
336 #endif
337 }
338
339 QT_END_NAMESPACE