Split implementation of keyboard layout handling into a dedicated class

Summary:
So far the implementation of keyboard layout handling was split between
KeyboardInputRedirection and Xkb. KeyboardInputRedirection registered
the global shortcut and did the handling for layout switch and config
changes. Xkb did the notification on layout change.

Layout changes can nowadays be detected through an InputEventSpy. It
can only happen after a key change or an explicit layout switch. Thus
it does not need to be in Xkb anymore which allows to reduce Xkb to
only care about the Xkb keymap and state tracking.

This change introduces a new class KeyboardLayout which is an
InputEventSpy and takes over the task of the layout change notification
from Xkb and the layout management from KeyboardInputRedirection. Thus
everything related to management of keyboard layout is together in one
class.

This allows in future to add unit test to it (requires further cleanup
of Xkb to be able to use it and drop the InputRedirection dependency) and
opens the possibility to also take over keyboard layout management on X11
for the Plasma desktop.

Test Plan: Manual testing

Reviewers: #kwin, #plasma_on_wayland

Subscribers: plasma-devel, kwin

Tags: #plasma_on_wayland, #kwin

Differential Revision: https://phabricator.kde.org/D4135
icc-effect-5.14.5
Martin Gräßlin 2017-01-14 18:06:41 +01:00
parent 437edb45ca
commit d6c0a5414e
5 changed files with 186 additions and 43 deletions

View File

@ -370,6 +370,7 @@ set(kwin_KDEINIT_SRCS
input_event.cpp
input_event_spy.cpp
keyboard_input.cpp
keyboard_layout.cpp
pointer_input.cpp
touch_input.cpp
netinfo.cpp

View File

@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "keyboard_input.h"
#include "input_event.h"
#include "input_event_spy.h"
#include "keyboard_layout.h"
#include "abstract_client.h"
#include "options.h"
#include "utils.h"
@ -377,19 +378,6 @@ void Xkb::updateModifiers()
const xkb_layout_index_t layout = xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE);
if (layout != m_currentLayout) {
m_currentLayout = layout;
// notify OSD service about the new layout
if (kwinApp()->usesLibinput()) {
// only if kwin is in charge of keyboard input
QDBusMessage msg = QDBusMessage::createMethodCall(
QStringLiteral("org.kde.plasmashell"),
QStringLiteral("/org/kde/osdService"),
QStringLiteral("org.kde.osdService"),
QStringLiteral("kbdLayoutChanged"));
msg << QString::fromLocal8Bit(xkb_keymap_layout_get_name(m_keymap, layout));
QDBusConnection::sessionBus().asyncCall(msg);
}
}
if (waylandServer()) {
waylandServer()->seat()->updateKeyboardModifiers(xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_DEPRESSED)),
@ -399,6 +387,11 @@ void Xkb::updateModifiers()
}
}
QString Xkb::layoutName() const
{
return QString::fromLocal8Bit(xkb_keymap_layout_get_name(m_keymap, m_currentLayout));
}
void Xkb::updateConsumedModifiers(uint32_t key)
{
Qt::KeyboardModifiers mods = Qt::NoModifier;
@ -564,6 +557,9 @@ void KeyboardInputRedirection::init()
m_input->installInputEventSpy(new KeyStateChangedSpy(m_input));
m_modifiersChangedSpy = new ModifiersChangedSpy(m_input);
m_input->installInputEventSpy(m_modifiersChangedSpy);
m_keyboardLayout = new KeyboardLayout(m_xkb.data());
m_keyboardLayout->init();
m_input->installInputEventSpy(m_keyboardLayout);
// setup key repeat
m_keyRepeat.timer = new QTimer(this);
@ -593,33 +589,6 @@ void KeyboardInputRedirection::init()
if (waylandServer()->hasScreenLockerIntegration()) {
connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this, &KeyboardInputRedirection::update);
}
QAction *switchKeyboardAction = new QAction(this);
switchKeyboardAction->setObjectName(QStringLiteral("Switch to Next Keyboard Layout"));
switchKeyboardAction->setProperty("componentName", QStringLiteral("KDE Keyboard Layout Switcher"));
const QKeySequence sequence = QKeySequence(Qt::ALT+Qt::CTRL+Qt::Key_K);
KGlobalAccel::self()->setDefaultShortcut(switchKeyboardAction, QList<QKeySequence>({sequence}));
KGlobalAccel::self()->setShortcut(switchKeyboardAction, QList<QKeySequence>({sequence}));
m_input->registerShortcut(sequence, switchKeyboardAction);
connect(switchKeyboardAction, &QAction::triggered, this,
[this] {
m_xkb->switchToNextLayout();
}
);
QDBusConnection::sessionBus().connect(QString(),
QStringLiteral("/Layouts"),
QStringLiteral("org.kde.keyboard"),
QStringLiteral("reloadConfig"),
this,
SLOT(reconfigure()));
m_xkb->reconfigure();
}
void KeyboardInputRedirection::reconfigure()
{
m_xkb->reconfigure();
}
void KeyboardInputRedirection::update()
@ -737,6 +706,7 @@ void KeyboardInputRedirection::processModifiers(uint32_t modsDepressed, uint32_t
// TODO: send to proper Client and also send when active Client changes
m_xkb->updateModifiers(modsDepressed, modsLatched, modsLocked, group);
m_modifiersChangedSpy->updateModifiers(modifiers());
m_keyboardLayout->checkLayoutChange();
}
void KeyboardInputRedirection::processKeymapChange(int fd, uint32_t size)
@ -746,6 +716,7 @@ void KeyboardInputRedirection::processKeymapChange(int fd, uint32_t size)
}
// TODO: should we pass the keymap to our Clients? Or only to the currently active one and update
m_xkb->installKeymap(fd, size);
m_keyboardLayout->resetLayout();
}
}

