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 Qt Virtual Keyboard module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL$
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 General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include <QtVirtualKeyboard/private/desktopinputpanel_p.h>
31#include <QtVirtualKeyboard/private/appinputpanel_p_p.h>
32#include <QtVirtualKeyboard/private/inputview_p.h>
33#include <QtVirtualKeyboard/private/platforminputcontext_p.h>
34#include <QtVirtualKeyboard/private/qvirtualkeyboardinputcontext_p.h>
35#include <QtVirtualKeyboard/qvirtualkeyboardinputcontext.h>
36#include <QGuiApplication>
37#include <QQmlEngine>
38#include <QScreen>
39#include <QtVirtualKeyboard/private/virtualkeyboarddebug_p.h>
40#if defined(QT_VIRTUALKEYBOARD_HAVE_XCB)
41#include <xcb/xcb.h>
42#include <xcb/xfixes.h>
43#endif
44#include <qpa/qplatformnativeinterface.h>
45#include <QtCore/private/qobject_p.h>
46#include <QtCore/QLibraryInfo>
47
48QT_BEGIN_NAMESPACE
49namespace QtVirtualKeyboard {
50
51class DesktopInputPanelPrivate : public AppInputPanelPrivate
52{
53public:
54 enum WindowingSystem {
55 Windows,
56 Xcb,
57 Other,
58 };
59
60 DesktopInputPanelPrivate() :
61 AppInputPanelPrivate(),
62 view(),
63 keyboardRect(),
64 previewRect(),
65 previewVisible(false),
66 previewBindingActive(false),
67 windowingSystem(Other)
68 {
69 const QString platformName = QGuiApplication::platformName();
70 if (platformName == QLatin1String("windows"))
71 windowingSystem = Windows;
72 else if (platformName == QLatin1String("xcb"))
73 windowingSystem = Xcb;
74 }
75
76 QScopedPointer<InputView> view;
77 QRectF keyboardRect;
78 QRectF previewRect;
79 bool previewVisible;
80 bool previewBindingActive;
81 WindowingSystem windowingSystem;
82};
83
84/*!
85 \class QtVirtualKeyboard::DesktopInputPanel
86 \internal
87*/
88
89DesktopInputPanel::DesktopInputPanel(QObject *parent) :
90 AppInputPanel(*new DesktopInputPanelPrivate(), parent)
91{
92 /* Activate the alpha buffer for this application.
93 */
94 QQuickWindow::setDefaultAlphaBuffer(true);
95 QScreen *screen = QGuiApplication::primaryScreen();
96 connect(asender: screen, SIGNAL(virtualGeometryChanged(QRect)), SLOT(repositionView(QRect)));
97}
98
99DesktopInputPanel::~DesktopInputPanel()
100{
101}
102
103void DesktopInputPanel::show()
104{
105 AppInputPanel::show();
106 Q_D(DesktopInputPanel);
107 if (d->view) {
108 repositionView(rect: QGuiApplication::primaryScreen()->availableGeometry());
109 d->view->show();
110 }
111}
112
113void DesktopInputPanel::hide()
114{
115 AppInputPanel::hide();
116 Q_D(DesktopInputPanel);
117 if (d->view)
118 d->view->hide();
119}
120
121bool DesktopInputPanel::isVisible() const
122{
123 return AppInputPanel::isVisible();
124}
125
126void DesktopInputPanel::setInputRect(const QRect &inputRect)
127{
128 Q_D(DesktopInputPanel);
129 d->keyboardRect = inputRect;
130 updateInputRegion();
131}
132
133void DesktopInputPanel::createView()
134{
135 Q_D(DesktopInputPanel);
136 if (!d->view) {
137 if (qGuiApp) {
138 connect(qGuiApp, SIGNAL(focusWindowChanged(QWindow*)), SLOT(focusWindowChanged(QWindow*)));
139 focusWindowChanged(qGuiApp->focusWindow());
140 }
141 d->view.reset(other: new InputView());
142 d->view->setFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::WindowDoesNotAcceptFocus);
143 /* Set appropriate WindowType for target environment.
144 There seems to be no common type which would
145 work in all environments. The purpose of this
146 flag is to avoid the window from capturing focus,
147 as well as hiding it from the task bar. */
148 switch (d->windowingSystem) {
149 case DesktopInputPanelPrivate::Xcb:
150 d->view->setFlags(d->view->flags() | Qt::Window | Qt::BypassWindowManagerHint);
151 break;
152 default:
153 d->view->setFlags(d->view->flags() | Qt::Tool);
154 break;
155 }
156 d->view->setColor(QColor(Qt::transparent));
157 d->view->setSource(QUrl(QLatin1String("qrc:///QtQuick/VirtualKeyboard/content/InputPanel.qml")));
158 if (QGuiApplication *app = qGuiApp)
159 connect(asender: app, SIGNAL(aboutToQuit()), SLOT(destroyView()));
160 }
161}
162
163void DesktopInputPanel::destroyView()
164{
165 Q_D(DesktopInputPanel);
166 d->view.reset();
167 d->previewBindingActive = false;
168}
169
170void DesktopInputPanel::repositionView(const QRect &rect)
171{
172 Q_D(DesktopInputPanel);
173 VIRTUALKEYBOARD_DEBUG() << "DesktopInputPanel::repositionView():" << rect;
174 if (d->view && d->view->geometry() != rect) {
175 QVirtualKeyboardInputContext *inputContext = qobject_cast<PlatformInputContext *>(object: parent())->inputContext();
176 if (inputContext) {
177 inputContext->setAnimating(true);
178 if (!d->previewBindingActive) {
179 QVirtualKeyboardInputContextPrivate *inputContextPrivate = inputContext->priv();
180 QObject::connect(sender: inputContextPrivate, signal: &QVirtualKeyboardInputContextPrivate::previewRectangleChanged, receiver: this, slot: &DesktopInputPanel::previewRectangleChanged);
181 QObject::connect(sender: inputContextPrivate, signal: &QVirtualKeyboardInputContextPrivate::previewVisibleChanged, receiver: this, slot: &DesktopInputPanel::previewVisibleChanged);
182 d->previewBindingActive = true;
183 }
184 }
185 d->view->setResizeMode(QQuickView::SizeViewToRootObject);
186 setInputRect(QRect());
187 d->view->setGeometry(rect);
188 d->view->setResizeMode(QQuickView::SizeRootObjectToView);
189 if (inputContext)
190 inputContext->setAnimating(false);
191 }
192}
193
194void DesktopInputPanel::focusWindowChanged(QWindow *focusWindow)
195{
196 disconnect(receiver: this, SLOT(focusWindowVisibleChanged(bool)));
197 if (focusWindow)
198 connect(sender: focusWindow, signal: &QWindow::visibleChanged, receiver: this, slot: &DesktopInputPanel::focusWindowVisibleChanged);
199}
200
201void DesktopInputPanel::focusWindowVisibleChanged(bool visible)
202{
203 if (!visible) {
204 QVirtualKeyboardInputContext *inputContext = qobject_cast<PlatformInputContext *>(object: parent())->inputContext();
205 if (inputContext)
206 inputContext->priv()->hideInputPanel();
207 }
208}
209
210void DesktopInputPanel::previewRectangleChanged()
211{
212 Q_D(DesktopInputPanel);
213 QVirtualKeyboardInputContext *inputContext = qobject_cast<PlatformInputContext *>(object: parent())->inputContext();
214 d->previewRect = inputContext->priv()->previewRectangle();
215 if (d->previewVisible)
216 updateInputRegion();
217}
218
219void DesktopInputPanel::previewVisibleChanged()
220{
221 Q_D(DesktopInputPanel);
222 QVirtualKeyboardInputContext *inputContext = qobject_cast<PlatformInputContext *>(object: parent())->inputContext();
223 d->previewVisible = inputContext->priv()->previewVisible();
224 if (d->view->isVisible())
225 updateInputRegion();
226}
227
228#if defined(QT_VIRTUALKEYBOARD_HAVE_XCB)
229static inline xcb_rectangle_t qRectToXCBRectangle(const QRect &r)
230{
231 xcb_rectangle_t result;
232 result.x = qMax(SHRT_MIN, b: r.x());
233 result.y = qMax(SHRT_MIN, b: r.y());
234 result.width = qMin(a: (int)USHRT_MAX, b: r.width());
235 result.height = qMin(a: (int)USHRT_MAX, b: r.height());
236 return result;
237}
238#endif
239
240void DesktopInputPanel::updateInputRegion()
241{
242 Q_D(DesktopInputPanel);
243
244 if (d->view.isNull() || d->keyboardRect.isEmpty())
245 return;
246
247 // Make sure the native window is created
248 if (!d->view->handle())
249 d->view->create();
250
251 switch (d->windowingSystem) {
252 case DesktopInputPanelPrivate::Xcb:
253#if defined(QT_VIRTUALKEYBOARD_HAVE_XCB)
254 {
255 QVector<xcb_rectangle_t> rects;
256 rects.push_back(t: qRectToXCBRectangle(r: d->keyboardRect.toRect()));
257 if (d->previewVisible && !d->previewRect.isEmpty())
258 rects.push_back(t: qRectToXCBRectangle(r: d->previewRect.toRect()));
259
260 QWindow *window = d->view.data();
261 QPlatformNativeInterface *platformNativeInterface = QGuiApplication::platformNativeInterface();
262 xcb_connection_t *xbcConnection = static_cast<xcb_connection_t *>(platformNativeInterface->nativeResourceForWindow(resource: "connection", window));
263 xcb_xfixes_region_t xbcRegion = xcb_generate_id(c: xbcConnection);
264 xcb_xfixes_create_region(c: xbcConnection, region: xbcRegion, rectangles_len: rects.size(), rectangles: rects.constData());
265 xcb_xfixes_set_window_shape_region(c: xbcConnection, dest: window->winId(), dest_kind: XCB_SHAPE_SK_INPUT, x_offset: 0, y_offset: 0, region: xbcRegion);
266 xcb_xfixes_destroy_region(c: xbcConnection, region: xbcRegion);
267 }
268#endif
269 break;
270
271 default:
272 {
273 QRegion inputRegion(d->keyboardRect.toRect());
274 if (d->previewVisible && !d->previewRect.isEmpty())
275 inputRegion += d->previewRect.toRect();
276
277 d->view->setMask(inputRegion);
278 break;
279 }
280 }
281}
282
283} // namespace QtVirtualKeyboard
284QT_END_NAMESPACE
285

source code of qtvirtualkeyboard/src/virtualkeyboard/desktopinputpanel.cpp