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 "qwinrtinputcontext.h"
41 #include "qwinrtscreen.h"
42 #include <QtGui/QGuiApplication>
43 #include <QtGui/QWindow>
44 #include <private/qeventdispatcher_winrt_p.h>
45 
46 #include <functional>
47 #include <wrl.h>
48 #include <roapi.h>
49 #include <windows.ui.viewmanagement.h>
50 #include <windows.ui.core.h>
51 using namespace Microsoft::WRL;
52 using namespace Microsoft::WRL::Wrappers;
53 using namespace ABI::Windows::Foundation;
54 using namespace ABI::Windows::UI::ViewManagement;
55 using namespace ABI::Windows::UI::Core;
56 
57 typedef ITypedEventHandler<InputPane*, InputPaneVisibilityEventArgs*> InputPaneVisibilityHandler;
58 
59 QT_BEGIN_NAMESPACE
60 
61 Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods")
62 
getInputPaneRect(ComPtr<IInputPane> pane,qreal scaleFactor)63 inline QRectF getInputPaneRect(ComPtr<IInputPane> pane, qreal scaleFactor)
64 {
65     Rect rect;
66     pane->get_OccludedRect(&rect);
67     return QRectF(qRound(qreal(rect.X) * scaleFactor), qRound(qreal(rect.Y) * scaleFactor),
68                   qRound(qreal(rect.Width) * scaleFactor), qRound(qreal(rect.Height) * scaleFactor));
69 }
70 
71 /*!
72     \class QWinRTInputContext
73     \brief Manages Input Method visibility
74     \internal
75     \ingroup qt-qpa-winrt
76 
77     Listens to the native virtual keyboard for hide/show events and provides
78     hints to the OS for showing/hiding. On WinRT, showInputPanel()/hideInputPanel()
79     have no effect because WinRT dictates that keyboard presence is user-driven:
80     (http://msdn.microsoft.com/en-us/library/windows/apps/hh465404.aspx)
81     Windows Phone, however, supports direct hiding/showing of the keyboard.
82 */
83 
QWinRTInputContext(QWinRTScreen * screen)84 QWinRTInputContext::QWinRTInputContext(QWinRTScreen *screen)
85     : m_screen(screen)
86 {
87     qCDebug(lcQpaInputMethods) << __FUNCTION__ << screen;
88 
89     QEventDispatcherWinRT::runOnXamlThread([this]() {
90         ComPtr<IInputPaneStatics> statics;
91         if (FAILED(GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_ViewManagement_InputPane).Get(),
92                                         &statics))) {
93             qWarning("failed to retrieve input pane statics.");
94             return S_OK;
95         }
96 
97         ComPtr<IInputPane> inputPane;
98         statics->GetForCurrentView(&inputPane);
99         if (inputPane) {
100             EventRegistrationToken showToken, hideToken;
101             inputPane->add_Showing(Callback<InputPaneVisibilityHandler>(
102                 this, &QWinRTInputContext::onShowing).Get(), &showToken);
103             inputPane->add_Hiding(Callback<InputPaneVisibilityHandler>(
104                 this, &QWinRTInputContext::onHiding).Get(), &hideToken);
105 
106             m_keyboardRect = getInputPaneRect(inputPane, m_screen->scaleFactor());
107             m_isInputPanelVisible = !m_keyboardRect.isEmpty();
108         } else {
109             qWarning("failed to retrieve InputPane.");
110         }
111         return S_OK;
112     });
113 
114     connect(QGuiApplication::inputMethod(), &QInputMethod::cursorRectangleChanged,
115             this, &QWinRTInputContext::updateScreenCursorRect);
116 }
117 
keyboardRect() const118 QRectF QWinRTInputContext::keyboardRect() const
119 {
120     return m_keyboardRect;
121 }
122 
isInputPanelVisible() const123 bool QWinRTInputContext::isInputPanelVisible() const
124 {
125     return m_isInputPanelVisible;
126 }
127 
updateScreenCursorRect()128 void QWinRTInputContext::updateScreenCursorRect()
129 {
130     m_screen->setCursorRect(QGuiApplication::inputMethod()->cursorRectangle());
131 }
132 
onShowing(IInputPane * pane,IInputPaneVisibilityEventArgs *)133 HRESULT QWinRTInputContext::onShowing(IInputPane *pane, IInputPaneVisibilityEventArgs *)
134 {
135     qCDebug(lcQpaInputMethods) << __FUNCTION__ << pane;
136     m_isInputPanelVisible = true;
137     emitInputPanelVisibleChanged();
138     return handleVisibilityChange(pane);
139 }
140 
onHiding(IInputPane * pane,IInputPaneVisibilityEventArgs *)141 HRESULT QWinRTInputContext::onHiding(IInputPane *pane, IInputPaneVisibilityEventArgs *)
142 {
143     qCDebug(lcQpaInputMethods) << __FUNCTION__ << pane;
144     m_isInputPanelVisible = false;
145     emitInputPanelVisibleChanged();
146     return handleVisibilityChange(pane);
147 }
148 
handleVisibilityChange(IInputPane * pane)149 HRESULT QWinRTInputContext::handleVisibilityChange(IInputPane *pane)
150 {
151     qCDebug(lcQpaInputMethods) << __FUNCTION__ << pane;
152     const QRectF keyboardRect = getInputPaneRect(pane, m_screen->scaleFactor());
153     if (m_keyboardRect != keyboardRect) {
154         m_keyboardRect = keyboardRect;
155         m_screen->setKeyboardRect(m_keyboardRect);
156         emitKeyboardRectChanged();
157     }
158     return S_OK;
159 }
160 
getInputPane(ComPtr<IInputPane2> * inputPane2)161 static HRESULT getInputPane(ComPtr<IInputPane2> *inputPane2)
162 {
163     ComPtr<IInputPaneStatics> factory;
164     HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_ViewManagement_InputPane).Get(),
165                                       &factory);
166     if (FAILED(hr)) {
167         qErrnoWarning(hr, "Failed to get input pane factory.");
168         return hr;
169     }
170 
171     ComPtr<IInputPane> inputPane;
172     hr = factory->GetForCurrentView(&inputPane);
173     if (FAILED(hr)) {
174         qErrnoWarning(hr, "Failed to get input pane.");
175         return hr;
176     }
177 
178     hr = inputPane.As(inputPane2);
179     if (FAILED(hr)) {
180         qErrnoWarning(hr, "Failed to get extended input pane.");
181         return hr;
182     }
183     return hr;
184 }
185 
showInputPanel()186 void QWinRTInputContext::showInputPanel()
187 {
188     qCDebug(lcQpaInputMethods) << __FUNCTION__;
189 
190     QEventDispatcherWinRT::runOnXamlThread([&]() {
191         ComPtr<IInputPane2> inputPane;
192         HRESULT hr = getInputPane(&inputPane);
193         if (FAILED(hr))
194             return S_OK;
195         boolean success;
196         hr = inputPane->TryShow(&success);
197         if (FAILED(hr) || !success)
198             qErrnoWarning(hr, "Failed to show input panel.");
199         return S_OK;
200     });
201 }
202 
hideInputPanel()203 void QWinRTInputContext::hideInputPanel()
204 {
205     qCDebug(lcQpaInputMethods) << __FUNCTION__;
206     if (!m_isInputPanelVisible)
207         return;
208 
209     QEventDispatcherWinRT::runOnXamlThread([&]() {
210         ComPtr<IInputPane2> inputPane;
211         HRESULT hr = getInputPane(&inputPane);
212         if (FAILED(hr))
213             return S_OK;
214         boolean success;
215         hr = inputPane->TryHide(&success);
216         if (FAILED(hr) || !success)
217             qErrnoWarning(hr, "Failed to hide input panel.");
218         return S_OK;
219     });
220 }
221 
222 QT_END_NAMESPACE
223