1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the plugins of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qwindowsmousehandler.h"
43 #include "qwindowskeymapper.h"
44 #include "qwindowscontext.h"
45 #include "qwindowswindow.h"
46 #include "qwindowsintegration.h"
48 #include <qpa/qwindowsysteminterface.h>
49 #include <QtGui/QGuiApplication>
50 #include <QtGui/QScreen>
52 #include <QtCore/QDebug>
53 #include <QtCore/QScopedArrayPointer>
59 static inline void compressMouseMove(MSG *msg)
61 // Compress mouse move events
62 if (msg->message == WM_MOUSEMOVE) {
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):
76 while (PeekMessage(&keyMsg, 0, WM_KEYFIRST, WM_KEYLAST,
78 if (keyMsg.time < mouseMsg.time) {
79 if ((keyMsg.lParam & 0xC0000000) == 0x40000000) {
80 PeekMessage(&keyMsg, 0, keyMsg.message,
81 keyMsg.message, PM_REMOVE);
87 break; // no key event before the WM_MOUSEMOVE event
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
100 // Update the passed in MSG structure with the
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);
112 break; // there was no more WM_MOUSEMOVE event
119 \class QWindowsMouseHandler
120 \brief Windows mouse handler
122 Dispatches mouse and touch events. Separate for code cleanliness.
125 \ingroup qt-lighthouse-win
128 QWindowsMouseHandler::QWindowsMouseHandler() :
129 m_windowUnderMouse(0),
134 Qt::MouseButtons QWindowsMouseHandler::queryMouseButtons()
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;
147 bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
148 QtWindows::WindowsEventType et,
149 MSG msg, LRESULT *result)
151 if (et == QtWindows::MouseWheelEvent)
152 return translateMouseWheelEvent(window, hwnd, msg, result);
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).
166 if (msg.message == WM_MOUSELEAVE) {
167 // When moving out of a child, MouseMove within parent is received first
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;
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)) {
183 while (PeekMessage(&mouseMsg, platformWindow->handle(), WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE)) ;
184 platformWindow->clearFlag(QWindowsWindow::SizeGripOperation);
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
192 if (m_windowUnderMouse) {
193 if (QWindowsContext::verboseEvents)
194 qDebug() << "Synthetic leave for " << m_windowUnderMouse;
195 QWindowSystemInterface::handleLeaveEvent(m_windowUnderMouse);
197 m_windowUnderMouse = window;
198 if (QWindowsContext::verboseEvents)
199 qDebug() << "Entering " << window;
200 QWindowsWindow::baseWindowOf(window)->applyCursor();
202 QWindowSystemInterface::handleEnterEvent(window);
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
213 const QPoint clientPosition = winEventPosition;
214 QWindowSystemInterface::handleMouseEvent(window, clientPosition,
215 QWindowsGeometryHint::mapToGlobal(hwnd, clientPosition),
216 keyStateToMouseButtons((int)msg.wParam),
217 QWindowsKeyMapper::queryKeyboardModifiers());
221 bool QWindowsMouseHandler::translateMouseWheelEvent(QWindow *window, HWND,
224 const Qt::MouseButtons buttons = keyStateToMouseButtons((int)msg.wParam);
225 const Qt::KeyboardModifiers mods = keyStateToModifiers((int)msg.wParam);
228 if (msg.message == WM_MOUSEWHEEL || msg.message == WM_MOUSEHWHEEL)
229 delta = (short) HIWORD (msg.wParam);
231 delta = (int) msg.wParam;
233 Qt::Orientation orientation = (msg.message == WM_MOUSEHWHEEL
234 || (buttons & Qt::AltModifier)) ?
235 Qt::Horizontal : Qt::Vertical;
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)
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),
255 delta, orientation, mods);
259 // from bool QApplicationPrivate::translateTouchEvent()
260 bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND,
261 QtWindows::WindowsEventType,
265 typedef QWindowSystemInterface::TouchPoint QTouchPoint;
266 typedef QList<QWindowSystemInterface::TouchPoint> QTouchPointList;
268 const QRect screenGeometry = window->screen()->geometry();
270 const int winTouchPointCount = msg.wParam;
271 QScopedArrayPointer<TOUCHINPUT> winTouchInputs(new TOUCHINPUT[winTouchPointCount]);
272 memset(winTouchInputs.data(), 0, sizeof(TOUCHINPUT) * winTouchPointCount);
274 QTouchPointList touchPoints;
275 touchPoints.reserve(winTouchPointCount);
276 Qt::TouchPointStates allStates = 0;
278 Q_ASSERT(QWindowsContext::user32dll.getTouchInputInfo);
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);
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);
297 if (winTouchInput.dwFlags & TOUCHEVENTF_DOWN) {
298 touchPoint.state = Qt::TouchPointPressed;
299 } else if (winTouchInput.dwFlags & TOUCHEVENTF_UP) {
300 touchPoint.state = Qt::TouchPointReleased;
302 // TODO: Previous code checked"
303 // screenPos == touchPoint.normalPosition -> Qt::TouchPointStationary, but
304 // but touchPoint.normalPosition was never initialized?
305 touchPoint.state = touchPoint.state;
308 touchPoint.normalPosition = QPointF(screenPos.x() / screenGeometry.width(),
309 screenPos.y() / screenGeometry.height());
311 allStates |= touchPoint.state;
313 touchPoints.append(touchPoint);
316 QWindowsContext::user32dll.closeTouchInputHandle((HANDLE) msg.lParam);
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();
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);
330 QWindowSystemInterface::handleTouchEvent(window,