View File

@ -43,6 +43,7 @@ namespace KWin
{
class InputRedirection;
class KeyboardLayout;
class ModifiersChangedSpy;
class Toplevel;
@ -94,6 +95,7 @@ public:
quint32 currentLayout() const {
return m_currentLayout;
}
QString layoutName() const;
private:
xkb_keymap *loadKeymapFromConfig();
@ -167,9 +169,6 @@ public:
Q_SIGNALS:
void ledsChanged(KWin::Xkb::LEDs);
private Q_SLOTS:
void reconfigure();
private:
InputRedirection *m_input;
bool m_inited = false;
@ -181,6 +180,7 @@ private:
QTimer *timer = nullptr;
} m_keyRepeat;
ModifiersChangedSpy *m_modifiersChangedSpy = nullptr;
KeyboardLayout *m_keyboardLayout = nullptr;
};
inline

115
keyboard_layout.cpp Normal file
View File

@ -0,0 +1,115 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2016, 2017 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 "keyboard_layout.h"
#include "keyboard_input.h"
#include "input_event.h"
#include "main.h"
#include "platform.h"
#include <KGlobalAccel>
#include <QAction>
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDBusPendingCall>
namespace KWin
{
KeyboardLayout::KeyboardLayout(Xkb *xkb)
: QObject()
, m_xkb(xkb)
{
}
KeyboardLayout::~KeyboardLayout() = default;
void KeyboardLayout::init()
{
QAction *switchKeyboardAction = new QAction(this);
switchKeyboardAction->setObjectName(QStringLiteral("Switch to Next Keyboard Layout"));
switchKeyboardAction->setProperty("componentName", QStringLiteral("KDE Keyboard Layout Switcher"));
const QKeySequence sequence = QKeySequence(Qt::ALT+Qt::CTRL+Qt::Key_K);
KGlobalAccel::self()->setDefaultShortcut(switchKeyboardAction, QList<QKeySequence>({sequence}));
KGlobalAccel::self()->setShortcut(switchKeyboardAction, QList<QKeySequence>({sequence}));
kwinApp()->platform()->setupActionForGlobalAccel(switchKeyboardAction);
connect(switchKeyboardAction, &QAction::triggered, this,
[this] {
m_xkb->switchToNextLayout();
checkLayoutChange();
}
);
QDBusConnection::sessionBus().connect(QString(),
QStringLiteral("/Layouts"),
QStringLiteral("org.kde.keyboard"),
QStringLiteral("reloadConfig"),
this,
SLOT(reconfigure()));
reconfigure();
}
void KeyboardLayout::reconfigure()
{
m_xkb->reconfigure();
resetLayout();
}
void KeyboardLayout::resetLayout()
{
m_layout = m_xkb->currentLayout();
}
void KeyboardLayout::keyEvent(KeyEvent *event)
{
if (!event->isAutoRepeat()) {
checkLayoutChange();
}
}
void KeyboardLayout::checkLayoutChange()
{
const auto layout = m_xkb->currentLayout();
if (m_layout == layout) {
return;
}
m_layout = layout;
notifyLayoutChange();
}
void KeyboardLayout::notifyLayoutChange()
{
// notify OSD service about the new layout
if (!kwinApp()->usesLibinput()) {
return;
}
// only if kwin is in charge of keyboard input
QDBusMessage msg = QDBusMessage::createMethodCall(
QStringLiteral("org.kde.plasmashell"),
QStringLiteral("/org/kde/osdService"),
QStringLiteral("org.kde.osdService"),
QStringLiteral("kbdLayoutChanged"));
msg << m_xkb->layoutName();
QDBusConnection::sessionBus().asyncCall(msg);
}
}

56
keyboard_layout.h Normal file
View File

@ -0,0 +1,56 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2016, 2017 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_KEYBOARD_LAYOUT_H
#define KWIN_KEYBOARD_LAYOUT_H
#include "input_event_spy.h"
#include <QObject>
typedef uint32_t xkb_layout_index_t;
namespace KWin
{
class Xkb;
class KeyboardLayout : public QObject, public InputEventSpy
{
Q_OBJECT
public:
explicit KeyboardLayout(Xkb *xkb);
~KeyboardLayout() override;
void init();
void checkLayoutChange();
void resetLayout();
void keyEvent(KeyEvent *event) override;
private Q_SLOTS:
void reconfigure();
private:
void notifyLayoutChange();
Xkb *m_xkb;
xkb_layout_index_t m_layout = 0;
};
}
#endif