Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qgeopositioninfosource_winrt.cpp
Go to the documentation of this file.
1// Copyright (C) 2015 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <QtCore/qcoreapplication.h>
7#include <QtCore/qdatetime.h>
8#include <QtCore/private/qfunctions_winrt_p.h>
9#include <QtCore/qloggingcategory.h>
10#include <QtCore/qmutex.h>
11#include <QtCore/qtimezone.h>
12#ifdef Q_OS_WINRT
13#include <QtCore/private/qeventdispatcher_winrt_p.h>
14#endif
15
16#include <functional>
17#include <windows.system.h>
18#include <windows.devices.geolocation.h>
19#include <windows.foundation.h>
20#include <windows.foundation.collections.h>
21
22using namespace Microsoft::WRL;
23using namespace Microsoft::WRL::Wrappers;
25using namespace ABI::Windows::Foundation;
26using namespace ABI::Windows::Foundation::Collections;
27
28typedef ITypedEventHandler<Geolocator *, PositionChangedEventArgs *> GeoLocatorPositionHandler;
29typedef ITypedEventHandler<Geolocator *, StatusChangedEventArgs *> GeoLocatorStatusHandler;
30typedef IAsyncOperationCompletedHandler<Geoposition*> PositionHandler;
31typedef IAsyncOperationCompletedHandler<GeolocationAccessStatus> AccessHandler;
32
33Q_DECLARE_LOGGING_CATEGORY(lcPositioningWinRT)
34
36
38
39#ifndef Q_OS_WINRT
41HRESULT runOnXamlThread(const std::function<HRESULT ()> &delegate, bool waitForRun = true)
42{
43 Q_UNUSED(waitForRun);
44 return delegate();
45}
46}
47
49 GeolocationAccessStatus *result)
50{
51 ComPtr<IAsyncInfo> asyncInfo;
52 HRESULT hr = asyncOp.As(&asyncInfo);
53 if (FAILED(hr))
54 return hr;
55
56 AsyncStatus status;
57 while (SUCCEEDED(hr = asyncInfo->get_Status(&status)) && status == AsyncStatus::Started)
59
60 if (FAILED(hr) || status != AsyncStatus::Completed) {
61 HRESULT ec;
62 hr = asyncInfo->get_ErrorCode(&ec);
63 if (FAILED(hr))
64 return hr;
65 hr = asyncInfo->Close();
66 if (FAILED(hr))
67 return hr;
68 return ec;
69 }
70
71 if (FAILED(hr))
72 return hr;
73
74 return asyncOp->GetResults(result);
75}
76#endif // !Q_OS_WINRT
77
82};
83
85public:
92 EventRegistrationToken statusToken;
93 EventRegistrationToken positionToken;
95 bool updatesOngoing = false;
99
100 PositionStatus positionStatus = PositionStatus_NotInitialized;
101};
102
106{
107 qRegisterMetaType<QGeoPositionInfoSource::Error>();
108 qCDebug(lcPositioningWinRT) << __FUNCTION__;
109 CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
111 d->positionError = QGeoPositionInfoSource::NoError;
112 d->updatesOngoing = false;
113 d->positionToken.value = 0;
114 d->statusToken.value = 0;
115}
116
118{
119 qCDebug(lcPositioningWinRT) << __FUNCTION__;
120 CoUninitialize();
121}
122
124{
127 if (d->initState == InitializationState::Initialized)
128 return 0;
129
130 qCDebug(lcPositioningWinRT) << __FUNCTION__;
132 if (!requestAccess()) {
135 qWarning ("Location access failed.");
136 return -1;
137 }
139 HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Devices_Geolocation_Geolocator).Get(),
140 &d->locator);
141 RETURN_HR_IF_FAILED("Could not initialize native location services.");
142
143 if (d->minimumUpdateInterval == -1) {
144 UINT32 interval;
145 hr = d->locator->get_ReportInterval(&interval);
146 RETURN_HR_IF_FAILED("Could not retrieve report interval.");
147 d->minimumUpdateInterval = static_cast<int>(interval);
148 }
149 if (d->updateInterval == -1)
150 d->updateInterval = d->minimumUpdateInterval;
151 setUpdateInterval(d->updateInterval);
152
153 return hr;
154 });
155 if (FAILED(hr)) {
158 return -1;
159 }
160
161 d->periodicTimer.setSingleShot(true);
162 connect(&d->periodicTimer, &QTimer::timeout, this, &QGeoPositionInfoSourceWinRT::virtualPositionUpdate);
163
164 d->singleUpdateTimer.setSingleShot(true);
165 connect(&d->singleUpdateTimer, &QTimer::timeout, this, &QGeoPositionInfoSourceWinRT::singleUpdateTimeOut);
166
167 QGeoPositionInfoSource::PositioningMethods preferredMethods = preferredPositioningMethods();
168 if (preferredMethods == QGeoPositionInfoSource::NoPositioningMethods)
170 setPreferredPositioningMethods(preferredMethods);
171
172 connect(this, &QGeoPositionInfoSourceWinRT::nativePositionUpdate, this, &QGeoPositionInfoSourceWinRT::updateSynchronized);
174 return 0;
175}
176
177QGeoPositionInfo QGeoPositionInfoSourceWinRT::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const
178{
179 qCDebug(lcPositioningWinRT) << __FUNCTION__;
181 Q_UNUSED(fromSatellitePositioningMethodsOnly);
182 return d->lastPosition;
183}
184
185QGeoPositionInfoSource::PositioningMethods QGeoPositionInfoSourceWinRT::supportedPositioningMethods() const
186{
189}
190
191void QGeoPositionInfoSourceWinRT::setPreferredPositioningMethods(QGeoPositionInfoSource::PositioningMethods methods)
192{
193 qCDebug(lcPositioningWinRT) << __FUNCTION__ << methods;
195
196 PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods();
198 if (previousPreferredPositioningMethods == preferredPositioningMethods()
199 || d->initState == InitializationState::Uninitialized) {
200 return;
201 }
202
203 const bool needsRestart = d->positionToken.value != 0 || d->statusToken.value != 0;
204
205 if (needsRestart)
206 stopHandler();
207
209 PositionAccuracy::PositionAccuracy_High :
210 PositionAccuracy::PositionAccuracy_Default;
212 return d->locator->put_DesiredAccuracy(acc);
213 });
214 RETURN_VOID_IF_FAILED("Could not set positioning accuracy.");
215
216 if (needsRestart)
217 startHandler();
218}
219
221{
222 qCDebug(lcPositioningWinRT) << __FUNCTION__ << msec;
224 if (d->initState == InitializationState::Uninitialized) {
225 d->updateInterval = msec;
226 return;
227 }
228
229 // minimumUpdateInterval is initialized to the lowest possible update interval in init().
230 // Passing 0 will cause an error on Windows 10.
231 // See https://docs.microsoft.com/en-us/uwp/api/windows.devices.geolocation.geolocator.reportinterval
232 if (msec != 0 && msec < minimumUpdateInterval())
233 msec = minimumUpdateInterval();
234
235 const bool needsRestart = d->positionToken.value != 0 || d->statusToken.value != 0;
236
237 if (needsRestart)
238 stopHandler();
239
240 HRESULT hr = d->locator->put_ReportInterval(static_cast<UINT32>(msec));
241 if (FAILED(hr)) {
243 qErrnoWarning(hr, "Failed to set update interval");
244 return;
245 }
246
247 d->updateInterval = msec;
248 d->periodicTimer.setInterval(d->updateInterval);
249
251
252 if (needsRestart)
253 startHandler();
254}
255
257{
259 return d->minimumUpdateInterval == -1 ? 1000 : d->minimumUpdateInterval;
260}
261
263{
264 qCDebug(lcPositioningWinRT) << __FUNCTION__;
266
268 if (init() < 0)
269 return;
270
271 if (d->updatesOngoing)
272 return;
273
274 if (!startHandler())
275 return;
276 d->updatesOngoing = true;
277 d->periodicTimer.start();
278}
279
281{
282 qCDebug(lcPositioningWinRT) << __FUNCTION__;
284
285 if (init() < 0)
286 return;
287
288 stopHandler();
289 d->updatesOngoing = false;
290 d->periodicTimer.stop();
291}
292
293bool QGeoPositionInfoSourceWinRT::startHandler()
294{
295 qCDebug(lcPositioningWinRT) << __FUNCTION__;
297
298 // Check if already attached
299 if (d->positionToken.value != 0)
300 return true;
301
304 return false;
305 }
306
307 if (!requestAccess()) {
309 return false;
310 }
311
313 HRESULT hr;
314
315 // We need to call this at least once on Windows 10 Mobile.
316 // Unfortunately this operation does not have a completion handler
317 // registered. That could have helped in the single update case
319 hr = d->locator->GetGeopositionAsync(&op);
320 RETURN_HR_IF_FAILED("Could not start position operation");
321
322 hr = d->locator->add_PositionChanged(Callback<GeoLocatorPositionHandler>(this,
324 &d->positionToken);
325 RETURN_HR_IF_FAILED("Could not add position handler");
326
327 hr = d->locator->add_StatusChanged(Callback<GeoLocatorStatusHandler>(this,
329 &d->statusToken);
330 RETURN_HR_IF_FAILED("Could not add status handler");
331 return hr;
332 });
333 if (FAILED(hr)) {
335 return false;
336 }
337
338 return true;
339}
340
341void QGeoPositionInfoSourceWinRT::stopHandler()
342{
343 qCDebug(lcPositioningWinRT) << __FUNCTION__;
345
346 if (!d->positionToken.value)
347 return;
349 d->locator->remove_PositionChanged(d->positionToken);
350 d->locator->remove_StatusChanged(d->statusToken);
351 return S_OK;
352 });
353 d->positionToken.value = 0;
354 d->statusToken.value = 0;
355}
356
358{
359 qCDebug(lcPositioningWinRT) << __FUNCTION__ << timeout;
361
362 if (init() < 0)
363 return;
364
366 if (timeout != 0 && timeout < minimumUpdateInterval()) {
369 return;
370 }
371
372 if (timeout == 0)
373 timeout = 2*60*1000; // Maximum time for cold start (see Android)
374
375 startHandler();
376 d->singleUpdateTimer.start(timeout);
377}
378
379void QGeoPositionInfoSourceWinRT::virtualPositionUpdate()
380{
381 qCDebug(lcPositioningWinRT) << __FUNCTION__;
383 QMutexLocker locker(&d->mutex);
384
385 // The operating system did not provide information in time
386 // Hence we send a virtual position update to keep same behavior
387 // between backends.
388 // This only applies to the periodic timer, not for single requests
389 // We can only do this if we received a valid position before
390 if (d->lastPosition.isValid()) {
391 QGeoPositionInfo sent = d->lastPosition;
393 d->lastPosition = sent;
394 emit positionUpdated(sent);
395 }
396 d->periodicTimer.start();
397}
398
399void QGeoPositionInfoSourceWinRT::singleUpdateTimeOut()
400{
402 QMutexLocker locker(&d->mutex);
403
404 if (d->singleUpdateTimer.isActive()) {
407 if (!d->updatesOngoing)
408 stopHandler();
409 }
410}
411
412void QGeoPositionInfoSourceWinRT::updateSynchronized(QGeoPositionInfo currentInfo)
413{
414 qCDebug(lcPositioningWinRT) << __FUNCTION__ << currentInfo;
416 QMutexLocker locker(&d->mutex);
417
418 d->periodicTimer.stop();
419 d->lastPosition = currentInfo;
420
421 if (d->updatesOngoing)
422 d->periodicTimer.start();
423
424 if (d->singleUpdateTimer.isActive()) {
425 d->singleUpdateTimer.stop();
426 if (!d->updatesOngoing)
427 stopHandler();
428 }
429
430 emit positionUpdated(currentInfo);
431}
432
434{
436 qCDebug(lcPositioningWinRT) << __FUNCTION__ << d->positionError;
437
438 // If the last encountered error was "Access denied", it is possible that the location service
439 // has been enabled by now so that we are clear again.
440 if ((d->positionError == QGeoPositionInfoSource::AccessError
443
444 return d->positionError;
445}
446
447void QGeoPositionInfoSourceWinRT::setError(QGeoPositionInfoSource::Error positionError)
448{
450
451 if (positionError == d->positionError)
452 return;
453
454 qCDebug(lcPositioningWinRT) << __FUNCTION__ << positionError;
455 d->positionError = positionError;
456 if (positionError != QGeoPositionInfoSource::NoError)
458}
459
460void QGeoPositionInfoSourceWinRT::reactOnError(QGeoPositionInfoSource::Error positionError)
461{
462 setError(positionError);
463 stopUpdates();
464}
465
466HRESULT QGeoPositionInfoSourceWinRT::onPositionChanged(IGeolocator *locator, IPositionChangedEventArgs *args)
467{
468 qCDebug(lcPositioningWinRT) << __FUNCTION__;
469 Q_UNUSED(locator);
470
471 HRESULT hr;
473 hr = args->get_Position(&position);
474 RETURN_HR_IF_FAILED("Could not access position object.");
475
476 QGeoPositionInfo currentInfo;
477
479 hr = position->get_Coordinate(&coord);
480 if (FAILED(hr))
481 qErrnoWarning(hr, "Could not access coordinate");
482
483 ComPtr<IGeocoordinateWithPoint> pointCoordinate;
484 hr = coord.As(&pointCoordinate);
485 if (FAILED(hr))
486 qErrnoWarning(hr, "Could not cast coordinate.");
487
488 ComPtr<IGeopoint> point;
489 hr = pointCoordinate->get_Point(&point);
490 if (FAILED(hr))
491 qErrnoWarning(hr, "Could not obtain coordinate's point.");
492
493 BasicGeoposition pos;
494 hr = point->get_Position(&pos);
495 if (FAILED(hr))
496 qErrnoWarning(hr, "Could not obtain point's position.");
497
498 DOUBLE lat = pos.Latitude;
499 DOUBLE lon = pos.Longitude;
500 DOUBLE alt = pos.Altitude;
501
502 bool altitudeAvailable = false;
503 ComPtr<IGeoshape> shape;
504 hr = point.As(&shape);
505 if (SUCCEEDED(hr) && shape) {
506 AltitudeReferenceSystem altitudeSystem;
507 hr = shape->get_AltitudeReferenceSystem(&altitudeSystem);
508 if (SUCCEEDED(hr) && altitudeSystem == AltitudeReferenceSystem_Geoid)
509 altitudeAvailable = true;
510 }
511 if (altitudeAvailable)
512 currentInfo.setCoordinate(QGeoCoordinate(lat, lon, alt));
513 else
514 currentInfo.setCoordinate(QGeoCoordinate(lat, lon));
515
516 DOUBLE accuracy;
517 hr = coord->get_Accuracy(&accuracy);
518 if (SUCCEEDED(hr))
520
521 IReference<double> *altAccuracy;
522 hr = coord->get_AltitudeAccuracy(&altAccuracy);
523 if (SUCCEEDED(hr) && altAccuracy) {
524 double value;
525 hr = altAccuracy->get_Value(&value);
527 }
528
529 IReference<double> *speed;
530 hr = coord->get_Speed(&speed);
531 if (SUCCEEDED(hr) && speed) {
532 double value;
533 hr = speed->get_Value(&value);
535 }
536
537 IReference<double> *heading;
538 hr = coord->get_Heading(&heading);
539 if (SUCCEEDED(hr) && heading) {
540 double value;
541 hr = heading->get_Value(&value);
542 double mod = 0;
543 value = modf(value, &mod);
544 value += static_cast<int>(mod) % 360;
545 if (value >=0 && value <= 359) // get_Value might return nan/-nan
547 }
548
549 DateTime dateTime;
550 hr = coord->get_Timestamp(&dateTime);
551
552 if (dateTime.UniversalTime > 0) {
553 ULARGE_INTEGER uLarge;
554 uLarge.QuadPart = dateTime.UniversalTime;
555 FILETIME fileTime;
556 fileTime.dwHighDateTime = uLarge.HighPart;
557 fileTime.dwLowDateTime = uLarge.LowPart;
558 SYSTEMTIME systemTime;
559 if (FileTimeToSystemTime(&fileTime, &systemTime)) {
560 currentInfo.setTimestamp(QDateTime(QDate(systemTime.wYear, systemTime.wMonth,
561 systemTime.wDay),
562 QTime(systemTime.wHour, systemTime.wMinute,
563 systemTime.wSecond, systemTime.wMilliseconds),
565 }
566 }
567
568 emit nativePositionUpdate(currentInfo);
569
570 return S_OK;
571}
572
573static inline bool isDisabledStatus(PositionStatus status)
574{
575 return status == PositionStatus_NoData || status == PositionStatus_Disabled
576 || status == PositionStatus_NotAvailable;
577}
578
579HRESULT QGeoPositionInfoSourceWinRT::onStatusChanged(IGeolocator *, IStatusChangedEventArgs *args)
580{
582
583 const PositionStatus oldStatus = d->positionStatus;
584 HRESULT hr = args->get_Status(&d->positionStatus);
585 RETURN_HR_IF_FAILED("Could not obtain position status");
586 qCDebug(lcPositioningWinRT) << __FUNCTION__ << d->positionStatus;
588 switch (d->positionStatus) {
589 case PositionStatus::PositionStatus_NotAvailable:
591 break;
592 case PositionStatus::PositionStatus_Disabled:
594 break;
595 case PositionStatus::PositionStatus_NoData:
597 break;
598 }
603 }
604
605 if (isDisabledStatus(oldStatus) != isDisabledStatus(d->positionStatus))
607
608 return S_OK;
609}
610
612{
614 qCDebug(lcPositioningWinRT) << __FUNCTION__;
615 GeolocationAccessStatus accessStatus;
616
618 HRESULT hr;
620 HRESULT hr;
621 if (!d->statics) {
622 hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Geolocation_Geolocator).Get(),
623 IID_PPV_ARGS(&d->statics));
624 RETURN_HR_IF_FAILED("Could not access Geolocation Statics.");
625 }
626
627 hr = d->statics->RequestAccessAsync(&op);
628 return hr;
629 });
630 if (FAILED(hr)) {
631 qCDebug(lcPositioningWinRT) << __FUNCTION__ << "Requesting access from Xaml thread failed";
632 return false;
633 }
634
635 // We cannot wait inside the XamlThread as that would deadlock
636#ifdef Q_OS_WINRT
637 QWinRTFunctions::await(op, &accessStatus);
638#else
639 await(op, &accessStatus);
640#endif
641 return accessStatus == GeolocationAccessStatus_Allowed;
642}
643
static JNINativeMethod methods[]
\inmodule QtCore\reentrant
Definition qdatetime.h:257
QDateTime addMSecs(qint64 msecs) const
Returns a QDateTime object containing a datetime msecs milliseconds later than the datetime of this o...
\inmodule QtCore \reentrant
Definition qdatetime.h:27
\inmodule QtPositioning
HRESULT onStatusChanged(ABI::Windows::Devices::Geolocation::IGeolocator *locator, ABI::Windows::Devices::Geolocation::IStatusChangedEventArgs *args)
QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly=false) const override
Returns an update containing the last known position, or a null update if none is available.
Error error() const override
Returns the type of error that last occurred.
void nativePositionUpdate(const QGeoPositionInfo)
QGeoPositionInfoSourceWinRT(QObject *parent=nullptr)
HRESULT onPositionChanged(ABI::Windows::Devices::Geolocation::IGeolocator *locator, ABI::Windows::Devices::Geolocation::IPositionChangedEventArgs *args)
void requestUpdate(int timeout=0) override
void setPreferredPositioningMethods(PositioningMethods methods) override
PositioningMethods supportedPositioningMethods() const override
Returns the positioning methods available to this source.
\inmodule QtPositioning
int updateInterval
This property holds the requested interval in milliseconds between each update.
void positionUpdated(const QGeoPositionInfo &update)
If startUpdates() or requestUpdate() is called, this signal is emitted when an update becomes availab...
void errorOccurred(QGeoPositionInfoSource::Error)
This signal is emitted after an error occurred.
virtual void setPreferredPositioningMethods(PositioningMethods methods)
void supportedPositioningMethodsChanged()
This signal is emitted when the supported positioning methods changed.
Error
The Error enumeration represents the errors which can occur.
virtual void setUpdateInterval(int msec)
PositioningMethods preferredPositioningMethods
Sets the preferred positioning methods for this source.
\inmodule QtPositioning
void setCoordinate(const QGeoCoordinate &coordinate)
Sets the coordinate for this position to coordinate.
void setAttribute(Attribute attribute, qreal value)
Sets the value for attribute to value.
void setTimestamp(const QDateTime &timestamp)
Sets the date and time at which this position was reported to timestamp.
QDateTime timestamp() const
Returns the date and time at which this position was reported, in UTC time.
\inmodule QtCore
Definition qmutex.h:317
\inmodule QtCore
Definition qmutex.h:285
\inmodule QtCore
Definition qobject.h:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
static void yieldCurrentThread()
Definition qthread.cpp:976
\inmodule QtCore \reentrant
Definition qdatetime.h:189
\inmodule QtCore
Definition qtimer.h:20
void timeout(QPrivateSignal)
This signal is emitted when the timer times out.
void qErrnoWarning(const char *msg,...)
HRESULT runOnXamlThread(const std::function< HRESULT()> &delegate, bool waitForRun=true)
Combined button and popup list for selecting options.
@ QueuedConnection
static QT_BEGIN_NAMESPACE bool await(IAsyncOperation< T > &&asyncInfo, T &result, uint timeout=0)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
IAsyncOperationCompletedHandler< Geoposition * > PositionHandler
ITypedEventHandler< Geolocator *, StatusChangedEventArgs * > GeoLocatorStatusHandler
IAsyncOperationCompletedHandler< GeolocationAccessStatus > AccessHandler
static HRESULT await(const ComPtr< IAsyncOperation< GeolocationAccessStatus > > &asyncOp, GeolocationAccessStatus *result)
ITypedEventHandler< Geolocator *, PositionChangedEventArgs * > GeoLocatorPositionHandler
static bool isDisabledStatus(PositionStatus status)
#define qWarning
Definition qlogging.h:162
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
#define Q_DECLARE_METATYPE(TYPE)
Definition qmetatype.h:1504
#define Q_ARG(Type, data)
Definition qobjectdefs.h:62
GLbitfield GLuint64 timeout
[4]
GLuint coord
GLuint64EXT * result
[6]
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
#define Q_UNUSED(x)
long HRESULT
QDateTime dateTime
[12]
QJSValueList args
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent