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