2020-08-02 22:22:19 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
2016-04-29 13:05:03 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
|
2016-04-29 13:05:03 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2016-04-29 13:05:03 +00:00
|
|
|
#include "virtualkeyboard.h"
|
2017-10-06 19:22:50 +00:00
|
|
|
#include "virtualkeyboard_dbus.h"
|
2016-08-03 06:31:58 +00:00
|
|
|
#include "input.h"
|
2017-08-16 19:18:01 +00:00
|
|
|
#include "keyboard_input.h"
|
2016-04-29 13:05:03 +00:00
|
|
|
#include "utils.h"
|
|
|
|
#include "screens.h"
|
|
|
|
#include "wayland_server.h"
|
|
|
|
#include "workspace.h"
|
2017-08-16 19:18:01 +00:00
|
|
|
#include "xkb.h"
|
2020-02-06 09:33:23 +00:00
|
|
|
#include "screenlockerwatcher.h"
|
2016-04-29 13:05:03 +00:00
|
|
|
|
2020-04-29 15:18:41 +00:00
|
|
|
#include <KWaylandServer/display.h>
|
|
|
|
#include <KWaylandServer/seat_interface.h>
|
|
|
|
#include <KWaylandServer/textinput_interface.h>
|
|
|
|
#include <KWaylandServer/surface_interface.h>
|
2020-07-11 16:40:28 +00:00
|
|
|
#include <KWaylandServer/inputmethod_v1_interface.h>
|
2016-04-29 13:05:03 +00:00
|
|
|
|
|
|
|
#include <KStatusNotifierItem>
|
|
|
|
#include <KLocalizedString>
|
|
|
|
|
|
|
|
#include <QDBusConnection>
|
|
|
|
#include <QDBusPendingCall>
|
|
|
|
#include <QDBusMessage>
|
|
|
|
#include <QGuiApplication>
|
|
|
|
#include <QQmlComponent>
|
|
|
|
#include <QQmlContext>
|
|
|
|
#include <QQmlEngine>
|
|
|
|
#include <QQuickItem>
|
2019-08-09 06:10:22 +00:00
|
|
|
#include <QQuickView>
|
2016-04-29 13:05:03 +00:00
|
|
|
#include <QQuickWindow>
|
virtualkeyboard: resize the focused window to make room for the keyboard
Summary:
alternative approach: try to resize the winidow to make room for the keyboard.
the new input wayland protocol doesn't have anymore the overlap rectangle (and it would not be going to work with qwidget apps anyways)
in the future will probably be needed anextension to the input protocol v3 which partially gets back this, tough window resizing is needed regardless
what's missing: the resize should be "temporary" and the window should be restored to its previous geometry when the keyboard closes
Test Plan: tested with test QML code
Reviewers: #plasma, #kwin, bshah, graesslin, romangg, davidedmundson
Reviewed By: #plasma, #kwin, romangg, davidedmundson
Subscribers: nicolasfella, mart, kwin, davidedmundson, graesslin
Tags: #kwin
Maniphest Tasks: T9815
Differential Revision: https://phabricator.kde.org/D18818
2019-03-20 10:04:51 +00:00
|
|
|
#include <QTimer>
|
2016-08-17 15:04:39 +00:00
|
|
|
// xkbcommon
|
|
|
|
#include <xkbcommon/xkbcommon.h>
|
2016-04-29 13:05:03 +00:00
|
|
|
|
2020-04-29 15:18:41 +00:00
|
|
|
using namespace KWaylandServer;
|
2016-04-29 13:05:03 +00:00
|
|
|
|
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
|
|
|
KWIN_SINGLETON_FACTORY(VirtualKeyboard)
|
|
|
|
|
|
|
|
VirtualKeyboard::VirtualKeyboard(QObject *parent)
|
|
|
|
: QObject(parent)
|
|
|
|
{
|
virtualkeyboard: resize the focused window to make room for the keyboard
Summary:
alternative approach: try to resize the winidow to make room for the keyboard.
the new input wayland protocol doesn't have anymore the overlap rectangle (and it would not be going to work with qwidget apps anyways)
in the future will probably be needed anextension to the input protocol v3 which partially gets back this, tough window resizing is needed regardless
what's missing: the resize should be "temporary" and the window should be restored to its previous geometry when the keyboard closes
Test Plan: tested with test QML code
Reviewers: #plasma, #kwin, bshah, graesslin, romangg, davidedmundson
Reviewed By: #plasma, #kwin, romangg, davidedmundson
Subscribers: nicolasfella, mart, kwin, davidedmundson, graesslin
Tags: #kwin
Maniphest Tasks: T9815
Differential Revision: https://phabricator.kde.org/D18818
2019-03-20 10:04:51 +00:00
|
|
|
m_floodTimer = new QTimer(this);
|
|
|
|
m_floodTimer->setSingleShot(true);
|
|
|
|
m_floodTimer->setInterval(250);
|
2016-04-29 13:05:03 +00:00
|
|
|
// 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()
|
|
|
|
{
|
2020-02-06 09:33:23 +00:00
|
|
|
connect(ScreenLockerWatcher::self(), &ScreenLockerWatcher::aboutToLock, this, &VirtualKeyboard::hide);
|
|
|
|
|
2016-04-29 13:05:03 +00:00
|
|
|
if (waylandServer()) {
|
2016-08-03 06:31:58 +00:00
|
|
|
m_enabled = !input()->hasAlphaNumericKeyboard();
|
2018-12-10 10:57:24 +00:00
|
|
|
qCDebug(KWIN_VIRTUALKEYBOARD) << "enabled by default: " << m_enabled;
|
2016-08-03 06:31:58 +00:00
|
|
|
connect(input(), &InputRedirection::hasAlphaNumericKeyboardChanged, this,
|
|
|
|
[this] (bool set) {
|
2018-12-10 10:57:24 +00:00
|
|
|
qCDebug(KWIN_VIRTUALKEYBOARD) << "AlphaNumeric Keyboard changed:" << set << "toggling VirtualKeyboard.";
|
2016-08-03 06:31:58 +00:00
|
|
|
setEnabled(!set);
|
2016-04-29 13:05:03 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-12-10 10:57:24 +00:00
|
|
|
qCDebug(KWIN_VIRTUALKEYBOARD) << "Registering the SNI";
|
2016-04-29 13:05:03 +00:00
|
|
|
m_sni = new KStatusNotifierItem(QStringLiteral("kwin-virtual-keyboard"), this);
|
2017-01-26 19:33:59 +00:00
|
|
|
m_sni->setStandardActionsEnabled(false);
|
2016-04-29 13:05:03 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
);
|
2017-10-06 19:22:50 +00:00
|
|
|
connect(this, &VirtualKeyboard::enabledChanged, this, &VirtualKeyboard::updateSni);
|
|
|
|
|
|
|
|
auto dbus = new VirtualKeyboardDBus(this);
|
2018-12-10 10:57:24 +00:00
|
|
|
qCDebug(KWIN_VIRTUALKEYBOARD) << "Registering the DBus interface";
|
2017-10-06 19:22:50 +00:00
|
|
|
dbus->setEnabled(m_enabled);
|
|
|
|
connect(dbus, &VirtualKeyboardDBus::activateRequested, this, &VirtualKeyboard::setEnabled);
|
|
|
|
connect(this, &VirtualKeyboard::enabledChanged, dbus, &VirtualKeyboardDBus::setEnabled);
|
2016-04-29 13:05:03 +00:00
|
|
|
|
|
|
|
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();
|
2020-07-11 16:40:28 +00:00
|
|
|
|
|
|
|
auto inputPanel = waylandServer()->display()->createInputPanelInterface(this);
|
2020-07-27 19:30:49 +00:00
|
|
|
connect(inputPanel, &InputPanelV1Interface::inputPanelSurfaceAdded, this, [this] (InputPanelSurfaceV1Interface *surface) {
|
2020-07-11 16:40:28 +00:00
|
|
|
m_inputClient = waylandServer()->createInputPanelClient(surface);
|
|
|
|
auto refreshFrame = [this] {
|
2020-07-27 19:30:49 +00:00
|
|
|
if (!m_trackedClient) {
|
2020-07-11 16:40:28 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-27 19:30:49 +00:00
|
|
|
m_trackedClient->setVirtualKeyboardGeometry(m_inputClient ? m_inputClient->inputGeometry() : QRect());
|
|
|
|
};
|
2020-07-11 16:40:28 +00:00
|
|
|
connect(surface->surface(), &SurfaceInterface::inputChanged, this, refreshFrame);
|
2020-07-27 19:30:49 +00:00
|
|
|
connect(surface->surface(), &QObject::destroyed, this, [this] {
|
|
|
|
if (m_trackedClient) {
|
|
|
|
m_trackedClient->setVirtualKeyboardGeometry({});
|
|
|
|
}
|
2020-07-11 16:40:28 +00:00
|
|
|
});
|
2020-07-27 19:30:49 +00:00
|
|
|
updateInputPanelState();
|
2020-07-11 16:40:28 +00:00
|
|
|
refreshFrame();
|
|
|
|
});
|
|
|
|
|
2016-04-29 13:05:03 +00:00
|
|
|
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,
|
2019-08-09 06:10:22 +00:00
|
|
|
[] {
|
2016-04-29 13:05:03 +00:00
|
|
|
qApp->inputMethod()->update(Qt::ImSurroundingText | Qt::ImCursorPosition | Qt::ImAnchorPosition);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
m_waylandHintsConnection = connect(t, &TextInputInterface::contentTypeChanged, this,
|
2019-08-09 06:10:22 +00:00
|
|
|
[] {
|
2016-04-29 13:05:03 +00:00
|
|
|
qApp->inputMethod()->update(Qt::ImHints);
|
|
|
|
}
|
|
|
|
);
|
2020-07-11 16:40:28 +00:00
|
|
|
m_waylandResetConnection = connect(t, &TextInputInterface::requestReset, this, [t] {
|
|
|
|
auto inputContext = waylandServer()->inputMethod()->context();
|
|
|
|
if (!inputContext) {
|
|
|
|
return;
|
2016-04-29 13:05:03 +00:00
|
|
|
}
|
2020-07-11 16:40:28 +00:00
|
|
|
|
|
|
|
inputContext->sendReset();
|
|
|
|
inputContext->sendSurroundingText(QString::fromUtf8(t->surroundingText()), t->surroundingTextCursorPosition(), t->surroundingTextSelectionAnchor());
|
|
|
|
inputContext->sendPreferredLanguage(QString::fromUtf8(t->preferredLanguage()));
|
|
|
|
});
|
|
|
|
m_waylandEnabledConnection = connect(t, &TextInputInterface::enabledChanged, this, [t, this] {
|
|
|
|
if (t->isEnabled()) {
|
|
|
|
waylandServer()->inputMethod()->sendDeactivate();
|
|
|
|
waylandServer()->inputMethod()->sendActivate();
|
|
|
|
adoptInputMethodContext();
|
|
|
|
} else {
|
|
|
|
waylandServer()->inputMethod()->sendDeactivate();
|
2020-07-27 19:30:49 +00:00
|
|
|
hide();
|
2020-07-11 16:40:28 +00:00
|
|
|
}
|
|
|
|
qApp->inputMethod()->update(Qt::ImQueryAll);
|
|
|
|
});
|
virtualkeyboard: resize the focused window to make room for the keyboard
Summary:
alternative approach: try to resize the winidow to make room for the keyboard.
the new input wayland protocol doesn't have anymore the overlap rectangle (and it would not be going to work with qwidget apps anyways)
in the future will probably be needed anextension to the input protocol v3 which partially gets back this, tough window resizing is needed regardless
what's missing: the resize should be "temporary" and the window should be restored to its previous geometry when the keyboard closes
Test Plan: tested with test QML code
Reviewers: #plasma, #kwin, bshah, graesslin, romangg, davidedmundson
Reviewed By: #plasma, #kwin, romangg, davidedmundson
Subscribers: nicolasfella, mart, kwin, davidedmundson, graesslin
Tags: #kwin
Maniphest Tasks: T9815
Differential Revision: https://phabricator.kde.org/D18818
2019-03-20 10:04:51 +00:00
|
|
|
|
2020-03-04 07:55:26 +00:00
|
|
|
auto newClient = waylandServer()->findClient(waylandServer()->seat()->focusedTextInputSurface());
|
virtualkeyboard: resize the focused window to make room for the keyboard
Summary:
alternative approach: try to resize the winidow to make room for the keyboard.
the new input wayland protocol doesn't have anymore the overlap rectangle (and it would not be going to work with qwidget apps anyways)
in the future will probably be needed anextension to the input protocol v3 which partially gets back this, tough window resizing is needed regardless
what's missing: the resize should be "temporary" and the window should be restored to its previous geometry when the keyboard closes
Test Plan: tested with test QML code
Reviewers: #plasma, #kwin, bshah, graesslin, romangg, davidedmundson
Reviewed By: #plasma, #kwin, romangg, davidedmundson
Subscribers: nicolasfella, mart, kwin, davidedmundson, graesslin
Tags: #kwin
Maniphest Tasks: T9815
Differential Revision: https://phabricator.kde.org/D18818
2019-03-20 10:04:51 +00:00
|
|
|
// Reset the old client virtual keybaord geom if necessary
|
|
|
|
// Old and new clients could be the same if focus moves between subsurfaces
|
|
|
|
if (newClient != m_trackedClient) {
|
|
|
|
if (m_trackedClient) {
|
|
|
|
m_trackedClient->setVirtualKeyboardGeometry(QRect());
|
|
|
|
}
|
|
|
|
m_trackedClient = newClient;
|
|
|
|
}
|
|
|
|
|
|
|
|
updateInputPanelState();
|
2016-04-29 13:05:03 +00:00
|
|
|
} 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);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
virtualkeyboard: resize the focused window to make room for the keyboard
Summary:
alternative approach: try to resize the winidow to make room for the keyboard.
the new input wayland protocol doesn't have anymore the overlap rectangle (and it would not be going to work with qwidget apps anyways)
in the future will probably be needed anextension to the input protocol v3 which partially gets back this, tough window resizing is needed regardless
what's missing: the resize should be "temporary" and the window should be restored to its previous geometry when the keyboard closes
Test Plan: tested with test QML code
Reviewers: #plasma, #kwin, bshah, graesslin, romangg, davidedmundson
Reviewed By: #plasma, #kwin, romangg, davidedmundson
Subscribers: nicolasfella, mart, kwin, davidedmundson, graesslin
Tags: #kwin
Maniphest Tasks: T9815
Differential Revision: https://phabricator.kde.org/D18818
2019-03-20 10:04:51 +00:00
|
|
|
connect(qApp->inputMethod(), &QInputMethod::visibleChanged, this, &VirtualKeyboard::updateInputPanelState);
|
2020-07-27 19:30:49 +00:00
|
|
|
}
|
virtualkeyboard: resize the focused window to make room for the keyboard
Summary:
alternative approach: try to resize the winidow to make room for the keyboard.
the new input wayland protocol doesn't have anymore the overlap rectangle (and it would not be going to work with qwidget apps anyways)
in the future will probably be needed anextension to the input protocol v3 which partially gets back this, tough window resizing is needed regardless
what's missing: the resize should be "temporary" and the window should be restored to its previous geometry when the keyboard closes
Test Plan: tested with test QML code
Reviewers: #plasma, #kwin, bshah, graesslin, romangg, davidedmundson
Reviewed By: #plasma, #kwin, romangg, davidedmundson
Subscribers: nicolasfella, mart, kwin, davidedmundson, graesslin
Tags: #kwin
Maniphest Tasks: T9815
Differential Revision: https://phabricator.kde.org/D18818
2019-03-20 10:04:51 +00:00
|
|
|
|
2020-07-27 19:30:49 +00:00
|
|
|
void VirtualKeyboard::show()
|
|
|
|
{
|
|
|
|
auto t = waylandServer()->seat()->focusedTextInput();
|
|
|
|
if (t) {
|
|
|
|
Q_EMIT t->enabledChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VirtualKeyboard::hide()
|
|
|
|
{
|
|
|
|
waylandServer()->inputMethod()->sendDeactivate();
|
|
|
|
updateInputPanelState();
|
2016-04-29 13:05:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VirtualKeyboard::setEnabled(bool enabled)
|
|
|
|
{
|
|
|
|
if (m_enabled == enabled) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_enabled = enabled;
|
|
|
|
qApp->inputMethod()->update(Qt::ImQueryAll);
|
2017-10-06 19:22:50 +00:00
|
|
|
emit enabledChanged(m_enabled);
|
2016-04-29 13:05:03 +00:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2020-07-11 16:40:28 +00:00
|
|
|
static void keysymReceived(quint32 serial, quint32 time, quint32 sym, bool pressed, Qt::KeyboardModifiers modifiers)
|
|
|
|
{
|
|
|
|
Q_UNUSED(serial)
|
|
|
|
Q_UNUSED(time)
|
|
|
|
auto t = waylandServer()->seat()->focusedTextInput();
|
|
|
|
if (t && t->isEnabled()) {
|
|
|
|
if (pressed) {
|
|
|
|
t->keysymPressed(sym, modifiers);
|
|
|
|
} else {
|
|
|
|
t->keysymReleased(sym, modifiers);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void commitString(qint32 serial, const QString &text)
|
|
|
|
{
|
|
|
|
Q_UNUSED(serial)
|
|
|
|
auto t = waylandServer()->seat()->focusedTextInput();
|
|
|
|
if (t && t->isEnabled()) {
|
|
|
|
t->commit(text.toUtf8());
|
|
|
|
t->preEdit({}, {});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void setPreeditCursor(qint32 index)
|
|
|
|
{
|
|
|
|
auto t = waylandServer()->seat()->focusedTextInput();
|
|
|
|
if (t && t->isEnabled()) {
|
|
|
|
t->setPreEditCursor(index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void setPreeditString(uint32_t serial, const QString &text, const QString &commit)
|
|
|
|
{
|
|
|
|
Q_UNUSED(serial)
|
|
|
|
auto t = waylandServer()->seat()->focusedTextInput();
|
|
|
|
if (t && t->isEnabled()) {
|
|
|
|
t->preEdit(text.toUtf8(), commit.toUtf8());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void deleteSurroundingText(int32_t index, uint32_t length)
|
|
|
|
{
|
|
|
|
auto t = waylandServer()->seat()->focusedTextInput();
|
|
|
|
if (t && t->isEnabled()) {
|
|
|
|
t->deleteSurroundingText(index, length);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void setCursorPosition(qint32 index, qint32 anchor)
|
|
|
|
{
|
|
|
|
auto t = waylandServer()->seat()->focusedTextInput();
|
|
|
|
if (t && t->isEnabled()) {
|
|
|
|
t->setCursorPosition(index, anchor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void setLanguage(uint32_t serial, const QString &language)
|
|
|
|
{
|
|
|
|
Q_UNUSED(serial)
|
|
|
|
auto t = waylandServer()->seat()->focusedTextInput();
|
|
|
|
if (t && t->isEnabled()) {
|
|
|
|
t->setLanguage(language.toUtf8());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void setTextDirection(uint32_t serial, Qt::LayoutDirection direction)
|
|
|
|
{
|
|
|
|
Q_UNUSED(serial)
|
|
|
|
auto t = waylandServer()->seat()->focusedTextInput();
|
|
|
|
if (t && t->isEnabled()) {
|
|
|
|
t->setTextDirection(direction);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VirtualKeyboard::adoptInputMethodContext()
|
|
|
|
{
|
|
|
|
auto inputContext = waylandServer()->inputMethod()->context();
|
|
|
|
TextInputInterface *ti = waylandServer()->seat()->focusedTextInput();
|
|
|
|
|
|
|
|
inputContext->sendSurroundingText(QString::fromUtf8(ti->surroundingText()), ti->surroundingTextCursorPosition(), ti->surroundingTextSelectionAnchor());
|
|
|
|
inputContext->sendPreferredLanguage(QString::fromUtf8(ti->preferredLanguage()));
|
|
|
|
|
|
|
|
connect(inputContext, &KWaylandServer::InputMethodContextV1Interface::keysym, waylandServer(), &keysymReceived);
|
|
|
|
connect(inputContext, &KWaylandServer::InputMethodContextV1Interface::commitString, waylandServer(), &commitString);
|
|
|
|
connect(inputContext, &KWaylandServer::InputMethodContextV1Interface::preeditCursor, waylandServer(), &setPreeditCursor);
|
|
|
|
connect(inputContext, &KWaylandServer::InputMethodContextV1Interface::preeditString, waylandServer(), &setPreeditString);
|
|
|
|
connect(inputContext, &KWaylandServer::InputMethodContextV1Interface::deleteSurroundingText, waylandServer(), &deleteSurroundingText);
|
|
|
|
connect(inputContext, &KWaylandServer::InputMethodContextV1Interface::cursorPosition, waylandServer(), &setCursorPosition);
|
|
|
|
connect(inputContext, &KWaylandServer::InputMethodContextV1Interface::language, waylandServer(), &setLanguage);
|
|
|
|
connect(inputContext, &KWaylandServer::InputMethodContextV1Interface::textDirection, waylandServer(), &setTextDirection);
|
|
|
|
}
|
|
|
|
|
2016-04-29 13:05:03 +00:00
|
|
|
void VirtualKeyboard::updateSni()
|
|
|
|
{
|
|
|
|
if (!m_sni) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (m_enabled) {
|
|
|
|
m_sni->setIconByName(QStringLiteral("input-keyboard-virtual-on"));
|
2019-03-19 20:54:21 +00:00
|
|
|
m_sni->setTitle(i18n("Virtual Keyboard: enabled"));
|
2016-04-29 13:05:03 +00:00
|
|
|
} else {
|
|
|
|
m_sni->setIconByName(QStringLiteral("input-keyboard-virtual-off"));
|
2019-03-19 20:54:21 +00:00
|
|
|
m_sni->setTitle(i18n("Virtual Keyboard: disabled"));
|
2016-04-29 13:05:03 +00:00
|
|
|
}
|
2019-03-19 20:54:21 +00:00
|
|
|
m_sni->setToolTipTitle(i18n("Whether to show the virtual keyboard on demand."));
|
2016-04-29 13:05:03 +00:00
|
|
|
}
|
|
|
|
|
virtualkeyboard: resize the focused window to make room for the keyboard
Summary:
alternative approach: try to resize the winidow to make room for the keyboard.
the new input wayland protocol doesn't have anymore the overlap rectangle (and it would not be going to work with qwidget apps anyways)
in the future will probably be needed anextension to the input protocol v3 which partially gets back this, tough window resizing is needed regardless
what's missing: the resize should be "temporary" and the window should be restored to its previous geometry when the keyboard closes
Test Plan: tested with test QML code
Reviewers: #plasma, #kwin, bshah, graesslin, romangg, davidedmundson
Reviewed By: #plasma, #kwin, romangg, davidedmundson
Subscribers: nicolasfella, mart, kwin, davidedmundson, graesslin
Tags: #kwin
Maniphest Tasks: T9815
Differential Revision: https://phabricator.kde.org/D18818
2019-03-20 10:04:51 +00:00
|
|
|
void VirtualKeyboard::updateInputPanelState()
|
|
|
|
{
|
|
|
|
if (!waylandServer()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto t = waylandServer()->seat()->focusedTextInput();
|
|
|
|
|
2020-07-11 16:40:28 +00:00
|
|
|
if (!t) {
|
virtualkeyboard: resize the focused window to make room for the keyboard
Summary:
alternative approach: try to resize the winidow to make room for the keyboard.
the new input wayland protocol doesn't have anymore the overlap rectangle (and it would not be going to work with qwidget apps anyways)
in the future will probably be needed anextension to the input protocol v3 which partially gets back this, tough window resizing is needed regardless
what's missing: the resize should be "temporary" and the window should be restored to its previous geometry when the keyboard closes
Test Plan: tested with test QML code
Reviewers: #plasma, #kwin, bshah, graesslin, romangg, davidedmundson
Reviewed By: #plasma, #kwin, romangg, davidedmundson
Subscribers: nicolasfella, mart, kwin, davidedmundson, graesslin
Tags: #kwin
Maniphest Tasks: T9815
Differential Revision: https://phabricator.kde.org/D18818
2019-03-20 10:04:51 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-27 19:30:49 +00:00
|
|
|
m_trackedClient->setVirtualKeyboardGeometry(m_inputClient ? m_inputClient->inputGeometry() : QRect());
|
|
|
|
t->setInputPanelState(m_inputClient && m_inputClient->isShown(false), QRect(0, 0, 0, 0));
|
2016-04-29 13:05:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2016-06-02 09:54:14 +00:00
|
|
|
if (contentHints.testFlag(TextInputInterface::ContentHint::LowerCase)) {
|
2016-04-29 13:05:03 +00:00
|
|
|
hints |= Qt::ImhPreferLowercase;
|
|
|
|
}
|
2016-06-02 09:54:14 +00:00
|
|
|
if (contentHints.testFlag(TextInputInterface::ContentHint::UpperCase)) {
|
2016-04-29 13:05:03 +00:00
|
|
|
hints |= Qt::ImhPreferUppercase;
|
|
|
|
}
|
2016-06-02 09:54:14 +00:00
|
|
|
if (contentHints.testFlag(TextInputInterface::ContentHint::TitleCase)) {
|
2016-04-29 13:05:03 +00:00
|
|
|
// 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;
|
2016-08-09 14:09:22 +00:00
|
|
|
break;
|
2016-04-29 13:05:03 +00:00
|
|
|
case TextInputInterface::ContentPurpose::Email:
|
|
|
|
hints |= Qt::ImhEmailCharactersOnly;
|
|
|
|
break;
|
|
|
|
case TextInputInterface::ContentPurpose::Date:
|
|
|
|
hints |= Qt::ImhDate;
|
|
|
|
break;
|
|
|
|
case TextInputInterface::ContentPurpose::Time:
|
|
|
|
hints |= Qt::ImhTime;
|
|
|
|
break;
|
2016-06-02 09:54:14 +00:00
|
|
|
case TextInputInterface::ContentPurpose::DateTime:
|
2016-04-29 13:05:03 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|