Integrate QtVirtualKeyboard into KWin/Wayland
Summary: The idea is to have KWin provide a virtual keyboard. To support this KWin uses the QT_IM_MODULE qtvirtualkeyboard and makes sure that the QPA plugin loads it. KWin has a new class VirtualKeyboard which acts as the focus object and the "proxy" for input methods. The QPA plugin ensures that this is the focusObject, so that all input method related events are sent to this class. From there it will be possible to delegate to other applications through the Wayland interfaces. Reviewers: #plasma Subscribers: plasma-devel Tags: #plasma Differential Revision: https://phabricator.kde.org/D1638icc-effect-5.14.5
parent
fdb04e6f75
commit
f26f2fe181
|
@ -415,6 +415,7 @@ set(kwin_KDEINIT_SRCS
|
|||
shell_client.cpp
|
||||
wayland_server.cpp
|
||||
wayland_cursor_theme.cpp
|
||||
virtualkeyboard.cpp
|
||||
)
|
||||
|
||||
if(KWIN_BUILD_TABBOX)
|
||||
|
|
|
@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*********************************************************************/
|
||||
#include "main_wayland.h"
|
||||
#include "composite.h"
|
||||
#include "virtualkeyboard.h"
|
||||
#include "workspace.h"
|
||||
#include <config-kwin.h>
|
||||
// kwin
|
||||
|
@ -124,6 +125,7 @@ void ApplicationWayland::performStartup()
|
|||
|
||||
// try creating the Wayland Backend
|
||||
createInput();
|
||||
VirtualKeyboard::create(this);
|
||||
createBackend();
|
||||
}
|
||||
|
||||
|
@ -444,7 +446,7 @@ int main(int argc, char * argv[])
|
|||
setenv("QT_QPA_PLATFORM", "wayland-org.kde.kwin.qpa", true);
|
||||
|
||||
qunsetenv("QT_DEVICE_PIXEL_RATIO");
|
||||
qunsetenv("QT_IM_MODULE");
|
||||
qputenv("QT_IM_MODULE", "qtvirtualkeyboard");
|
||||
qputenv("QSG_RENDER_LOOP", "basic");
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||
QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
|
||||
|
|
|
@ -26,6 +26,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "screen.h"
|
||||
#include "sharingplatformcontext.h"
|
||||
#include "window.h"
|
||||
#include "../../virtualkeyboard.h"
|
||||
#include "../../main.h"
|
||||
#include "../../wayland_server.h"
|
||||
|
||||
|
@ -41,6 +42,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include <QtConcurrentRun>
|
||||
|
||||
#include <qpa/qplatformwindow.h>
|
||||
#include <qpa/qplatforminputcontext.h>
|
||||
#include <qpa/qplatforminputcontextfactory_p.h>
|
||||
#include <qpa/qwindowsysteminterface.h>
|
||||
#include <QtCore/private/qeventdispatcher_unix_p.h>
|
||||
#include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h>
|
||||
#include <QtPlatformSupport/private/qgenericunixthemes_p.h>
|
||||
|
@ -57,6 +61,7 @@ Integration::Integration()
|
|||
, QPlatformIntegration()
|
||||
, m_fontDb(new QGenericUnixFontDatabase())
|
||||
, m_nativeInterface(new NativeInterface(this))
|
||||
, m_inputContext()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -90,6 +95,31 @@ void Integration::initialize()
|
|||
QPlatformIntegration::initialize();
|
||||
m_dummyScreen = new Screen(nullptr);
|
||||
screenAdded(m_dummyScreen);
|
||||
m_inputContext.reset(QPlatformInputContextFactory::create(QStringLiteral("qtvirtualkeyboard")));
|
||||
qunsetenv("QT_IM_MODULE");
|
||||
connect(qApp, &QGuiApplication::focusObjectChanged, this,
|
||||
[this] {
|
||||
if (VirtualKeyboard::self() && qApp->focusObject() != VirtualKeyboard::self()) {
|
||||
m_inputContext->setFocusObject(VirtualKeyboard::self());
|
||||
}
|
||||
}
|
||||
);
|
||||
connect(kwinApp(), &Application::workspaceCreated, this,
|
||||
[this] {
|
||||
if (VirtualKeyboard::self()) {
|
||||
m_inputContext->setFocusObject(VirtualKeyboard::self());
|
||||
}
|
||||
}
|
||||
);
|
||||
connect(qApp->inputMethod(), &QInputMethod::visibleChanged, this,
|
||||
[this] {
|
||||
if (qApp->inputMethod()->isVisible()) {
|
||||
if (QWindow *w = VirtualKeyboard::self()->inputPanel()) {
|
||||
QWindowSystemInterface::handleWindowActivated(w, Qt::ActiveWindowFocusReason);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
QAbstractEventDispatcher *Integration::createEventDispatcher() const
|
||||
|
@ -261,5 +291,10 @@ void Integration::initEgl()
|
|||
}
|
||||
}
|
||||
|
||||
QPlatformInputContext *Integration::inputContext() const
|
||||
{
|
||||
return m_inputContext.data();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ public:
|
|||
QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override;
|
||||
|
||||
void initialize() override;
|
||||
QPlatformInputContext *inputContext() const override;
|
||||
|
||||
KWayland::Client::Compositor *compositor() const;
|
||||
EGLDisplay eglDisplay() const;
|
||||
|
@ -77,6 +78,7 @@ private:
|
|||
KWayland::Client::Shell *m_shell = nullptr;
|
||||
EGLDisplay m_eglDisplay = EGL_NO_DISPLAY;
|
||||
Screen *m_dummyScreen = nullptr;
|
||||
QScopedPointer<QPlatformInputContext> m_inputContext;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
install( DIRECTORY outline/plasma DESTINATION ${DATA_INSTALL_DIR}/${KWIN_NAME}/outline )
|
||||
install( DIRECTORY virtualkeyboard DESTINATION ${DATA_INSTALL_DIR}/${KWIN_NAME} )
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Enterprise.VirtualKeyboard 2.0
|
||||
|
||||
Item {
|
||||
id: window
|
||||
InputPanel {
|
||||
id: inputPanel
|
||||
objectName: "inputPanel"
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
import QtQuick 2.0
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Item {
|
||||
id: window
|
||||
InputPanel {
|
||||
id: inputPanel
|
||||
objectName: "inputPanel"
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
}
|
|
@ -764,6 +764,9 @@ bool ShellClient::isLockScreen() const
|
|||
|
||||
bool ShellClient::isInputMethod() const
|
||||
{
|
||||
if (m_internal && m_internalWindow) {
|
||||
return m_internalWindow->property("__kwin_input_method").toBool();
|
||||
}
|
||||
return surface()->client() == waylandServer()->inputMethodConnection();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Layouts 1.2
|
||||
|
||||
GridLayout {
|
||||
columns: 2
|
||||
Label {
|
||||
text: "Normal:"
|
||||
}
|
||||
TextField {
|
||||
}
|
||||
Label {
|
||||
text: "Digits:"
|
||||
}
|
||||
TextField {
|
||||
inputMethodHints: Qt.ImhDigitsOnly
|
||||
}
|
||||
Label {
|
||||
text: "Numbers:"
|
||||
}
|
||||
TextField {
|
||||
inputMethodHints: Qt.ImhFormattedNumbersOnly
|
||||
}
|
||||
Label {
|
||||
text: "Uppercase:"
|
||||
}
|
||||
TextField {
|
||||
inputMethodHints: Qt.ImhUppercaseOnly
|
||||
}
|
||||
Label {
|
||||
text: "Lowercase:"
|
||||
}
|
||||
TextField {
|
||||
inputMethodHints: Qt.ImhLowercaseOnly
|
||||
}
|
||||
Label {
|
||||
text: "Phone:"
|
||||
}
|
||||
TextField {
|
||||
inputMethodHints: Qt.ImhDialableCharactersOnly
|
||||
}
|
||||
Label {
|
||||
text: "Email:"
|
||||
}
|
||||
TextField {
|
||||
inputMethodHints: Qt.ImhEmailCharactersOnly
|
||||
}
|
||||
Label {
|
||||
text: "Url:"
|
||||
}
|
||||
TextField {
|
||||
inputMethodHints: Qt.ImhUrlCharactersOnly
|
||||
}
|
||||
Label {
|
||||
text: "Date:"
|
||||
}
|
||||
TextField {
|
||||
inputMethodHints: Qt.ImhDate
|
||||
}
|
||||
Label {
|
||||
text: "Time:"
|
||||
}
|
||||
TextField {
|
||||
inputMethodHints: Qt.ImhTime
|
||||
}
|
||||
}
|
|
@ -0,0 +1,436 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#include "virtualkeyboard.h"
|
||||
#include "utils.h"
|
||||
#include "screens.h"
|
||||
#include "wayland_server.h"
|
||||
#include "workspace.h"
|
||||
|
||||
#include <KWayland/Server/display.h>
|
||||
#include <KWayland/Server/seat_interface.h>
|
||||
#include <KWayland/Server/textinput_interface.h>
|
||||
|
||||
#include <KKeyServer>
|
||||
#include <KStatusNotifierItem>
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusPendingCall>
|
||||
#include <QDBusMessage>
|
||||
#include <QGuiApplication>
|
||||
#include <QQmlComponent>
|
||||
#include <QQmlContext>
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickItem>
|
||||
#include <QQuickView>
|
||||
#include <QQuickWindow>
|
||||
|
||||
using namespace KWayland::Server;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
KWIN_SINGLETON_FACTORY(VirtualKeyboard)
|
||||
|
||||
VirtualKeyboard::VirtualKeyboard(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
// this is actually too late. Other processes are started before init,
|
||||
// so might miss the availability of text input
|
||||
// but without Workspace we don't have the window listed at all
|
||||
connect(kwinApp(), &Application::workspaceCreated, this, &VirtualKeyboard::init);
|
||||
}
|
||||
|
||||
VirtualKeyboard::~VirtualKeyboard() = default;
|
||||
|
||||
void VirtualKeyboard::init()
|
||||
{
|
||||
// TODO: need a shared Qml engine
|
||||
m_inputWindow.reset(new QQuickView(nullptr));
|
||||
m_inputWindow->setGeometry(screens()->geometry(screens()->current()));
|
||||
m_inputWindow->setResizeMode(QQuickView::SizeRootObjectToView);
|
||||
m_inputWindow->setSource(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral(KWIN_NAME "/virtualkeyboard/main.qml"))));
|
||||
if (m_inputWindow->status() != QQuickView::Status::Ready) {
|
||||
// try enterprise
|
||||
m_inputWindow->setSource(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral(KWIN_NAME "/virtualkeyboard/main-enterprise.qml"))));
|
||||
if (m_inputWindow->status() != QQuickView::Status::Ready) {
|
||||
m_inputWindow.reset();
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_inputWindow->setProperty("__kwin_input_method", true);
|
||||
|
||||
if (waylandServer()) {
|
||||
m_enabled = !waylandServer()->seat()->hasKeyboard();
|
||||
connect(waylandServer()->seat(), &KWayland::Server::SeatInterface::hasKeyboardChanged, this,
|
||||
[this] {
|
||||
setEnabled(!waylandServer()->seat()->hasKeyboard());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
m_sni = new KStatusNotifierItem(QStringLiteral("kwin-virtual-keyboard"), this);
|
||||
m_sni->setCategory(KStatusNotifierItem::Hardware);
|
||||
m_sni->setStatus(KStatusNotifierItem::Passive);
|
||||
m_sni->setTitle(i18n("Virtual Keyboard"));
|
||||
updateSni();
|
||||
connect(m_sni, &KStatusNotifierItem::activateRequested, this,
|
||||
[this] {
|
||||
setEnabled(!m_enabled);
|
||||
}
|
||||
);
|
||||
|
||||
if (waylandServer()) {
|
||||
// we can announce support for the text input interface
|
||||
auto t = waylandServer()->display()->createTextInputManager(TextInputInterfaceVersion::UnstableV0, waylandServer()->display());
|
||||
t->create();
|
||||
auto t2 = waylandServer()->display()->createTextInputManager(TextInputInterfaceVersion::UnstableV2, waylandServer()->display());
|
||||
t2->create();
|
||||
connect(waylandServer()->seat(), &SeatInterface::focusedTextInputChanged, this,
|
||||
[this] {
|
||||
disconnect(m_waylandShowConnection);
|
||||
disconnect(m_waylandHideConnection);
|
||||
disconnect(m_waylandHintsConnection);
|
||||
disconnect(m_waylandSurroundingTextConnection);
|
||||
disconnect(m_waylandResetConnection);
|
||||
disconnect(m_waylandEnabledConnection);
|
||||
qApp->inputMethod()->reset();
|
||||
if (auto t = waylandServer()->seat()->focusedTextInput()) {
|
||||
m_waylandShowConnection = connect(t, &TextInputInterface::requestShowInputPanel, this, &VirtualKeyboard::show);
|
||||
m_waylandHideConnection = connect(t, &TextInputInterface::requestHideInputPanel, this, &VirtualKeyboard::hide);
|
||||
m_waylandSurroundingTextConnection = connect(t, &TextInputInterface::surroundingTextChanged, this,
|
||||
[] {
|
||||
qApp->inputMethod()->update(Qt::ImSurroundingText | Qt::ImCursorPosition | Qt::ImAnchorPosition);
|
||||
}
|
||||
);
|
||||
m_waylandHintsConnection = connect(t, &TextInputInterface::contentTypeChanged, this,
|
||||
[] {
|
||||
qApp->inputMethod()->update(Qt::ImHints);
|
||||
}
|
||||
);
|
||||
m_waylandResetConnection = connect(t, &TextInputInterface::requestReset, qApp->inputMethod(), &QInputMethod::reset);
|
||||
m_waylandEnabledConnection = connect(t, &TextInputInterface::enabledChanged, this,
|
||||
[] {
|
||||
qApp->inputMethod()->update(Qt::ImQueryAll);
|
||||
}
|
||||
);
|
||||
// TODO: calculate overlap
|
||||
t->setInputPanelState(m_inputWindow->isVisible(), QRect(0, 0, 0, 0));
|
||||
} else {
|
||||
m_waylandShowConnection = QMetaObject::Connection();
|
||||
m_waylandHideConnection = QMetaObject::Connection();
|
||||
m_waylandHintsConnection = QMetaObject::Connection();
|
||||
m_waylandSurroundingTextConnection = QMetaObject::Connection();
|
||||
m_waylandResetConnection = QMetaObject::Connection();
|
||||
m_waylandEnabledConnection = QMetaObject::Connection();
|
||||
}
|
||||
qApp->inputMethod()->update(Qt::ImQueryAll);
|
||||
}
|
||||
);
|
||||
}
|
||||
m_inputWindow->installEventFilter(this);
|
||||
connect(Workspace::self(), &Workspace::destroyed, this,
|
||||
[this] {
|
||||
m_inputWindow.reset();
|
||||
}
|
||||
);
|
||||
m_inputWindow->setColor(Qt::transparent);
|
||||
m_inputWindow->setMask(m_inputWindow->rootObject()->childrenRect().toRect());
|
||||
connect(m_inputWindow->rootObject(), &QQuickItem::childrenRectChanged, m_inputWindow.data(),
|
||||
[this] {
|
||||
if (!m_inputWindow) {
|
||||
return;
|
||||
}
|
||||
m_inputWindow->setMask(m_inputWindow->rootObject()->childrenRect().toRect());
|
||||
}
|
||||
);
|
||||
connect(qApp->inputMethod(), &QInputMethod::visibleChanged, m_inputWindow.data(),
|
||||
[this] {
|
||||
m_inputWindow->setVisible(qApp->inputMethod()->isVisible());
|
||||
if (qApp->inputMethod()->isVisible()) {
|
||||
m_inputWindow->setMask(m_inputWindow->rootObject()->childrenRect().toRect());
|
||||
}
|
||||
if (waylandServer()) {
|
||||
if (auto t = waylandServer()->seat()->focusedTextInput()) {
|
||||
// TODO: calculate overlap
|
||||
t->setInputPanelState(m_inputWindow->isVisible(), QRect(0, 0, 0, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void VirtualKeyboard::setEnabled(bool enabled)
|
||||
{
|
||||
if (m_enabled == enabled) {
|
||||
return;
|
||||
}
|
||||
m_enabled = enabled;
|
||||
qApp->inputMethod()->update(Qt::ImQueryAll);
|
||||
|
||||
updateSni();
|
||||
|
||||
// send OSD message
|
||||
QDBusMessage msg = QDBusMessage::createMethodCall(
|
||||
QStringLiteral("org.kde.plasmashell"),
|
||||
QStringLiteral("/org/kde/osdService"),
|
||||
QStringLiteral("org.kde.osdService"),
|
||||
QStringLiteral("virtualKeyboardEnabledChanged")
|
||||
);
|
||||
msg.setArguments({enabled});
|
||||
QDBusConnection::sessionBus().asyncCall(msg);
|
||||
}
|
||||
|
||||
void VirtualKeyboard::updateSni()
|
||||
{
|
||||
if (!m_sni) {
|
||||
return;
|
||||
}
|
||||
if (m_enabled) {
|
||||
m_sni->setIconByName(QStringLiteral("input-keyboard-virtual-on"));
|
||||
m_sni->setToolTipTitle(i18n("Virtual Keyboard is enabled."));
|
||||
} else {
|
||||
m_sni->setIconByName(QStringLiteral("input-keyboard-virtual-off"));
|
||||
m_sni->setToolTipTitle(i18n("Virtual Keyboard is disabled."));
|
||||
}
|
||||
}
|
||||
|
||||
void VirtualKeyboard::show()
|
||||
{
|
||||
if (m_inputWindow.isNull() || !m_enabled) {
|
||||
return;
|
||||
}
|
||||
m_inputWindow->setGeometry(screens()->geometry(screens()->current()));
|
||||
qApp->inputMethod()->show();
|
||||
}
|
||||
|
||||
void VirtualKeyboard::hide()
|
||||
{
|
||||
if (m_inputWindow.isNull()) {
|
||||
return;
|
||||
}
|
||||
qApp->inputMethod()->hide();
|
||||
}
|
||||
|
||||
bool VirtualKeyboard::event(QEvent *e)
|
||||
{
|
||||
if (e->type() == QEvent::InputMethod) {
|
||||
QInputMethodEvent *event = static_cast<QInputMethodEvent*>(e);
|
||||
if (m_enabled && waylandServer()) {
|
||||
bool isPreedit = false;
|
||||
for (auto attribute : event->attributes()) {
|
||||
switch (attribute.type) {
|
||||
case QInputMethodEvent::TextFormat:
|
||||
case QInputMethodEvent::Cursor:
|
||||
case QInputMethodEvent::Language:
|
||||
case QInputMethodEvent::Ruby:
|
||||
isPreedit = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
TextInputInterface *ti = waylandServer()->seat()->focusedTextInput();
|
||||
if (ti && ti->isEnabled()) {
|
||||
if (!isPreedit && event->preeditString().isEmpty() && !event->commitString().isEmpty()) {
|
||||
ti->commit(event->commitString().toUtf8());
|
||||
} else {
|
||||
ti->preEdit(event->preeditString().toUtf8(), event->commitString().toUtf8());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (e->type() == QEvent::InputMethodQuery) {
|
||||
auto event = static_cast<QInputMethodQueryEvent*>(e);
|
||||
TextInputInterface *ti = nullptr;
|
||||
if (waylandServer() && m_enabled) {
|
||||
ti = waylandServer()->seat()->focusedTextInput();
|
||||
}
|
||||
if (event->queries().testFlag(Qt::ImEnabled)) {
|
||||
event->setValue(Qt::ImEnabled, QVariant(ti != nullptr && ti->isEnabled()));
|
||||
}
|
||||
if (event->queries().testFlag(Qt::ImCursorRectangle)) {
|
||||
// not used by virtual keyboard
|
||||
}
|
||||
if (event->queries().testFlag(Qt::ImFont)) {
|
||||
// not used by virtual keyboard
|
||||
}
|
||||
if (event->queries().testFlag(Qt::ImCursorPosition)) {
|
||||
// the virtual keyboard doesn't send us the cursor position in the preedit
|
||||
// this would break text input, thus we ignore it
|
||||
// see https://bugreports.qt.io/browse/QTBUG-53517
|
||||
#if 0
|
||||
event->setValue(Qt::ImCursorPosition, QString::fromUtf8(ti->surroundingText().left(ti->surroundingTextCursorPosition())).size());
|
||||
#else
|
||||
event->setValue(Qt::ImCursorPosition, 0);
|
||||
#endif
|
||||
}
|
||||
if (event->queries().testFlag(Qt::ImSurroundingText)) {
|
||||
// the virtual keyboard doesn't send us the cursor position in the preedit
|
||||
// this would break text input, thus we ignore it
|
||||
// see https://bugreports.qt.io/browse/QTBUG-53517
|
||||
#if 0
|
||||
event->setValue(Qt::ImSurroundingText, QString::fromUtf8(ti->surroundingText()));
|
||||
#else
|
||||
event->setValue(Qt::ImSurroundingText, QString());
|
||||
#endif
|
||||
}
|
||||
if (event->queries().testFlag(Qt::ImCurrentSelection)) {
|
||||
// TODO: should be text between cursor and anchor, but might be dangerous
|
||||
}
|
||||
if (event->queries().testFlag(Qt::ImMaximumTextLength)) {
|
||||
// not used by virtual keyboard
|
||||
}
|
||||
if (event->queries().testFlag(Qt::ImAnchorPosition)) {
|
||||
// not used by virtual keyboard
|
||||
}
|
||||
if (event->queries().testFlag(Qt::ImHints)) {
|
||||
if (ti && ti->isEnabled()) {
|
||||
Qt::InputMethodHints hints;
|
||||
const auto contentHints = ti->contentHints();
|
||||
if (!contentHints.testFlag(TextInputInterface::ContentHint::AutoCompletion)) {
|
||||
hints |= Qt::ImhNoPredictiveText;
|
||||
}
|
||||
if (contentHints.testFlag(TextInputInterface::ContentHint::AutoCorrection)) {
|
||||
// no mapping in Qt
|
||||
}
|
||||
if (!contentHints.testFlag(TextInputInterface::ContentHint::AutoCapitalization)) {
|
||||
hints |= Qt::ImhNoAutoUppercase;
|
||||
}
|
||||
if (contentHints.testFlag(TextInputInterface::ContentHint::Lowercase)) {
|
||||
hints |= Qt::ImhPreferLowercase;
|
||||
}
|
||||
if (contentHints.testFlag(TextInputInterface::ContentHint::Uppercase)) {
|
||||
hints |= Qt::ImhPreferUppercase;
|
||||
}
|
||||
if (contentHints.testFlag(TextInputInterface::ContentHint::Titlecase)) {
|
||||
// no mapping in Qt
|
||||
}
|
||||
if (contentHints.testFlag(TextInputInterface::ContentHint::HiddenText)) {
|
||||
hints |= Qt::ImhHiddenText;
|
||||
}
|
||||
if (contentHints.testFlag(TextInputInterface::ContentHint::SensitiveData)) {
|
||||
hints |= Qt::ImhSensitiveData;
|
||||
}
|
||||
if (contentHints.testFlag(TextInputInterface::ContentHint::Latin)) {
|
||||
hints |= Qt::ImhPreferLatin;
|
||||
}
|
||||
if (contentHints.testFlag(TextInputInterface::ContentHint::MultiLine)) {
|
||||
hints |= Qt::ImhMultiLine;
|
||||
}
|
||||
switch (ti->contentPurpose()) {
|
||||
case TextInputInterface::ContentPurpose::Digits:
|
||||
hints |= Qt::ImhDigitsOnly;
|
||||
break;
|
||||
case TextInputInterface::ContentPurpose::Number:
|
||||
hints |= Qt::ImhFormattedNumbersOnly;
|
||||
break;
|
||||
case TextInputInterface::ContentPurpose::Phone:
|
||||
hints |= Qt::ImhDialableCharactersOnly;
|
||||
break;
|
||||
case TextInputInterface::ContentPurpose::Url:
|
||||
hints |= Qt::ImhUrlCharactersOnly;
|
||||
case TextInputInterface::ContentPurpose::Email:
|
||||
hints |= Qt::ImhEmailCharactersOnly;
|
||||
break;
|
||||
case TextInputInterface::ContentPurpose::Date:
|
||||
hints |= Qt::ImhDate;
|
||||
break;
|
||||
case TextInputInterface::ContentPurpose::Time:
|
||||
hints |= Qt::ImhTime;
|
||||
break;
|
||||
case TextInputInterface::ContentPurpose::Datetime:
|
||||
hints |= Qt::ImhDate;
|
||||
hints |= Qt::ImhTime;
|
||||
break;
|
||||
case TextInputInterface::ContentPurpose::Name:
|
||||
// no mapping in Qt
|
||||
case TextInputInterface::ContentPurpose::Password:
|
||||
// no mapping in Qt
|
||||
case TextInputInterface::ContentPurpose::Terminal:
|
||||
// no mapping in Qt
|
||||
case TextInputInterface::ContentPurpose::Normal:
|
||||
// that's the default
|
||||
case TextInputInterface::ContentPurpose::Alpha:
|
||||
// no mapping in Qt
|
||||
break;
|
||||
}
|
||||
event->setValue(Qt::ImHints, QVariant(int(hints)));
|
||||
} else {
|
||||
event->setValue(Qt::ImHints, Qt::ImhNone);
|
||||
}
|
||||
}
|
||||
if (event->queries().testFlag(Qt::ImPreferredLanguage)) {
|
||||
// not used by virtual keyboard
|
||||
}
|
||||
if (event->queries().testFlag(Qt::ImPlatformData)) {
|
||||
// not used by virtual keyboard
|
||||
}
|
||||
if (event->queries().testFlag(Qt::ImAbsolutePosition)) {
|
||||
// not used by virtual keyboard
|
||||
}
|
||||
if (event->queries().testFlag(Qt::ImTextBeforeCursor)) {
|
||||
// not used by virtual keyboard
|
||||
}
|
||||
if (event->queries().testFlag(Qt::ImTextAfterCursor)) {
|
||||
// not used by virtual keyboard
|
||||
}
|
||||
event->accept();
|
||||
return true;
|
||||
}
|
||||
return QObject::event(e);
|
||||
}
|
||||
|
||||
bool VirtualKeyboard::eventFilter(QObject *o, QEvent *e)
|
||||
{
|
||||
if (o != m_inputWindow.data() || !m_inputWindow->isVisible()) {
|
||||
return false;
|
||||
}
|
||||
if (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease) {
|
||||
QKeyEvent *event = static_cast<QKeyEvent*>(e);
|
||||
if (event->nativeScanCode() == 0) {
|
||||
// this is a key composed by the virtual keyboard - we need to send it to the client
|
||||
// TODO: proper xkb support in KWindowSystem needed
|
||||
int sym = 0;
|
||||
KKeyServer::keyQtToSymX(event->key(), &sym);
|
||||
if (sym != 0) {
|
||||
if (waylandServer()) {
|
||||
auto t = waylandServer()->seat()->focusedTextInput();
|
||||
if (t && t->isEnabled()) {
|
||||
if (e->type() == QEvent::KeyPress) {
|
||||
t->keysymPressed(sym);
|
||||
} else if (e->type() == QEvent::KeyRelease) {
|
||||
t->keysymReleased(sym);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QWindow *VirtualKeyboard::inputPanel() const
|
||||
{
|
||||
return m_inputWindow.data();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#ifndef KWIN_INPUT_METHODS_H
|
||||
#define KWIN_INPUT_METHODS_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <kwinglobals.h>
|
||||
#include <kwin_export.h>
|
||||
|
||||
class QQuickView;
|
||||
class QWindow;
|
||||
class KStatusNotifierItem;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class KWIN_EXPORT VirtualKeyboard : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
virtual ~VirtualKeyboard();
|
||||
|
||||
void init();
|
||||
|
||||
bool event(QEvent *e) override;
|
||||
bool eventFilter(QObject *o, QEvent *event) override;
|
||||
|
||||
QWindow *inputPanel() const;
|
||||
|
||||
private:
|
||||
void show();
|
||||
void hide();
|
||||
void setEnabled(bool enable);
|
||||
void updateSni();
|
||||
|
||||
bool m_enabled = false;
|
||||
KStatusNotifierItem *m_sni = nullptr;
|
||||
QScopedPointer<QQuickView> m_inputWindow;
|
||||
QMetaObject::Connection m_waylandShowConnection;
|
||||
QMetaObject::Connection m_waylandHideConnection;
|
||||
QMetaObject::Connection m_waylandHintsConnection;
|
||||
QMetaObject::Connection m_waylandSurroundingTextConnection;
|
||||
QMetaObject::Connection m_waylandResetConnection;
|
||||
QMetaObject::Connection m_waylandEnabledConnection;
|
||||
KWIN_SINGLETON(VirtualKeyboard)
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue