1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (info@qt.nokia.com)
7 ** This file is part of the plugins of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qwindowsmousehandler.h"
43 #include "qwindowscontext.h"
44 #include "qwindowswindow.h"
45 #include "qwindowsintegration.h"
47 #include <QtGui/QWindowSystemInterface>
48 #include <QtGui/QGuiApplication>
49 #include <QtGui/QScreen>
51 #include <QtCore/QDebug>
52 #include <QtCore/QScopedArrayPointer>
58 static inline void compressMouseMove(MSG *msg)
60 // Compress mouse move events
61 if (msg->message == WM_MOUSEMOVE) {
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):
75 while (PeekMessage(&keyMsg, 0, WM_KEYFIRST, WM_KEYLAST,
77 if (keyMsg.time < mouseMsg.time) {
78 if ((keyMsg.lParam & 0xC0000000) == 0x40000000) {
79 PeekMessage(&keyMsg, 0, keyMsg.message,
80 keyMsg.message, PM_REMOVE);
86 break; // no key event before the WM_MOUSEMOVE event
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
99 // Update the passed in MSG structure with the
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);
111 break; // there was no more WM_MOUSEMOVE event
118 \class QWindowsMouseHandler
119 \brief Windows mouse handler
121 Dispatches mouse and touch events. Separate for code cleanliness.
123 \ingroup qt-lighthouse-win
126 QWindowsMouseHandler::QWindowsMouseHandler() :
127 m_windowUnderMouse(0)
131 bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
132 QtWindows::WindowsEventType et,
133 MSG msg, LRESULT *result)
135 if (et & QtWindows::NonClientEventFlag)
137 if (et == QtWindows::MouseWheelEvent)
138 return translateMouseWheelEvent(window, hwnd, msg, result);
140 if (msg.message == WM_MOUSELEAVE) {
141 // When moving out of a child, MouseMove within parent is received first
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;
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
157 if (m_windowUnderMouse) {
158 if (QWindowsContext::verboseEvents)
159 qDebug() << "Synthetic leave for " << m_windowUnderMouse;
160 QWindowSystemInterface::handleLeaveEvent(m_windowUnderMouse);
162 m_windowUnderMouse = window;
163 if (QWindowsContext::verboseEvents)
164 qDebug() << "Entering " << window;
165 QWindowsWindow::baseWindowOf(window)->applyCursor();
166 QWindowSystemInterface::handleEnterEvent(window);
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.");
175 QWindowSystemInterface::handleMouseEvent(window, client,
176 QWindowsGeometryHint::mapToGlobal(hwnd, client),
177 keyStateToMouseButtons((int)msg.wParam));
181 bool QWindowsMouseHandler::translateMouseWheelEvent(QWindow *window, HWND,
184 const Qt::MouseButtons buttons = keyStateToMouseButtons((int)msg.wParam);
186 if (msg.message == WM_MOUSEWHEEL || msg.message == WM_MOUSEHWHEEL)
187 delta = (short) HIWORD (msg.wParam);
189 delta = (int) msg.wParam;
191 Qt::Orientation orientation = (msg.message == WM_MOUSEHWHEEL
192 || (buttons & Qt::AltModifier)) ?
193 Qt::Horizontal : Qt::Vertical;
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)
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),
217 // from bool QApplicationPrivate::translateTouchEvent()
218 bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND,
219 QtWindows::WindowsEventType,
222 typedef QWindowSystemInterface::TouchPoint QTouchPoint;
223 typedef QList<QWindowSystemInterface::TouchPoint> QTouchPointList;
225 const QRect screenGeometry = window->screen()->geometry();
227 const int winTouchPointCount = msg.wParam;
228 QScopedArrayPointer<TOUCHINPUT> winTouchInputs(new TOUCHINPUT[winTouchPointCount]);
229 memset(winTouchInputs.data(), 0, sizeof(TOUCHINPUT) * winTouchPointCount);
231 QTouchPointList touchPoints;
232 touchPoints.reserve(winTouchPointCount);
233 Qt::TouchPointStates allStates = 0;
235 Q_ASSERT(QWindowsContext::user32dll.getTouchInputInfo);
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);
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);
255 if (winTouchInput.dwFlags & TOUCHEVENTF_DOWN) {
256 touchPoint.state = Qt::TouchPointPressed;
257 } else if (winTouchInput.dwFlags & TOUCHEVENTF_UP) {
258 touchPoint.state = Qt::TouchPointReleased;
260 // TODO: Previous code checked"
261 // screenPos == touchPoint.normalPosition -> Qt::TouchPointStationary, but
262 // but touchPoint.normalPosition was never initialized?
263 touchPoint.state = touchPoint.state;
266 touchPoint.normalPosition = QPointF(screenPos.x() / screenGeometry.width(),
267 screenPos.y() / screenGeometry.height());
269 allStates |= touchPoint.state;
271 touchPoints.append(touchPoint);
274 QWindowsContext::user32dll.closeTouchInputHandle((HANDLE) msg.lParam);
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();
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,