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 | |
48 | QT_BEGIN_NAMESPACE |
49 | namespace QtVirtualKeyboard { |
50 | |
51 | class DesktopInputPanelPrivate : public AppInputPanelPrivate |
52 | { |
53 | public: |
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 | |
89 | DesktopInputPanel::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 | |
99 | DesktopInputPanel::~DesktopInputPanel() |
100 | { |
101 | } |
102 | |
103 | void 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 | |
113 | void DesktopInputPanel::hide() |
114 | { |
115 | AppInputPanel::hide(); |
116 | Q_D(DesktopInputPanel); |
117 | if (d->view) |
118 | d->view->hide(); |
119 | } |
120 | |
121 | bool DesktopInputPanel::isVisible() const |
122 | { |
123 | return AppInputPanel::isVisible(); |
124 | } |
125 | |
126 | void DesktopInputPanel::setInputRect(const QRect &inputRect) |
127 | { |
128 | Q_D(DesktopInputPanel); |
129 | d->keyboardRect = inputRect; |
130 | updateInputRegion(); |
131 | } |
132 | |
133 | void 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 | |
163 | void DesktopInputPanel::destroyView() |
164 | { |
165 | Q_D(DesktopInputPanel); |
166 | d->view.reset(); |
167 | d->previewBindingActive = false; |
168 | } |
169 | |
170 | void 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 | |
194 | void 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 | |
201 | void 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 | |
210 | void 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 | |
219 | void 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) |
229 | static 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 | |
240 | void 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 |
284 | QT_END_NAMESPACE |
285 | |