diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -370,6 +370,7 @@ input_event.cpp input_event_spy.cpp keyboard_input.cpp + keyboard_layout.cpp pointer_input.cpp touch_input.cpp netinfo.cpp diff --git a/keyboard_input.h b/keyboard_input.h --- a/keyboard_input.h +++ b/keyboard_input.h @@ -43,6 +43,7 @@ { class InputRedirection; +class KeyboardLayout; class ModifiersChangedSpy; class Toplevel; @@ -94,6 +95,7 @@ quint32 currentLayout() const { return m_currentLayout; } + QString layoutName() const; private: xkb_keymap *loadKeymapFromConfig(); @@ -167,9 +169,6 @@ Q_SIGNALS: void ledsChanged(KWin::Xkb::LEDs); -private Q_SLOTS: - void reconfigure(); - private: InputRedirection *m_input; bool m_inited = false; @@ -181,6 +180,7 @@ QTimer *timer = nullptr; } m_keyRepeat; ModifiersChangedSpy *m_modifiersChangedSpy = nullptr; + KeyboardLayout *m_keyboardLayout = nullptr; }; inline diff --git a/keyboard_input.cpp b/keyboard_input.cpp --- a/keyboard_input.cpp +++ b/keyboard_input.cpp @@ -20,6 +20,7 @@ #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 @@ 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 @@ } } +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 @@ 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 @@ 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({sequence})); - KGlobalAccel::self()->setShortcut(switchKeyboardAction, QList({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 @@ // 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 @@ } // 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(); } } diff --git a/keyboard_layout.h b/keyboard_layout.h new file mode 100644 --- /dev/null +++ b/keyboard_layout.h @@ -0,0 +1,56 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2016, 2017 Martin Gräßlin + +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 . +*********************************************************************/ +#ifndef KWIN_KEYBOARD_LAYOUT_H +#define KWIN_KEYBOARD_LAYOUT_H + +#include "input_event_spy.h" +#include +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 diff --git a/keyboard_layout.cpp b/keyboard_layout.cpp new file mode 100644 --- /dev/null +++ b/keyboard_layout.cpp @@ -0,0 +1,115 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2016, 2017 Martin Gräßlin + +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 . +*********************************************************************/ +#include "keyboard_layout.h" +#include "keyboard_input.h" +#include "input_event.h" +#include "main.h" +#include "platform.h" + +#include +#include +#include +#include +#include + +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({sequence})); + KGlobalAccel::self()->setShortcut(switchKeyboardAction, QList({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); +} + +}