| 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 |
Definitions
Learn Advanced QML with KDAB
Find out more
