| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the plugins of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qwinrtscreen.h" |
| |
| #include "qwinrtbackingstore.h" |
| #include "qwinrtinputcontext.h" |
| #include "qwinrtcursor.h" |
| #if QT_CONFIG(draganddrop) |
| #include "qwinrtdrag.h" |
| #endif |
| #include "qwinrtwindow.h" |
| #include "qwinrtcanvas.h" |
| #include <private/qeventdispatcher_winrt_p.h> |
| #include <private/qhighdpiscaling_p.h> |
| |
| #include <QtCore/qdebug.h> |
| #include <QtCore/QLoggingCategory> |
| #include <QtGui/QSurfaceFormat> |
| #include <QtGui/QGuiApplication> |
| #include <qpa/qwindowsysteminterface.h> |
| #include <QtCore/qt_windows.h> |
| #include <QtCore/qfunctions_winrt.h> |
| |
| #include <functional> |
| #include <wrl.h> |
| #include <windows.system.h> |
| #include <Windows.ApplicationModel.h> |
| #include <Windows.ApplicationModel.core.h> |
| #include <windows.devices.input.h> |
| #include <windows.ui.h> |
| #include <windows.ui.core.h> |
| #include <windows.ui.input.h> |
| #include <windows.ui.xaml.h> |
| #include <windows.ui.viewmanagement.h> |
| #include <windows.graphics.display.h> |
| #include <windows.foundation.h> |
| |
| using namespace Microsoft::WRL; |
| using namespace Microsoft::WRL::Wrappers; |
| using namespace ABI::Windows::ApplicationModel; |
| using namespace ABI::Windows::ApplicationModel::Core; |
| using namespace ABI::Windows::Foundation; |
| using namespace ABI::Windows::System; |
| using namespace ABI::Windows::UI; |
| using namespace ABI::Windows::UI::Core; |
| using namespace ABI::Windows::UI::Input; |
| using namespace ABI::Windows::UI::ViewManagement; |
| using namespace ABI::Windows::Devices::Input; |
| using namespace ABI::Windows::Graphics::Display; |
| |
| typedef ITypedEventHandler<CoreWindow*, WindowActivatedEventArgs*> ActivatedHandler; |
| typedef ITypedEventHandler<CoreWindow*, CoreWindowEventArgs*> ClosedHandler; |
| typedef ITypedEventHandler<CoreWindow*, CharacterReceivedEventArgs*> CharacterReceivedHandler; |
| typedef ITypedEventHandler<CoreWindow*, InputEnabledEventArgs*> InputEnabledHandler; |
| typedef ITypedEventHandler<CoreWindow*, KeyEventArgs*> KeyHandler; |
| typedef ITypedEventHandler<CoreWindow*, PointerEventArgs*> PointerHandler; |
| typedef ITypedEventHandler<CoreWindow*, VisibilityChangedEventArgs*> VisibilityChangedHandler; |
| typedef ITypedEventHandler<DisplayInformation*, IInspectable*> DisplayInformationHandler; |
| typedef ITypedEventHandler<ICorePointerRedirector*, PointerEventArgs*> RedirectHandler; |
| typedef ITypedEventHandler<ApplicationView*, IInspectable*> VisibleBoundsChangedHandler; |
| |
| QT_BEGIN_NAMESPACE |
| |
| Q_LOGGING_CATEGORY(lcQpaEvents, "qt.qpa.events") |
| |
| #if !defined(QT_NO_DEBUG_STREAM) |
| QDebug operator<<(QDebug dbg, QWinRTScreen::MousePositionTransition transition) |
| { |
| QDebugStateSaver saver(dbg); |
| dbg.nospace() << "QWinRTScreen::MousePositionTransition::"; |
| switch (transition) { |
| case QWinRTScreen::MousePositionTransition::MovedOut: |
| dbg << "MovedOut"; |
| break; |
| case QWinRTScreen::MousePositionTransition::MovedIn: |
| dbg << "MovedIn"; |
| break; |
| case QWinRTScreen::MousePositionTransition::StayedOut: |
| dbg << "StayedOut"; |
| break; |
| case QWinRTScreen::MousePositionTransition::StayedIn: |
| dbg << "StayedIn"; |
| break; |
| } |
| return dbg; |
| } |
| #endif |
| |
| struct KeyInfo { |
| KeyInfo() |
| { |
| } |
| |
| KeyInfo(quint32 virtualKey) |
| : virtualKey(virtualKey) |
| { |
| } |
| |
| QString text; |
| quint32 virtualKey{0}; |
| bool isAutoRepeat{false}; |
| }; |
| |
| static inline Qt::ScreenOrientations qtOrientationsFromNative(DisplayOrientations native) |
| { |
| Qt::ScreenOrientations orientations = Qt::PrimaryOrientation; |
| if (native & DisplayOrientations_Portrait) |
| orientations |= Qt::PortraitOrientation; |
| if (native & DisplayOrientations_PortraitFlipped) |
| orientations |= Qt::InvertedPortraitOrientation; |
| if (native & DisplayOrientations_Landscape) |
| orientations |= Qt::LandscapeOrientation; |
| if (native & DisplayOrientations_LandscapeFlipped) |
| orientations |= Qt::InvertedLandscapeOrientation; |
| return orientations; |
| } |
| |
| static inline DisplayOrientations nativeOrientationsFromQt(Qt::ScreenOrientations orientation) |
| { |
| DisplayOrientations native = DisplayOrientations_None; |
| if (orientation & Qt::PortraitOrientation) |
| native |= DisplayOrientations_Portrait; |
| if (orientation & Qt::InvertedPortraitOrientation) |
| native |= DisplayOrientations_PortraitFlipped; |
| if (orientation & Qt::LandscapeOrientation) |
| native |= DisplayOrientations_Landscape; |
| if (orientation & Qt::InvertedLandscapeOrientation) |
| native |= DisplayOrientations_LandscapeFlipped; |
| return native; |
| } |
| |
| static inline bool qIsNonPrintable(quint32 keyCode) |
| { |
| switch (keyCode) { |
| case '\b': |
| case '\n': |
| case '\t': |
| case '\r': |
| case '\v': |
| case '\f': |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| // Return Qt meta key from VirtualKey |
| static inline Qt::Key qKeyFromVirtual(VirtualKey key) |
| { |
| switch (key) { |
| |
| default: |
| return Qt::Key_unknown; |
| |
| // Non-printable characters |
| case VirtualKey_Enter: |
| return Qt::Key_Enter; |
| case VirtualKey_Tab: |
| return Qt::Key_Tab; |
| case VirtualKey_Back: |
| return Qt::Key_Backspace; |
| |
| // Modifiers |
| case VirtualKey_Shift: |
| case VirtualKey_LeftShift: |
| case VirtualKey_RightShift: |
| return Qt::Key_Shift; |
| case VirtualKey_Control: |
| case VirtualKey_LeftControl: |
| case VirtualKey_RightControl: |
| return Qt::Key_Control; |
| case VirtualKey_Menu: |
| case VirtualKey_LeftMenu: |
| case VirtualKey_RightMenu: |
| return Qt::Key_Alt; |
| case VirtualKey_LeftWindows: |
| case VirtualKey_RightWindows: |
| return Qt::Key_Meta; |
| |
| // Toggle keys |
| case VirtualKey_CapitalLock: |
| return Qt::Key_CapsLock; |
| case VirtualKey_NumberKeyLock: |
| return Qt::Key_NumLock; |
| case VirtualKey_Scroll: |
| return Qt::Key_ScrollLock; |
| |
| // East-Asian language keys |
| case VirtualKey_Kana: |
| //case VirtualKey_Hangul: // Same enum as Kana |
| return Qt::Key_Kana_Shift; |
| case VirtualKey_Junja: |
| return Qt::Key_Hangul_Jeonja; |
| case VirtualKey_Kanji: |
| //case VirtualKey_Hanja: // Same enum as Kanji |
| return Qt::Key_Kanji; |
| case VirtualKey_ModeChange: |
| return Qt::Key_Mode_switch; |
| case VirtualKey_Convert: |
| return Qt::Key_Henkan; |
| case VirtualKey_NonConvert: |
| return Qt::Key_Muhenkan; |
| |
| // Misc. keys |
| case VirtualKey_Cancel: |
| return Qt::Key_Cancel; |
| case VirtualKey_Clear: |
| return Qt::Key_Clear; |
| case VirtualKey_Application: |
| return Qt::Key_ApplicationLeft; |
| case VirtualKey_Sleep: |
| return Qt::Key_Sleep; |
| case VirtualKey_Pause: |
| return Qt::Key_Pause; |
| case VirtualKey_PageUp: |
| return Qt::Key_PageUp; |
| case VirtualKey_PageDown: |
| return Qt::Key_PageDown; |
| case VirtualKey_End: |
| return Qt::Key_End; |
| case VirtualKey_Home: |
| return Qt::Key_Home; |
| case VirtualKey_Left: |
| return Qt::Key_Left; |
| case VirtualKey_Up: |
| return Qt::Key_Up; |
| case VirtualKey_Right: |
| return Qt::Key_Right; |
| case VirtualKey_Down: |
| return Qt::Key_Down; |
| case VirtualKey_Select: |
| return Qt::Key_Select; |
| case VirtualKey_Print: |
| return Qt::Key_Print; |
| case VirtualKey_Execute: |
| return Qt::Key_Execute; |
| case VirtualKey_Insert: |
| return Qt::Key_Insert; |
| case VirtualKey_Delete: |
| return Qt::Key_Delete; |
| case VirtualKey_Help: |
| return Qt::Key_Help; |
| case VirtualKey_Snapshot: |
| return Qt::Key_Camera; |
| case VirtualKey_Escape: |
| return Qt::Key_Escape; |
| |
| // Function Keys |
| case VirtualKey_F1: |
| return Qt::Key_F1; |
| case VirtualKey_F2: |
| return Qt::Key_F2; |
| case VirtualKey_F3: |
| return Qt::Key_F3; |
| case VirtualKey_F4: |
| return Qt::Key_F4; |
| case VirtualKey_F5: |
| return Qt::Key_F5; |
| case VirtualKey_F6: |
| return Qt::Key_F6; |
| case VirtualKey_F7: |
| return Qt::Key_F7; |
| case VirtualKey_F8: |
| return Qt::Key_F8; |
| case VirtualKey_F9: |
| return Qt::Key_F9; |
| case VirtualKey_F10: |
| return Qt::Key_F10; |
| case VirtualKey_F11: |
| return Qt::Key_F11; |
| case VirtualKey_F12: |
| return Qt::Key_F12; |
| case VirtualKey_F13: |
| return Qt::Key_F13; |
| case VirtualKey_F14: |
| return Qt::Key_F14; |
| case VirtualKey_F15: |
| return Qt::Key_F15; |
| case VirtualKey_F16: |
| return Qt::Key_F16; |
| case VirtualKey_F17: |
| return Qt::Key_F17; |
| case VirtualKey_F18: |
| return Qt::Key_F18; |
| case VirtualKey_F19: |
| return Qt::Key_F19; |
| case VirtualKey_F20: |
| return Qt::Key_F20; |
| case VirtualKey_F21: |
| return Qt::Key_F21; |
| case VirtualKey_F22: |
| return Qt::Key_F22; |
| case VirtualKey_F23: |
| return Qt::Key_F23; |
| case VirtualKey_F24: |
| return Qt::Key_F24; |
| |
| // Character keys |
| case VirtualKey_Space: |
| return Qt::Key_Space; |
| case VirtualKey_Number0: |
| case VirtualKey_NumberPad0: |
| return Qt::Key_0; |
| case VirtualKey_Number1: |
| case VirtualKey_NumberPad1: |
| return Qt::Key_1; |
| case VirtualKey_Number2: |
| case VirtualKey_NumberPad2: |
| return Qt::Key_2; |
| case VirtualKey_Number3: |
| case VirtualKey_NumberPad3: |
| return Qt::Key_3; |
| case VirtualKey_Number4: |
| case VirtualKey_NumberPad4: |
| return Qt::Key_4; |
| case VirtualKey_Number5: |
| case VirtualKey_NumberPad5: |
| return Qt::Key_5; |
| case VirtualKey_Number6: |
| case VirtualKey_NumberPad6: |
| return Qt::Key_6; |
| case VirtualKey_Number7: |
| case VirtualKey_NumberPad7: |
| return Qt::Key_7; |
| case VirtualKey_Number8: |
| case VirtualKey_NumberPad8: |
| return Qt::Key_8; |
| case VirtualKey_Number9: |
| case VirtualKey_NumberPad9: |
| return Qt::Key_9; |
| case VirtualKey_A: |
| return Qt::Key_A; |
| case VirtualKey_B: |
| return Qt::Key_B; |
| case VirtualKey_C: |
| return Qt::Key_C; |
| case VirtualKey_D: |
| return Qt::Key_D; |
| case VirtualKey_E: |
| return Qt::Key_E; |
| case VirtualKey_F: |
| return Qt::Key_F; |
| case VirtualKey_G: |
| return Qt::Key_G; |
| case VirtualKey_H: |
| return Qt::Key_H; |
| case VirtualKey_I: |
| return Qt::Key_I; |
| case VirtualKey_J: |
| return Qt::Key_J; |
| case VirtualKey_K: |
| return Qt::Key_K; |
| case VirtualKey_L: |
| return Qt::Key_L; |
| case VirtualKey_M: |
| return Qt::Key_M; |
| case VirtualKey_N: |
| return Qt::Key_N; |
| case VirtualKey_O: |
| return Qt::Key_O; |
| case VirtualKey_P: |
| return Qt::Key_P; |
| case VirtualKey_Q: |
| return Qt::Key_Q; |
| case VirtualKey_R: |
| return Qt::Key_R; |
| case VirtualKey_S: |
| return Qt::Key_S; |
| case VirtualKey_T: |
| return Qt::Key_T; |
| case VirtualKey_U: |
| return Qt::Key_U; |
| case VirtualKey_V: |
| return Qt::Key_V; |
| case VirtualKey_W: |
| return Qt::Key_W; |
| case VirtualKey_X: |
| return Qt::Key_X; |
| case VirtualKey_Y: |
| return Qt::Key_Y; |
| case VirtualKey_Z: |
| return Qt::Key_Z; |
| case VirtualKey_Multiply: |
| return Qt::Key_9; |
| case VirtualKey_Add: |
| return Qt::Key_9; |
| case VirtualKey_Separator: |
| return Qt::Key_9; |
| case VirtualKey_Subtract: |
| return Qt::Key_9; |
| case VirtualKey_Decimal: |
| return Qt::Key_9; |
| case VirtualKey_Divide: |
| return Qt::Key_9; |
| |
| /* Keys with no matching Qt enum (?) |
| case VirtualKey_None: |
| case VirtualKey_LeftButton: |
| case VirtualKey_RightButton: |
| case VirtualKey_MiddleButton: |
| case VirtualKey_XButton1: |
| case VirtualKey_XButton2: |
| case VirtualKey_Final: |
| case VirtualKey_Accept:*/ |
| } |
| } |
| |
| // Some keys like modifiers, caps lock etc. should not be automatically repeated if the key is held down |
| static inline bool shouldAutoRepeat(Qt::Key key) |
| { |
| switch (key) { |
| case Qt::Key_Shift: |
| case Qt::Key_Control: |
| case Qt::Key_Alt: |
| case Qt::Key_Meta: |
| case Qt::Key_CapsLock: |
| case Qt::Key_NumLock: |
| case Qt::Key_ScrollLock: |
| return false; |
| default: |
| return true; |
| } |
| } |
| |
| static inline Qt::Key qKeyFromCode(quint32 code, int mods) |
| { |
| if (code >= 'a' && code <= 'z') |
| code = toupper(code); |
| if ((mods & Qt::ControlModifier) != 0) { |
| if (code >= 0 && code <= 31) // Ctrl+@..Ctrl+A..CTRL+Z..Ctrl+_ |
| code += '@'; // to @..A..Z.._ |
| } |
| return static_cast<Qt::Key>(code & 0xff); |
| } |
| |
| typedef HRESULT (__stdcall ICoreWindow::*CoreWindowCallbackRemover)(EventRegistrationToken); |
| uint qHash(CoreWindowCallbackRemover key) { void *ptr = *(void **)(&key); return qHash(ptr); } |
| typedef HRESULT (__stdcall IDisplayInformation::*DisplayCallbackRemover)(EventRegistrationToken); |
| uint qHash(DisplayCallbackRemover key) { void *ptr = *(void **)(&key); return qHash(ptr); } |
| typedef HRESULT (__stdcall ICorePointerRedirector::*RedirectorCallbackRemover)(EventRegistrationToken); |
| uint qHash(RedirectorCallbackRemover key) { void *ptr = *(void **)(&key); return qHash(ptr); } |
| typedef HRESULT (__stdcall IApplicationView2::*ApplicationView2CallbackRemover)(EventRegistrationToken); |
| uint qHash(ApplicationView2CallbackRemover key) { void *ptr = *(void **)(&key); return qHash(ptr); } |
| |
| class QWinRTScreenPrivate |
| { |
| public: |
| QTouchDevice *touchDevice; |
| ComPtr<ICoreWindow> coreWindow; |
| ComPtr<ICorePointerRedirector> redirect; |
| ComPtr<QWinRTCanvas> canvas; |
| ComPtr<IApplicationView> view; |
| ComPtr<IDisplayInformation> displayInformation; |
| |
| QScopedPointer<QWinRTCursor> cursor; |
| QHash<quint32, QWindowSystemInterface::TouchPoint> touchPoints; |
| QRectF logicalRect; |
| QRectF visibleRect; |
| QSurfaceFormat surfaceFormat; |
| qreal logicalDpi; |
| QDpi physicalDpi; |
| qreal scaleFactor; |
| Qt::ScreenOrientation nativeOrientation; |
| Qt::ScreenOrientation orientation; |
| QList<QWindow *> visibleWindows; |
| QHash<Qt::Key, KeyInfo> activeKeys; |
| QHash<CoreWindowCallbackRemover, EventRegistrationToken> windowTokens; |
| QHash<DisplayCallbackRemover, EventRegistrationToken> displayTokens; |
| QHash<RedirectorCallbackRemover, EventRegistrationToken> redirectTokens; |
| QHash<ApplicationView2CallbackRemover, EventRegistrationToken> view2Tokens; |
| ComPtr<IApplicationView2> view2; |
| QAtomicPointer<QWinRTWindow> mouseGrabWindow; |
| QAtomicPointer<QWinRTWindow> keyboardGrabWindow; |
| QWindow *currentPressWindow = nullptr; |
| QWindow *currentTargetWindow = nullptr; |
| bool firstMouseMove = true; |
| bool resizePending = false; |
| }; |
| |
| // To be called from the XAML thread |
| QWinRTScreen::QWinRTScreen() |
| : d_ptr(new QWinRTScreenPrivate) |
| { |
| Q_D(QWinRTScreen); |
| qCDebug(lcQpaWindows) << __FUNCTION__; |
| d->orientation = Qt::PrimaryOrientation; |
| d->touchDevice = nullptr; |
| |
| HRESULT hr; |
| ComPtr<Xaml::IWindowStatics> windowStatics; |
| hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_Xaml_Window).Get(), |
| IID_PPV_ARGS(&windowStatics)); |
| Q_ASSERT_SUCCEEDED(hr); |
| ComPtr<Xaml::IWindow> window; |
| hr = windowStatics->get_Current(&window); |
| Q_ASSERT_SUCCEEDED(hr); |
| hr = window->Activate(); |
| Q_ASSERT_SUCCEEDED(hr); |
| |
| hr = window->get_CoreWindow(&d->coreWindow); |
| Q_ASSERT_SUCCEEDED(hr); |
| |
| hr = d->coreWindow.As(&d->redirect); |
| Q_ASSERT_SUCCEEDED(hr); |
| |
| hr = d->coreWindow->Activate(); |
| Q_ASSERT_SUCCEEDED(hr); |
| |
| Rect rect; |
| hr = d->coreWindow->get_Bounds(&rect); |
| Q_ASSERT_SUCCEEDED(hr); |
| d->logicalRect = QRectF(0.0f, 0.0f, rect.Width, rect.Height); |
| d->visibleRect = QRectF(0.0f, 0.0f, rect.Width, rect.Height); |
| |
| // Orientation handling |
| ComPtr<IDisplayInformationStatics> displayInformationStatics; |
| hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Graphics_Display_DisplayInformation).Get(), |
| IID_PPV_ARGS(&displayInformationStatics)); |
| Q_ASSERT_SUCCEEDED(hr); |
| |
| hr = displayInformationStatics->GetForCurrentView(&d->displayInformation); |
| Q_ASSERT_SUCCEEDED(hr); |
| |
| // Set native orientation |
| DisplayOrientations displayOrientation; |
| hr = d->displayInformation->get_NativeOrientation(&displayOrientation); |
| Q_ASSERT_SUCCEEDED(hr); |
| d->nativeOrientation = static_cast<Qt::ScreenOrientation>(static_cast<int>(qtOrientationsFromNative(displayOrientation))); |
| // Set initial pixel density |
| onDpiChanged(nullptr, nullptr); |
| d->orientation = d->nativeOrientation; |
| |
| ComPtr<IApplicationViewStatics2> applicationViewStatics; |
| hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_ViewManagement_ApplicationView).Get(), |
| IID_PPV_ARGS(&applicationViewStatics)); |
| RETURN_VOID_IF_FAILED("Could not get ApplicationViewStatics"); |
| |
| hr = applicationViewStatics->GetForCurrentView(&d->view); |
| RETURN_VOID_IF_FAILED("Could not access currentView"); |
| |
| d->canvas = Make<QWinRTCanvas>([this]() { return topWindow(); }); |
| |
| ComPtr<Xaml::IFrameworkElement> frameworkElement; |
| hr = d->canvas.As(&frameworkElement); |
| Q_ASSERT_SUCCEEDED(hr); |
| hr = frameworkElement->put_Width(d->logicalRect.width()); |
| Q_ASSERT_SUCCEEDED(hr); |
| hr = frameworkElement->put_Height(d->logicalRect.height()); |
| Q_ASSERT_SUCCEEDED(hr); |
| |
| ComPtr<Xaml::IUIElement> uiElement; |
| hr = d->canvas.As(&uiElement); |
| Q_ASSERT_SUCCEEDED(hr); |
| |
| #if QT_CONFIG(draganddrop) |
| QWinRTDrag::instance()->setUiElement(uiElement); |
| #endif |
| hr = window->put_Content(uiElement.Get()); |
| Q_ASSERT_SUCCEEDED(hr); |
| |
| d->cursor.reset(new QWinRTCursor); |
| |
| hr = d->view.As(&d->view2); |
| Q_ASSERT_SUCCEEDED(hr); |
| } |
| |
| QWinRTScreen::~QWinRTScreen() |
| { |
| Q_D(QWinRTScreen); |
| qCDebug(lcQpaWindows) << __FUNCTION__ << this; |
| |
| // Unregister callbacks |
| HRESULT hr; |
| hr = QEventDispatcherWinRT::runOnXamlThread([this, d]() { |
| HRESULT hr; |
| for (QHash<CoreWindowCallbackRemover, EventRegistrationToken>::const_iterator i = d->windowTokens.begin(); i != d->windowTokens.end(); ++i) { |
| hr = (d->coreWindow.Get()->*i.key())(i.value()); |
| Q_ASSERT_SUCCEEDED(hr); |
| } |
| for (QHash<DisplayCallbackRemover, EventRegistrationToken>::const_iterator i = d->displayTokens.begin(); i != d->displayTokens.end(); ++i) { |
| hr = (d->displayInformation.Get()->*i.key())(i.value()); |
| Q_ASSERT_SUCCEEDED(hr); |
| } |
| for (QHash<RedirectorCallbackRemover, EventRegistrationToken>::const_iterator i = d->redirectTokens.begin(); i != d->redirectTokens.end(); ++i) { |
| hr = (d->redirect.Get()->*i.key())(i.value()); |
| Q_ASSERT_SUCCEEDED(hr); |
| } |
| for (QHash<ApplicationView2CallbackRemover, EventRegistrationToken>::const_iterator i = d->view2Tokens.begin(); i != d->view2Tokens.end(); ++i) { |
| hr = (d->view2.Get()->*i.key())(i.value()); |
| Q_ASSERT_SUCCEEDED(hr); |
| } |
| return hr; |
| }); |
| RETURN_VOID_IF_FAILED("Failed to unregister screen event callbacks"); |
| } |
| |
| QRect QWinRTScreen::geometry() const |
| { |
| Q_D(const QWinRTScreen); |
| return QRect(QPoint(), QSizeF(d->logicalRect.size() * d->scaleFactor).toSize()); |
| } |
| |
| QRect QWinRTScreen::availableGeometry() const |
| { |
| Q_D(const QWinRTScreen); |
| return QRectF((d->visibleRect.x() - d->logicalRect.x())* d->scaleFactor, |
| (d->visibleRect.y() - d->logicalRect.y()) * d->scaleFactor, |
| d->visibleRect.width() * d->scaleFactor, |
| d->visibleRect.height() * d->scaleFactor).toRect(); |
| } |
| |
| int QWinRTScreen::depth() const |
| { |
| return 32; |
| } |
| |
| QImage::Format QWinRTScreen::format() const |
| { |
| return QImage::Format_RGB32; |
| } |
| |
| QSizeF QWinRTScreen::physicalSize() const |
| { |
| Q_D(const QWinRTScreen); |
| return QSizeF(d->logicalRect.width() * d->scaleFactor / d->physicalDpi.first * qreal(25.4), |
| d->logicalRect.height() * d->scaleFactor / d->physicalDpi.second * qreal(25.4)); |
| } |
| |
| QDpi QWinRTScreen::logicalDpi() const |
| { |
| Q_D(const QWinRTScreen); |
| return QDpi(d->logicalDpi, d->logicalDpi); |
| } |
| |
| qreal QWinRTScreen::scaleFactor() const |
| { |
| Q_D(const QWinRTScreen); |
| return d->scaleFactor; |
| } |
| |
| QPlatformCursor *QWinRTScreen::cursor() const |
| { |
| Q_D(const QWinRTScreen); |
| return d->cursor.data(); |
| } |
| |
| Qt::KeyboardModifiers QWinRTScreen::keyboardModifiers() const |
| { |
| Q_D(const QWinRTScreen); |
| |
| Qt::KeyboardModifiers mods; |
| CoreVirtualKeyStates mod; |
| HRESULT hr = d->coreWindow->GetAsyncKeyState(VirtualKey_Shift, &mod); |
| Q_ASSERT_SUCCEEDED(hr); |
| if (mod & CoreVirtualKeyStates_Down) |
| mods |= Qt::ShiftModifier; |
| hr = d->coreWindow->GetAsyncKeyState(VirtualKey_Menu, &mod); |
| Q_ASSERT_SUCCEEDED(hr); |
| if (mod & CoreVirtualKeyStates_Down) |
| mods |= Qt::AltModifier; |
| hr = d->coreWindow->GetAsyncKeyState(VirtualKey_Control, &mod); |
| Q_ASSERT_SUCCEEDED(hr); |
| if (mod & CoreVirtualKeyStates_Down) |
| mods |= Qt::ControlModifier; |
| hr = d->coreWindow->GetAsyncKeyState(VirtualKey_LeftWindows, &mod); |
| Q_ASSERT_SUCCEEDED(hr); |
| if (mod & CoreVirtualKeyStates_Down) { |
| mods |= Qt::MetaModifier; |
| } else { |
| hr = d->coreWindow->GetAsyncKeyState(VirtualKey_RightWindows, &mod); |
| Q_ASSERT_SUCCEEDED(hr); |
| if (mod & CoreVirtualKeyStates_Down) |
| mods |= Qt::MetaModifier; |
| } |
| return mods; |
| } |
| |
| Qt::ScreenOrientation QWinRTScreen::nativeOrientation() const |
| { |
| Q_D(const QWinRTScreen); |
| return d->nativeOrientation; |
| } |
| |
| Qt::ScreenOrientation QWinRTScreen::orientation() const |
| { |
| Q_D(const QWinRTScreen); |
| return d->orientation; |
| } |
| |
| ICoreWindow *QWinRTScreen::coreWindow() const |
| { |
| Q_D(const QWinRTScreen); |
| return d->coreWindow.Get(); |
| } |
| |
| Xaml::IDependencyObject *QWinRTScreen::canvas() const |
| { |
| Q_D(const QWinRTScreen); |
| Xaml::IDependencyObject *depCanvas; |
| if (SUCCEEDED(d->canvas.CopyTo(&depCanvas))) |
| return depCanvas; |
| return nullptr; |
| } |
| |
| void QWinRTScreen::initialize() |
| { |
| Q_D(QWinRTScreen); |
| HRESULT hr; |
| hr = d->coreWindow->add_KeyDown(Callback<KeyHandler>(this, &QWinRTScreen::onKeyDown).Get(), &d->windowTokens[&ICoreWindow::remove_KeyDown]); |
| Q_ASSERT_SUCCEEDED(hr); |
| hr = d->coreWindow->add_KeyUp(Callback<KeyHandler>(this, &QWinRTScreen::onKeyUp).Get(), &d->windowTokens[&ICoreWindow::remove_KeyUp]); |
| Q_ASSERT_SUCCEEDED(hr); |
| hr = d->coreWindow->add_CharacterReceived(Callback<CharacterReceivedHandler>(this, &QWinRTScreen::onCharacterReceived).Get(), &d->windowTokens[&ICoreWindow::remove_CharacterReceived]); |
| Q_ASSERT_SUCCEEDED(hr); |
| hr = d->coreWindow->add_PointerEntered(Callback<PointerHandler>(this, &QWinRTScreen::onPointerEntered).Get(), &d->windowTokens[&ICoreWindow::remove_PointerEntered]); |
| Q_ASSERT_SUCCEEDED(hr); |
| hr = d->coreWindow->add_PointerExited(Callback<PointerHandler>(this, &QWinRTScreen::onPointerExited).Get(), &d->windowTokens[&ICoreWindow::remove_PointerExited]); |
| Q_ASSERT_SUCCEEDED(hr); |
| hr = d->coreWindow->add_PointerMoved(Callback<PointerHandler>(this, &QWinRTScreen::onPointerUpdated).Get(), &d->windowTokens[&ICoreWindow::remove_PointerMoved]); |
| Q_ASSERT_SUCCEEDED(hr); |
| hr = d->coreWindow->add_PointerPressed(Callback<PointerHandler>(this, &QWinRTScreen::onPointerUpdated).Get(), &d->windowTokens[&ICoreWindow::remove_PointerPressed]); |
| Q_ASSERT_SUCCEEDED(hr); |
| hr = d->coreWindow->add_PointerReleased(Callback<PointerHandler>(this, &QWinRTScreen::onPointerUpdated).Get(), &d->windowTokens[&ICoreWindow::remove_PointerReleased]); |
| Q_ASSERT_SUCCEEDED(hr); |
| hr = d->coreWindow->add_PointerWheelChanged(Callback<PointerHandler>(this, &QWinRTScreen::onPointerUpdated).Get(), &d->windowTokens[&ICoreWindow::remove_PointerWheelChanged]); |
| Q_ASSERT_SUCCEEDED(hr); |
| hr = d->view2->add_VisibleBoundsChanged(Callback<VisibleBoundsChangedHandler>(this, &QWinRTScreen::onWindowSizeChanged).Get(), &d->view2Tokens[&IApplicationView2::remove_VisibleBoundsChanged]); |
| Q_ASSERT_SUCCEEDED(hr); |
| hr = d->coreWindow->add_Activated(Callback<ActivatedHandler>(this, &QWinRTScreen::onActivated).Get(), &d->windowTokens[&ICoreWindow::remove_Activated]); |
| Q_ASSERT_SUCCEEDED(hr); |
| hr = d->coreWindow->add_Closed(Callback<ClosedHandler>(this, &QWinRTScreen::onClosed).Get(), &d->windowTokens[&ICoreWindow::remove_Closed]); |
| Q_ASSERT_SUCCEEDED(hr); |
| hr = d->coreWindow->add_VisibilityChanged(Callback<VisibilityChangedHandler>(this, &QWinRTScreen::onVisibilityChanged).Get(), &d->windowTokens[&ICoreWindow::remove_VisibilityChanged]); |
| Q_ASSERT_SUCCEEDED(hr); |
| hr = d->displayInformation->add_OrientationChanged(Callback<DisplayInformationHandler>(this, &QWinRTScreen::onOrientationChanged).Get(), &d->displayTokens[&IDisplayInformation::remove_OrientationChanged]); |
| Q_ASSERT_SUCCEEDED(hr); |
| hr = d->displayInformation->add_DpiChanged(Callback<DisplayInformationHandler>(this, &QWinRTScreen::onDpiChanged).Get(), &d->displayTokens[&IDisplayInformation::remove_DpiChanged]); |
| Q_ASSERT_SUCCEEDED(hr); |
| onOrientationChanged(nullptr, nullptr); |
| onVisibilityChanged(nullptr, nullptr); |
| |
| hr = d->redirect->add_PointerRoutedReleased(Callback<RedirectHandler>(this, &QWinRTScreen::onRedirectReleased).Get(), &d->redirectTokens[&ICorePointerRedirector::remove_PointerRoutedReleased]); |
| Q_ASSERT_SUCCEEDED(hr); |
| } |
| |
| void QWinRTScreen::setCursorRect(const QRectF &cursorRect) |
| { |
| mCursorRect = cursorRect; |
| } |
| |
| void QWinRTScreen::setKeyboardRect(const QRectF &keyboardRect) |
| { |
| Q_D(QWinRTScreen); |
| QRectF visibleRectF; |
| HRESULT hr; |
| Rect windowSize; |
| |
| hr = d->coreWindow->get_Bounds(&windowSize); |
| if (FAILED(hr)) { |
| qErrnoWarning(hr, "Failed to get window bounds"); |
| return; |
| } |
| d->logicalRect = QRectF(windowSize.X, windowSize.Y, windowSize.Width, windowSize.Height); |
| Rect visibleRect; |
| hr = d->view2->get_VisibleBounds(&visibleRect); |
| if (FAILED(hr)) { |
| qErrnoWarning(hr, "Failed to get window visible bounds"); |
| return; |
| } |
| visibleRectF = QRectF(visibleRect.X, visibleRect.Y, visibleRect.Width, visibleRect.Height); |
| // if keyboard is snapped to the bottom of the screen and would cover the cursor the content is |
| // moved up to make it visible |
| if (keyboardRect.intersects(mCursorRect) |
| && qFuzzyCompare(geometry().height(), keyboardRect.y() + keyboardRect.height())) { |
| visibleRectF.moveTop(visibleRectF.top() - keyboardRect.height() / d->scaleFactor); |
| } |
| d->visibleRect = visibleRectF; |
| |
| qCDebug(lcQpaWindows) << __FUNCTION__ << d->visibleRect; |
| QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), availableGeometry()); |
| QPlatformScreen::resizeMaximizedWindows(); |
| handleExpose(); |
| } |
| |
| QWindow *QWinRTScreen::topWindow() const |
| { |
| Q_D(const QWinRTScreen); |
| return d->visibleWindows.isEmpty() ? 0 : d->visibleWindows.first(); |
| } |
| |
| QWindow *QWinRTScreen::windowAt(const QPoint &pos) |
| { |
| Q_D(const QWinRTScreen); |
| for (auto w : qAsConst(d->visibleWindows)) { |
| if (w->geometry().contains(pos)) |
| return w; |
| } |
| qCDebug(lcQpaWindows) << __FUNCTION__ << ": No window found at:" << pos; |
| return nullptr; |
| } |
| |
| void QWinRTScreen::addWindow(QWindow *window) |
| { |
| Q_D(QWinRTScreen); |
| qCDebug(lcQpaWindows) << __FUNCTION__ << window; |
| if (window == topWindow() || window->surfaceClass() == QSurface::Offscreen) |
| return; |
| |
| d->visibleWindows.prepend(window); |
| const Qt::WindowType type = window->type(); |
| if (type != Qt::Popup && type != Qt::ToolTip && type != Qt::Tool) { |
| updateWindowTitle(window->title()); |
| QWindowSystemInterface::handleWindowActivated(window, Qt::OtherFocusReason); |
| } |
| |
| handleExpose(); |
| d->firstMouseMove = true; |
| QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); |
| |
| #if QT_CONFIG(draganddrop) |
| QWinRTDrag::instance()->setDropTarget(window); |
| #endif |
| } |
| |
| void QWinRTScreen::removeWindow(QWindow *window) |
| { |
| Q_D(QWinRTScreen); |
| qCDebug(lcQpaWindows) << __FUNCTION__ << window; |
| |
| handleExpose(); |
| |
| const bool wasTopWindow = window == topWindow(); |
| if (!d->visibleWindows.removeAll(window)) |
| return; |
| |
| const Qt::WindowType type = window->type(); |
| if (wasTopWindow && type != Qt::Popup && type != Qt::ToolTip && type != Qt::Tool) |
| QWindowSystemInterface::handleWindowActivated(nullptr, Qt::OtherFocusReason); |
| QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); |
| #if QT_CONFIG(draganddrop) |
| if (wasTopWindow) |
| QWinRTDrag::instance()->setDropTarget(topWindow()); |
| #endif |
| } |
| |
| void QWinRTScreen::raise(QWindow *window) |
| { |
| Q_D(QWinRTScreen); |
| d->visibleWindows.removeAll(window); |
| addWindow(window); |
| } |
| |
| void QWinRTScreen::lower(QWindow *window) |
| { |
| Q_D(QWinRTScreen); |
| const bool wasTopWindow = window == topWindow(); |
| if (wasTopWindow && d->visibleWindows.size() == 1) |
| return; |
| if (window->surfaceClass() == QSurface::Offscreen) |
| return; |
| d->visibleWindows.removeAll(window); |
| d->visibleWindows.append(window); |
| if (wasTopWindow) |
| QWindowSystemInterface::handleWindowActivated(window, Qt::OtherFocusReason); |
| handleExpose(); |
| } |
| |
| bool QWinRTScreen::setMouseGrabWindow(QWinRTWindow *window, bool grab) |
| { |
| Q_D(QWinRTScreen); |
| qCDebug(lcQpaWindows) << __FUNCTION__ << window |
| << "(" << window->window()->objectName() << "):" << grab; |
| |
| if (!grab || window == nullptr) |
| d->mouseGrabWindow = nullptr; |
| else if (d->mouseGrabWindow != window) |
| d->mouseGrabWindow = window; |
| return grab; |
| } |
| |
| QWinRTWindow *QWinRTScreen::mouseGrabWindow() const |
| { |
| Q_D(const QWinRTScreen); |
| return d->mouseGrabWindow; |
| } |
| |
| bool QWinRTScreen::setKeyboardGrabWindow(QWinRTWindow *window, bool grab) |
| { |
| Q_D(QWinRTScreen); |
| qCDebug(lcQpaWindows) << __FUNCTION__ << window |
| << "(" << window->window()->objectName() << "):" << grab; |
| |
| if (!grab || window == nullptr) |
| d->keyboardGrabWindow = nullptr; |
| else if (d->keyboardGrabWindow != window) |
| d->keyboardGrabWindow = window; |
| return grab; |
| } |
| |
| QWinRTWindow *QWinRTScreen::keyboardGrabWindow() const |
| { |
| Q_D(const QWinRTScreen); |
| return d->keyboardGrabWindow; |
| } |
| |
| void QWinRTScreen::updateWindowTitle(const QString &title) |
| { |
| Q_D(QWinRTScreen); |
| |
| HStringReference titleRef(reinterpret_cast<LPCWSTR>(title.utf16()), title.length()); |
| HRESULT hr = d->view->put_Title(titleRef.Get()); |
| RETURN_VOID_IF_FAILED("Unable to set window title"); |
| } |
| |
| void QWinRTScreen::handleExpose() |
| { |
| Q_D(QWinRTScreen); |
| if (d->visibleWindows.isEmpty()) |
| return; |
| QList<QWindow *>::const_iterator it = d->visibleWindows.constBegin(); |
| QWindowSystemInterface::handleExposeEvent(*it, geometry()); |
| while (++it != d->visibleWindows.constEnd()) |
| QWindowSystemInterface::handleExposeEvent(*it, QRegion()); |
| } |
| |
| HRESULT QWinRTScreen::onKeyDown(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::IKeyEventArgs *args) |
| { |
| Q_D(QWinRTScreen); |
| VirtualKey virtualKey; |
| HRESULT hr = args->get_VirtualKey(&virtualKey); |
| Q_ASSERT_SUCCEEDED(hr); |
| CorePhysicalKeyStatus status; |
| hr = args->get_KeyStatus(&status); |
| Q_ASSERT_SUCCEEDED(hr); |
| |
| Qt::Key key = qKeyFromVirtual(virtualKey); |
| |
| const bool wasPressed = d->activeKeys.contains(key); |
| if (wasPressed) { |
| if (!shouldAutoRepeat(key)) |
| return S_OK; |
| |
| d->activeKeys[key].isAutoRepeat = true; |
| // If the key was pressed before trigger a key release before the next key press |
| QWindowSystemInterface::handleExtendedKeyEvent( |
| topWindow(), |
| QEvent::KeyRelease, |
| key, |
| keyboardModifiers(), |
| !status.ScanCode ? -1 : status.ScanCode, |
| virtualKey, |
| 0, |
| QString(), |
| d->activeKeys.value(key).isAutoRepeat); |
| } else { |
| d->activeKeys.insert(key, KeyInfo(virtualKey)); |
| } |
| |
| // Defer character key presses to onCharacterReceived |
| if (key == Qt::Key_unknown || (key >= Qt::Key_Space && key <= Qt::Key_ydiaeresis)) |
| return S_OK; |
| |
| Qt::KeyboardModifiers modifiers = keyboardModifiers(); |
| // If the key actually pressed is a modifier key, then we remove its modifier key from the |
| // state, since a modifier-key can't have itself as a modifier (see qwindowskeymapper.cpp) |
| if (key == Qt::Key_Control) |
| modifiers = modifiers ^ Qt::ControlModifier; |
| else if (key == Qt::Key_Shift) |
| modifiers = modifiers ^ Qt::ShiftModifier; |
| else if (key == Qt::Key_Alt) |
| modifiers = modifiers ^ Qt::AltModifier; |
| |
| QWindowSystemInterface::handleExtendedKeyEvent( |
| topWindow(), |
| QEvent::KeyPress, |
| key, |
| modifiers, |
| !status.ScanCode ? -1 : status.ScanCode, |
| virtualKey, |
| 0, |
| QString(), |
| d->activeKeys.value(key).isAutoRepeat); |
| return S_OK; |
| } |
| |
| HRESULT QWinRTScreen::onKeyUp(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::IKeyEventArgs *args) |
| { |
| Q_D(QWinRTScreen); |
| VirtualKey virtualKey; |
| HRESULT hr = args->get_VirtualKey(&virtualKey); |
| Q_ASSERT_SUCCEEDED(hr); |
| CorePhysicalKeyStatus status; |
| hr = args->get_KeyStatus(&status); |
| Q_ASSERT_SUCCEEDED(hr); |
| |
| Qt::Key key = qKeyFromVirtual(virtualKey); |
| const KeyInfo info = d->activeKeys.take(key); |
| QWindowSystemInterface::handleExtendedKeyEvent( |
| topWindow(), |
| QEvent::KeyRelease, |
| key, |
| keyboardModifiers(), |
| !status.ScanCode ? -1 : status.ScanCode, |
| virtualKey, |
| 0, |
| info.text, |
| false); // The final key release does not have autoRepeat set on Windows |
| return S_OK; |
| } |
| |
| HRESULT QWinRTScreen::onCharacterReceived(ICoreWindow *, ICharacterReceivedEventArgs *args) |
| { |
| Q_D(QWinRTScreen); |
| quint32 keyCode; |
| HRESULT hr = args->get_KeyCode(&keyCode); |
| Q_ASSERT_SUCCEEDED(hr); |
| CorePhysicalKeyStatus status; |
| hr = args->get_KeyStatus(&status); |
| Q_ASSERT_SUCCEEDED(hr); |
| |
| // Don't generate character events for non-printables; the meta key stage is enough |
| if (qIsNonPrintable(keyCode)) |
| return S_OK; |
| |
| const Qt::KeyboardModifiers modifiers = keyboardModifiers(); |
| const Qt::Key key = qKeyFromCode(keyCode, modifiers); |
| const QString text = QChar(keyCode); |
| KeyInfo &info = d->activeKeys[key]; |
| info.text = text; |
| QWindowSystemInterface::handleExtendedKeyEvent( |
| topWindow(), |
| QEvent::KeyPress, |
| key, |
| modifiers, |
| !status.ScanCode ? -1 : status.ScanCode, |
| info.virtualKey, |
| 0, |
| text, |
| info.isAutoRepeat); |
| return S_OK; |
| } |
| |
| HRESULT QWinRTScreen::onPointerEntered(ICoreWindow *, IPointerEventArgs *args) |
| { |
| Q_D(QWinRTScreen); |
| qCDebug(lcQpaEvents) << __FUNCTION__; |
| |
| ComPtr<IPointerPoint> pointerPoint; |
| if (SUCCEEDED(args->get_CurrentPoint(&pointerPoint))) { |
| // Assumes full-screen window |
| Point point; |
| pointerPoint->get_Position(&point); |
| QPoint pos(point.X * d->scaleFactor, point.Y * d->scaleFactor); |
| |
| d->currentTargetWindow = topWindow(); |
| if (d->mouseGrabWindow) |
| d->currentTargetWindow = d->mouseGrabWindow.loadRelaxed()->window(); |
| |
| qCDebug(lcQpaEvents) << __FUNCTION__ << "handleEnterEvent" << d->currentTargetWindow << pos; |
| QWindowSystemInterface::handleEnterEvent(d->currentTargetWindow, pos, pos); |
| d->firstMouseMove = false; |
| } |
| return S_OK; |
| } |
| |
| HRESULT QWinRTScreen::onPointerExited(ICoreWindow *, IPointerEventArgs *args) |
| { |
| Q_D(QWinRTScreen); |
| qCDebug(lcQpaEvents) << __FUNCTION__; |
| ComPtr<IPointerPoint> pointerPoint; |
| if (FAILED(args->get_CurrentPoint(&pointerPoint))) |
| return E_INVALIDARG; |
| |
| quint32 id; |
| if (FAILED(pointerPoint->get_PointerId(&id))) |
| return E_INVALIDARG; |
| |
| d->touchPoints.remove(id); |
| |
| if (d->mouseGrabWindow) |
| d->currentTargetWindow = d->mouseGrabWindow.loadRelaxed()->window(); |
| |
| qCDebug(lcQpaEvents) << __FUNCTION__ << "handleLeaveEvent" << d->currentTargetWindow; |
| QWindowSystemInterface::handleLeaveEvent(d->currentTargetWindow); |
| d->currentTargetWindow = nullptr; |
| return S_OK; |
| } |
| |
| // Required for qwinrtdrag.cpp |
| ComPtr<IPointerPoint> qt_winrt_lastPointerPoint; |
| |
| HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args) |
| { |
| Q_D(QWinRTScreen); |
| qCDebug(lcQpaEvents) << __FUNCTION__; |
| ComPtr<IPointerPoint> pointerPoint; |
| if (FAILED(args->get_CurrentPoint(&pointerPoint))) |
| return E_INVALIDARG; |
| |
| qt_winrt_lastPointerPoint = pointerPoint; |
| // Common traits - point, modifiers, properties |
| Point point; |
| pointerPoint->get_Position(&point); |
| const QPointF pos(point.X * d->scaleFactor, point.Y * d->scaleFactor); |
| QPointF localPos = pos; |
| |
| const QPoint posPoint = pos.toPoint(); |
| QWindow *windowUnderPointer = windowAt(QHighDpiScaling::mapPositionFromNative(posPoint, this)); |
| d->currentTargetWindow = windowUnderPointer; |
| |
| if (d->mouseGrabWindow) |
| d->currentTargetWindow = d->mouseGrabWindow.loadRelaxed()->window(); |
| |
| if (d->currentTargetWindow) { |
| const QPointF globalPosDelta = pos - posPoint; |
| localPos = d->currentTargetWindow->mapFromGlobal(posPoint) + globalPosDelta; |
| } |
| |
| VirtualKeyModifiers modifiers; |
| args->get_KeyModifiers(&modifiers); |
| Qt::KeyboardModifiers mods; |
| if (modifiers & VirtualKeyModifiers_Control) |
| mods |= Qt::ControlModifier; |
| if (modifiers & VirtualKeyModifiers_Menu) |
| mods |= Qt::AltModifier; |
| if (modifiers & VirtualKeyModifiers_Shift) |
| mods |= Qt::ShiftModifier; |
| if (modifiers & VirtualKeyModifiers_Windows) |
| mods |= Qt::MetaModifier; |
| |
| ComPtr<IPointerPointProperties> properties; |
| if (FAILED(pointerPoint->get_Properties(&properties))) |
| return E_INVALIDARG; |
| |
| ComPtr<IPointerDevice> pointerDevice; |
| HRESULT hr = pointerPoint->get_PointerDevice(&pointerDevice); |
| RETURN_OK_IF_FAILED("Failed to get pointer device."); |
| |
| PointerDeviceType pointerDeviceType; |
| hr = pointerDevice->get_PointerDeviceType(&pointerDeviceType); |
| RETURN_OK_IF_FAILED("Failed to get pointer device type."); |
| |
| switch (pointerDeviceType) { |
| case PointerDeviceType_Mouse: { |
| qint32 delta; |
| properties->get_MouseWheelDelta(&delta); |
| if (delta) { |
| boolean isHorizontal; |
| properties->get_IsHorizontalMouseWheel(&isHorizontal); |
| QPoint angleDelta(isHorizontal ? delta : 0, isHorizontal ? 0 : delta); |
| qCDebug(lcQpaEvents) << __FUNCTION__ << "handleWheelEvent" << d->currentTargetWindow |
| << localPos << pos << angleDelta << mods; |
| QWindowSystemInterface::handleWheelEvent(d->currentTargetWindow, localPos, pos, QPoint(), angleDelta, mods); |
| break; |
| } |
| |
| boolean isPressed; |
| Qt::MouseButtons buttons = Qt::NoButton; |
| properties->get_IsLeftButtonPressed(&isPressed); |
| if (isPressed) |
| buttons |= Qt::LeftButton; |
| |
| properties->get_IsMiddleButtonPressed(&isPressed); |
| if (isPressed) |
| buttons |= Qt::MiddleButton; |
| |
| properties->get_IsRightButtonPressed(&isPressed); |
| if (isPressed) |
| buttons |= Qt::RightButton; |
| |
| properties->get_IsXButton1Pressed(&isPressed); |
| if (isPressed) |
| buttons |= Qt::XButton1; |
| |
| properties->get_IsXButton2Pressed(&isPressed); |
| if (isPressed) |
| buttons |= Qt::XButton2; |
| |
| // In case of a mouse grab we have to store the target of a press event |
| // to be able to send one additional release event to this target when the mouse |
| // button is released. This is a similar approach to AutoMouseCapture in the |
| // windows qpa backend. Otherwise the release might not be propagated and the original |
| // press event receiver considers a button to still be pressed, as in Qt Quick Controls 1 |
| // menus. |
| if (buttons != Qt::NoButton && d->currentPressWindow == nullptr && !d->mouseGrabWindow) |
| d->currentPressWindow = windowUnderPointer; |
| if (buttons == Qt::NoButton && d->currentPressWindow && d->mouseGrabWindow) { |
| const QPointF globalPosDelta = pos - posPoint; |
| const QPointF localPressPos = d->currentPressWindow->mapFromGlobal(posPoint) + globalPosDelta; |
| |
| qCDebug(lcQpaEvents) << __FUNCTION__ << "handleMouseEvent" << d->currentPressWindow |
| << localPressPos << pos << buttons << mods; |
| QWindowSystemInterface::handleMouseEvent(d->currentPressWindow, localPressPos, pos, buttons, mods); |
| d->currentPressWindow = nullptr; |
| } |
| // If the mouse button is released outside of a window, targetWindow is 0, but the event |
| // has to be delivered to the window, that initially received the mouse press. Do not reset |
| // d->currentTargetWindow though, as it is used (and reset) in onPointerExited. |
| if (buttons == Qt::NoButton && d->currentPressWindow && !d->currentTargetWindow) { |
| d->currentTargetWindow = d->currentPressWindow; |
| d->currentPressWindow = nullptr; |
| } |
| |
| qCDebug(lcQpaEvents) << __FUNCTION__ << "handleMouseEvent" << d->currentTargetWindow |
| << localPos << pos << buttons << mods; |
| QWindowSystemInterface::handleMouseEvent(d->currentTargetWindow, localPos, pos, buttons, mods); |
| |
| break; |
| } |
| case PointerDeviceType_Pen: |
| case PointerDeviceType_Touch: { |
| if (!d->touchDevice) { |
| d->touchDevice = new QTouchDevice; |
| d->touchDevice->setName(QStringLiteral("WinRTTouchScreen")); |
| d->touchDevice->setType(QTouchDevice::TouchScreen); |
| d->touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::Pressure | QTouchDevice::NormalizedPosition); |
| QWindowSystemInterface::registerTouchDevice(d->touchDevice); |
| } |
| |
| quint32 id; |
| pointerPoint->get_PointerId(&id); |
| |
| Rect area; |
| properties->get_ContactRect(&area); |
| |
| float pressure; |
| properties->get_Pressure(&pressure); |
| |
| boolean isPressed; |
| pointerPoint->get_IsInContact(&isPressed); |
| |
| // Devices like the Hololens set a static pressure of 0.0 or 0.5 |
| // (depending on the image) independent of the pressed state. |
| // In those cases we need to synthesize the pressure value. To our |
| // knowledge this does not apply to pens |
| if (pointerDeviceType == PointerDeviceType_Touch && (pressure == 0.0f || pressure == 0.5f)) |
| pressure = isPressed ? 1. : 0.; |
| |
| const QRectF areaRect(area.X * d->scaleFactor, area.Y * d->scaleFactor, |
| area.Width * d->scaleFactor, area.Height * d->scaleFactor); |
| |
| QHash<quint32, QWindowSystemInterface::TouchPoint>::iterator it = d->touchPoints.find(id); |
| if (it == d->touchPoints.end()) { |
| it = d->touchPoints.insert(id, QWindowSystemInterface::TouchPoint()); |
| it.value().id = id; |
| } |
| |
| const bool wasPressEvent = isPressed && it.value().pressure == 0.; |
| if (wasPressEvent) |
| it.value().state = Qt::TouchPointPressed; |
| else if (!isPressed && it.value().pressure > 0.) |
| it.value().state = Qt::TouchPointReleased; |
| else if (it.value().area == areaRect) |
| it.value().state = Qt::TouchPointStationary; |
| else |
| it.value().state = Qt::TouchPointMoved; |
| |
| it.value().area = areaRect; |
| it.value().normalPosition = QPointF(point.X/d->logicalRect.width(), point.Y/d->logicalRect.height()); |
| it.value().pressure = pressure; |
| |
| qCDebug(lcQpaEvents) << __FUNCTION__ << "handleTouchEvent" << d->currentTargetWindow |
| << d->touchDevice << d->touchPoints.values() << mods; |
| QWindowSystemInterface::handleTouchEvent(d->currentTargetWindow, d->touchDevice, d->touchPoints.values(), mods); |
| if (wasPressEvent) |
| it.value().state = Qt::TouchPointStationary; |
| |
| // Fall-through for pen to generate tablet event |
| if (pointerDeviceType != PointerDeviceType_Pen) |
| break; |
| |
| boolean isEraser; |
| properties->get_IsEraser(&isEraser); |
| int pointerType = isEraser ? 3 : 1; |
| |
| float xTilt; |
| properties->get_XTilt(&xTilt); |
| |
| float yTilt; |
| properties->get_YTilt(&yTilt); |
| |
| float rotation; |
| properties->get_Twist(&rotation); |
| |
| qCDebug(lcQpaEvents) << __FUNCTION__ << "handleTabletEvent" << d->currentTargetWindow |
| << isPressed << pos << pointerType << pressure << xTilt << yTilt |
| << rotation << id << mods; |
| QWindowSystemInterface::handleTabletEvent(d->currentTargetWindow, isPressed, pos, pos, 0, |
| pointerType, pressure, xTilt, yTilt, |
| 0, rotation, 0, id, mods); |
| |
| break; |
| } |
| } |
| |
| return S_OK; |
| } |
| |
| void QWinRTScreen::emulateMouseMove(const QPointF &point, MousePositionTransition transition) |
| { |
| Q_D(QWinRTScreen); |
| qCDebug(lcQpaEvents) << __FUNCTION__ << point << transition; |
| if (transition == MousePositionTransition::StayedOut) |
| return; |
| qt_winrt_lastPointerPoint = nullptr; |
| const QPointF pos(point.x() * d->scaleFactor, point.y() * d->scaleFactor); |
| QPointF localPos = pos; |
| |
| const QPoint posPoint = pos.toPoint(); |
| QWindow *windowUnderPointer = windowAt(QHighDpiScaling::mapPositionFromNative(posPoint, this)); |
| d->currentTargetWindow = windowUnderPointer; |
| |
| if (d->mouseGrabWindow) |
| d->currentTargetWindow = d->mouseGrabWindow.loadRelaxed()->window(); |
| |
| if (d->currentTargetWindow) { |
| const QPointF globalPosDelta = pos - posPoint; |
| localPos = d->currentTargetWindow->mapFromGlobal(posPoint) + globalPosDelta; |
| } |
| |
| // In case of a mouse grab we have to store the target of a press event |
| // to be able to send one additional release event to this target when the mouse |
| // button is released. This is a similar approach to AutoMouseCapture in the |
| // windows qpa backend. Otherwise the release might not be propagated and the original |
| // press event receiver considers a button to still be pressed, as in Qt Quick Controls 1 |
| // menus. |
| if (d->currentPressWindow && d->mouseGrabWindow) { |
| const QPointF globalPosDelta = pos - posPoint; |
| const QPointF localPressPos = d->currentPressWindow->mapFromGlobal(posPoint) + globalPosDelta; |
| |
| qCDebug(lcQpaEvents) << __FUNCTION__ << "handleMouseEvent" << d->currentPressWindow |
| << localPressPos << pos << Qt::NoButton << Qt::NoModifier; |
| QWindowSystemInterface::handleMouseEvent(d->currentPressWindow, localPressPos, pos, |
| Qt::NoButton, Qt::NoModifier); |
| d->currentPressWindow = nullptr; |
| } |
| // If the mouse button is released outside of a window, targetWindow is 0, but the event |
| // has to be delivered to the window, that initially received the mouse press. Do not reset |
| // d->currentTargetWindow though, as it is used (and reset) in onPointerExited. |
| if (d->currentPressWindow && !d->currentTargetWindow) { |
| d->currentTargetWindow = d->currentPressWindow; |
| d->currentPressWindow = nullptr; |
| } |
| |
| if (transition == MousePositionTransition::MovedOut) { |
| qCDebug(lcQpaEvents) << __FUNCTION__ << "handleLeaveEvent" << d->currentTargetWindow; |
| QWindowSystemInterface::handleLeaveEvent(d->currentTargetWindow); |
| return; |
| } |
| |
| if (transition == MousePositionTransition::MovedIn || d->firstMouseMove) { |
| qCDebug(lcQpaEvents) << __FUNCTION__ << "handleEnterEvent" << d->currentTargetWindow |
| << localPos << pos; |
| QWindowSystemInterface::handleEnterEvent(d->currentTargetWindow, localPos, pos); |
| d->firstMouseMove = false; |
| } |
| qCDebug(lcQpaEvents) << __FUNCTION__ << "handleMouseEvent" << d->currentTargetWindow |
| << localPos << pos << Qt::NoButton << Qt::NoModifier; |
| QWindowSystemInterface::handleMouseEvent(d->currentTargetWindow, localPos, pos, Qt::NoButton, |
| Qt::NoModifier); |
| } |
| |
| void QWinRTScreen::setResizePending() |
| { |
| Q_D(QWinRTScreen); |
| d->resizePending = true; |
| } |
| |
| bool QWinRTScreen::resizePending() const |
| { |
| Q_D(const QWinRTScreen); |
| return d->resizePending; |
| } |
| |
| HRESULT QWinRTScreen::onActivated(ICoreWindow *, IWindowActivatedEventArgs *args) |
| { |
| Q_D(QWinRTScreen); |
| qCDebug(lcQpaWindows) << __FUNCTION__; |
| |
| CoreWindowActivationState activationState; |
| args->get_WindowActivationState(&activationState); |
| if (activationState == CoreWindowActivationState_Deactivated) { |
| QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive); |
| return S_OK; |
| } |
| |
| QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive); |
| |
| // Activate topWindow |
| if (!d->visibleWindows.isEmpty()) { |
| Qt::FocusReason focusReason = activationState == CoreWindowActivationState_PointerActivated |
| ? Qt::MouseFocusReason : Qt::ActiveWindowFocusReason; |
| QWindowSystemInterface::handleWindowActivated(topWindow(), focusReason); |
| } |
| return S_OK; |
| } |
| |
| HRESULT QWinRTScreen::onClosed(ICoreWindow *, ICoreWindowEventArgs *) |
| { |
| qCDebug(lcQpaWindows) << __FUNCTION__; |
| |
| foreach (QWindow *w, QGuiApplication::topLevelWindows()) |
| QWindowSystemInterface::handleCloseEvent(w); |
| return S_OK; |
| } |
| |
| HRESULT QWinRTScreen::onVisibilityChanged(ICoreWindow *, IVisibilityChangedEventArgs *args) |
| { |
| Q_D(QWinRTScreen); |
| boolean visible; |
| HRESULT hr = args ? args->get_Visible(&visible) : d->coreWindow->get_Visible(&visible); |
| RETURN_OK_IF_FAILED("Failed to get visibility."); |
| qCDebug(lcQpaWindows) << __FUNCTION__ << visible; |
| QWindowSystemInterface::handleApplicationStateChanged(visible ? Qt::ApplicationActive : Qt::ApplicationHidden); |
| if (visible) { |
| handleExpose(); |
| onWindowSizeChanged(nullptr, nullptr); |
| } |
| return S_OK; |
| } |
| |
| HRESULT QWinRTScreen::onOrientationChanged(IDisplayInformation *, IInspectable *) |
| { |
| Q_D(QWinRTScreen); |
| qCDebug(lcQpaWindows) << __FUNCTION__; |
| DisplayOrientations displayOrientation; |
| HRESULT hr = d->displayInformation->get_CurrentOrientation(&displayOrientation); |
| RETURN_OK_IF_FAILED("Failed to get current orientations."); |
| |
| Qt::ScreenOrientation newOrientation = static_cast<Qt::ScreenOrientation>(static_cast<int>(qtOrientationsFromNative(displayOrientation))); |
| if (d->orientation != newOrientation) { |
| d->orientation = newOrientation; |
| qCDebug(lcQpaWindows) << " New orientation:" << newOrientation; |
| onWindowSizeChanged(nullptr, nullptr); |
| QWindowSystemInterface::handleScreenOrientationChange(screen(), d->orientation); |
| handleExpose(); // Clean broken frames caused by race between Qt and ANGLE |
| } |
| return S_OK; |
| } |
| |
| HRESULT QWinRTScreen::onDpiChanged(IDisplayInformation *, IInspectable *) |
| { |
| Q_D(QWinRTScreen); |
| |
| HRESULT hr; |
| ResolutionScale resolutionScale; |
| hr = d->displayInformation->get_ResolutionScale(&resolutionScale); |
| d->scaleFactor = qreal(resolutionScale) / 100; |
| |
| qCDebug(lcQpaWindows) << __FUNCTION__ << "Scale Factor:" << d->scaleFactor; |
| |
| RETURN_OK_IF_FAILED("Failed to get scale factor"); |
| |
| FLOAT dpi; |
| hr = d->displayInformation->get_LogicalDpi(&dpi); |
| RETURN_OK_IF_FAILED("Failed to get logical DPI."); |
| d->logicalDpi = dpi; |
| |
| hr = d->displayInformation->get_RawDpiX(&dpi); |
| RETURN_OK_IF_FAILED("Failed to get x raw DPI."); |
| d->physicalDpi.first = dpi ? dpi : 96.0; |
| |
| hr = d->displayInformation->get_RawDpiY(&dpi); |
| RETURN_OK_IF_FAILED("Failed to get y raw DPI."); |
| d->physicalDpi.second = dpi ? dpi : 96.0; |
| qCDebug(lcQpaWindows) << __FUNCTION__ << "Logical DPI:" << d->logicalDpi |
| << "Physical DPI:" << d->physicalDpi; |
| |
| return S_OK; |
| } |
| |
| HRESULT QWinRTScreen::onRedirectReleased(ICorePointerRedirector *, IPointerEventArgs *args) |
| { |
| // When dragging ends with a non-mouse input device then onRedirectRelease is invoked. |
| // QTBUG-58781 |
| qCDebug(lcQpaEvents) << __FUNCTION__; |
| return onPointerUpdated(nullptr, args); |
| } |
| |
| HRESULT QWinRTScreen::onWindowSizeChanged(IApplicationView *w, IInspectable *) |
| { |
| Q_D(QWinRTScreen); |
| |
| HRESULT hr; |
| Rect windowSize; |
| |
| hr = d->coreWindow->get_Bounds(&windowSize); |
| RETURN_OK_IF_FAILED("Failed to get window bounds"); |
| d->logicalRect = QRectF(windowSize.X, windowSize.Y, windowSize.Width, windowSize.Height); |
| |
| Rect visibleRect; |
| hr = d->view2->get_VisibleBounds(&visibleRect); |
| RETURN_OK_IF_FAILED("Failed to get window visible bounds"); |
| d->visibleRect = QRectF(visibleRect.X, visibleRect.Y, visibleRect.Width, visibleRect.Height); |
| |
| qCDebug(lcQpaWindows) << __FUNCTION__ << d->logicalRect; |
| QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), availableGeometry()); |
| QPlatformScreen::resizeMaximizedWindows(); |
| handleExpose(); |
| // If we "emulate" a resize, w will be nullptr.Checking w shows whether it's a real resize |
| if (w) |
| d->resizePending = false; |
| return S_OK; |
| } |
| |
| QT_END_NAMESPACE |