1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins 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 https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://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 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qwinrtcursor.h"
41 #include "qwinrtscreen.h"
42 #include <private/qeventdispatcher_winrt_p.h>
43 
44 #include <QtCore/qfunctions_winrt.h>
45 #include <QtGui/QGuiApplication>
46 #include <QtGui/QScreen>
47 
48 #include <functional>
49 #include <wrl.h>
50 #include <windows.ui.core.h>
51 #include <windows.foundation.h>
52 using namespace Microsoft::WRL;
53 using namespace Microsoft::WRL::Wrappers;
54 using namespace ABI::Windows::UI::Core;
55 using namespace ABI::Windows::Foundation;
56 
57 QT_BEGIN_NAMESPACE
58 
qIsPointInRect(const Point & p,const Rect & r)59 static inline bool qIsPointInRect(const Point &p, const Rect &r)
60 {
61     return (p.X >= r.X && p.Y >= r.Y && p.X < r.X + r.Width && p.Y < r.Y + r.Height);
62 }
63 
64 class QWinRTCursorPrivate
65 {
66 public:
67     ComPtr<ICoreCursorFactory> cursorFactory;
68 };
69 
QWinRTCursor()70 QWinRTCursor::QWinRTCursor()
71   : d_ptr(new QWinRTCursorPrivate)
72 {
73     Q_D(QWinRTCursor);
74 
75     HRESULT hr;
76     hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_Core_CoreCursor).Get(),
77                                 IID_PPV_ARGS(&d->cursorFactory));
78     Q_ASSERT_SUCCEEDED(hr);
79 }
80 
81 #ifndef QT_NO_CURSOR
changeCursor(QCursor * windowCursor,QWindow * window)82 void QWinRTCursor::changeCursor(QCursor *windowCursor, QWindow *window)
83 {
84     Q_D(QWinRTCursor);
85 
86     HRESULT hr;
87     ICoreWindow *coreWindow = static_cast<QWinRTScreen *>(window->screen()->handle())->coreWindow();
88 
89     CoreCursorType type;
90     switch (windowCursor ? windowCursor->shape() : Qt::ArrowCursor) {
91     case Qt::BlankCursor:
92         hr = QEventDispatcherWinRT::runOnXamlThread([coreWindow]() {
93             coreWindow->put_PointerCursor(nullptr);
94             return S_OK;
95         });
96         RETURN_VOID_IF_FAILED("Failed to set blank native cursor");
97         return;
98     default:
99     case Qt::OpenHandCursor:
100     case Qt::ClosedHandCursor:
101     case Qt::DragCopyCursor:
102     case Qt::DragMoveCursor:
103     case Qt::DragLinkCursor:
104         // (unavailable)
105     case Qt::ArrowCursor:
106         type = CoreCursorType_Arrow;
107         break;
108     case Qt::UpArrowCursor:
109         type = CoreCursorType_UpArrow;
110         break;
111     case Qt::CrossCursor:
112         type = CoreCursorType_Cross;
113         break;
114     case Qt::WaitCursor:
115     case Qt::BusyCursor:
116         type = CoreCursorType_Wait;
117         break;
118     case Qt::IBeamCursor:
119         type = CoreCursorType_IBeam;
120         break;
121     case Qt::SizeVerCursor:
122     case Qt::SplitVCursor:
123         type = CoreCursorType_SizeNorthSouth;
124         break;
125     case Qt::SizeHorCursor:
126     case Qt::SplitHCursor:
127         type = CoreCursorType_SizeWestEast;
128         break;
129     case Qt::SizeBDiagCursor:
130         type = CoreCursorType_SizeNortheastSouthwest;
131         break;
132     case Qt::SizeFDiagCursor:
133         type = CoreCursorType_SizeNorthwestSoutheast;
134         break;
135     case Qt::SizeAllCursor:
136         type = CoreCursorType_SizeAll;
137         break;
138     case Qt::PointingHandCursor:
139         type = CoreCursorType_Hand;
140         break;
141     case Qt::ForbiddenCursor:
142         type = CoreCursorType_UniversalNo;
143         break;
144     case Qt::WhatsThisCursor:
145         type = CoreCursorType_Help;
146         break;
147     case Qt::BitmapCursor:
148     case Qt::CustomCursor:
149         // TODO: figure out if arbitrary bitmaps can be made into resource IDs
150         // For now, we don't get enough info from QCursor to set a custom cursor
151         type = CoreCursorType_Custom;
152         break;
153     }
154 
155     ComPtr<ICoreCursor> cursor;
156     hr = d->cursorFactory->CreateCursor(type, 0, &cursor);
157     RETURN_VOID_IF_FAILED("Failed to create native cursor.");
158 
159     hr = QEventDispatcherWinRT::runOnXamlThread([coreWindow, &cursor]() {
160         return coreWindow->put_PointerCursor(cursor.Get());
161     });
162     RETURN_VOID_IF_FAILED("Failed to set native cursor");
163 }
164 #endif // QT_NO_CURSOR
165 
pos() const166 QPoint QWinRTCursor::pos() const
167 {
168     const QWinRTScreen *screen = static_cast<QWinRTScreen *>(QGuiApplication::primaryScreen()->handle());
169     Q_ASSERT(screen);
170     ICoreWindow *coreWindow = screen->coreWindow();
171     Q_ASSERT(coreWindow);
172     Point point;
173     Rect bounds;
174     HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([coreWindow, &point, &bounds]() {
175         HRESULT hr = coreWindow->get_PointerPosition(&point);
176         RETURN_HR_IF_FAILED("Failed to obtain pointer position.");
177         hr = coreWindow->get_Bounds(&bounds);
178         RETURN_HR_IF_FAILED("Failed to obtain window bounds.");
179         return hr;
180     });
181     Q_ASSERT_SUCCEEDED(hr);
182     QPointF position(qreal(point.X), qreal(point.Y));
183     // If no cursor get_PointerPosition returns SHRT_MIN for x and y
184     if ((int(position.x()) == SHRT_MIN && int(position.y()) == SHRT_MIN)
185             || FAILED(hr))
186         return QPointF(Q_INFINITY, Q_INFINITY).toPoint();
187     position.rx() -= qreal(bounds.X);
188     position.ry() -= qreal(bounds.Y);
189     position *= screen->scaleFactor();
190     return position.toPoint();
191 }
192 
setPos(const QPoint & pos)193 void QWinRTCursor::setPos(const QPoint &pos)
194 {
195     QWinRTScreen *screen = static_cast<QWinRTScreen *>(QGuiApplication::primaryScreen()->handle());
196     Q_ASSERT(screen);
197     ComPtr<ICoreWindow> coreWindow = screen->coreWindow();
198     Q_ASSERT(coreWindow);
199     const QPointF scaledPos = QPointF(pos) / screen->scaleFactor();
200     QWinRTScreen::MousePositionTransition t;
201     HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([coreWindow, scaledPos, &t]() {
202         ComPtr<ICoreWindow2> coreWindow2;
203         HRESULT hr = coreWindow.As(&coreWindow2);
204         RETURN_HR_IF_FAILED("Failed to cast core window.");
205         Rect bounds;
206         hr = coreWindow->get_Bounds(&bounds);
207         RETURN_HR_IF_FAILED("Failed to obtain window bounds.");
208         Point mousePos;
209         hr = coreWindow->get_PointerPosition(&mousePos);
210         RETURN_HR_IF_FAILED("Failed to obtain mouse position.");
211         const Point p = { FLOAT(scaledPos.x()) + bounds.X,
212                           FLOAT(scaledPos.y()) + bounds.Y };
213         const bool wasInWindow = qIsPointInRect(mousePos, bounds);
214         const bool willBeInWindow = qIsPointInRect(p, bounds);
215         if (wasInWindow && willBeInWindow)
216             t = QWinRTScreen::MousePositionTransition::StayedIn;
217         else if (wasInWindow && !willBeInWindow)
218             t = QWinRTScreen::MousePositionTransition::MovedOut;
219         else if (!wasInWindow && willBeInWindow)
220             t = QWinRTScreen::MousePositionTransition::MovedIn;
221         else
222             t = QWinRTScreen::MousePositionTransition::StayedOut;
223         return coreWindow2->put_PointerPosition(p);
224     });
225     RETURN_VOID_IF_FAILED("Failed to set cursor position");
226     screen->emulateMouseMove(scaledPos, t);
227 }
228 
229 QT_END_NAMESPACE
230 
231