diff --git a/CMakeLists.txt b/CMakeLists.txt index e0a8d67cd1..e6eca355c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -332,6 +332,7 @@ set(kwin_KDEINIT_SRCS focuschain.cpp globalshortcuts.cpp input.cpp + keyboard_input.cpp pointer_input.cpp touch_input.cpp netinfo.cpp diff --git a/input.cpp b/input.cpp index fbec574917..b4158d7fbc 100644 --- a/input.cpp +++ b/input.cpp @@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "input.h" +#include "keyboard_input.h" #include "pointer_input.h" #include "touch_input.h" #include "client.h" @@ -65,222 +66,6 @@ along with this program. If not, see . namespace KWin { -Xkb::Xkb(InputRedirection *input) - : m_input(input) - , m_context(xkb_context_new(static_cast(0))) - , m_keymap(NULL) - , m_state(NULL) - , m_shiftModifier(0) - , m_controlModifier(0) - , m_altModifier(0) - , m_metaModifier(0) - , m_modifiers(Qt::NoModifier) -{ - if (!m_context) { - qCDebug(KWIN_CORE) << "Could not create xkb context"; - } else { - // load default keymap - xkb_keymap *keymap = xkb_keymap_new_from_names(m_context, nullptr, XKB_KEYMAP_COMPILE_NO_FLAGS); - if (keymap) { - updateKeymap(keymap); - } else { - qCDebug(KWIN_CORE) << "Could not create default xkb keymap"; - } - } -} - -Xkb::~Xkb() -{ - xkb_state_unref(m_state); - xkb_keymap_unref(m_keymap); - xkb_context_unref(m_context); -} - -void Xkb::installKeymap(int fd, uint32_t size) -{ - if (!m_context) { - return; - } - char *map = reinterpret_cast(mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0)); - if (map == MAP_FAILED) { - return; - } - xkb_keymap *keymap = xkb_keymap_new_from_string(m_context, map, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_MAP_COMPILE_PLACEHOLDER); - munmap(map, size); - if (!keymap) { - qCDebug(KWIN_CORE) << "Could not map keymap from file"; - return; - } - updateKeymap(keymap); -} - -void Xkb::updateKeymap(xkb_keymap *keymap) -{ - Q_ASSERT(keymap); - xkb_state *state = xkb_state_new(keymap); - if (!state) { - qCDebug(KWIN_CORE) << "Could not create XKB state"; - xkb_keymap_unref(keymap); - return; - } - // now release the old ones - xkb_state_unref(m_state); - xkb_keymap_unref(m_keymap); - - m_keymap = keymap; - m_state = state; - - m_shiftModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_SHIFT); - m_controlModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_CTRL); - m_altModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_ALT); - m_metaModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_LOGO); - - createKeymapFile(); -} - -void Xkb::createKeymapFile() -{ - if (!waylandServer()) { - return; - } - // TODO: uninstall keymap on server? - if (!m_keymap) { - return; - } - - ScopedCPointer keymapString(xkb_keymap_get_as_string(m_keymap, XKB_KEYMAP_FORMAT_TEXT_V1)); - if (keymapString.isNull()) { - return; - } - const uint size = qstrlen(keymapString.data()) + 1; - - QTemporaryFile *tmp = new QTemporaryFile(m_input); - if (!tmp->open()) { - delete tmp; - return; - } - unlink(tmp->fileName().toUtf8().constData()); - if (!tmp->resize(size)) { - delete tmp; - return; - } - uchar *address = tmp->map(0, size); - if (!address) { - return; - } - if (qstrncpy(reinterpret_cast(address), keymapString.data(), size) == nullptr) { - delete tmp; - return; - } - waylandServer()->seat()->setKeymap(tmp->handle(), size); -} - -void Xkb::updateModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group) -{ - if (!m_keymap || !m_state) { - return; - } - xkb_state_update_mask(m_state, modsDepressed, modsLatched, modsLocked, 0, 0, group); - updateModifiers(); -} - -void Xkb::updateKey(uint32_t key, InputRedirection::KeyboardKeyState state) -{ - if (!m_keymap || !m_state) { - return; - } - xkb_state_update_key(m_state, key + 8, static_cast(state)); - updateModifiers(); - if (state == InputRedirection::KeyboardKeyPressed) { - m_modOnlyShortcut.pressCount++; - if (m_modOnlyShortcut.pressCount == 1) { - m_modOnlyShortcut.modifier = Qt::KeyboardModifier(int(m_modifiers)); - } else { - m_modOnlyShortcut.modifier = Qt::NoModifier; - } - } else { - m_modOnlyShortcut.pressCount--; - // TODO: ignore on lock screen - if (m_modOnlyShortcut.pressCount == 0) { - if (m_modOnlyShortcut.modifier != Qt::NoModifier) { - const auto list = options->modifierOnlyDBusShortcut(m_modOnlyShortcut.modifier); - if (list.size() >= 4) { - auto call = QDBusMessage::createMethodCall(list.at(0), list.at(1), list.at(2), list.at(3)); - QVariantList args; - for (int i = 4; i < list.size(); ++i) { - args << list.at(i); - } - call.setArguments(args); - QDBusConnection::sessionBus().asyncCall(call); - } - } - } - m_modOnlyShortcut.modifier = Qt::NoModifier; - } -} - -void Xkb::updateModifiers() -{ - Qt::KeyboardModifiers mods = Qt::NoModifier; - if (xkb_state_mod_index_is_active(m_state, m_shiftModifier, XKB_STATE_MODS_EFFECTIVE) == 1) { - mods |= Qt::ShiftModifier; - } - if (xkb_state_mod_index_is_active(m_state, m_altModifier, XKB_STATE_MODS_EFFECTIVE) == 1) { - mods |= Qt::AltModifier; - } - if (xkb_state_mod_index_is_active(m_state, m_controlModifier, XKB_STATE_MODS_EFFECTIVE) == 1) { - mods |= Qt::ControlModifier; - } - if (xkb_state_mod_index_is_active(m_state, m_metaModifier, XKB_STATE_MODS_EFFECTIVE) == 1) { - mods |= Qt::MetaModifier; - } - m_modifiers = mods; -} - -xkb_keysym_t Xkb::toKeysym(uint32_t key) -{ - if (!m_state) { - return XKB_KEY_NoSymbol; - } - return xkb_state_key_get_one_sym(m_state, key + 8); -} - -QString Xkb::toString(xkb_keysym_t keysym) -{ - if (!m_state || keysym == XKB_KEY_NoSymbol) { - return QString(); - } - QByteArray byteArray(7, 0); - int ok = xkb_keysym_to_utf8(keysym, byteArray.data(), byteArray.size()); - if (ok == -1 || ok == 0) { - return QString(); - } - return QString::fromUtf8(byteArray.constData()); -} - -Qt::Key Xkb::toQtKey(xkb_keysym_t keysym) -{ - int key = Qt::Key_unknown; - KKeyServer::symXToKeyQt(keysym, &key); - return static_cast(key); -} - -quint32 Xkb::getMods(quint32 components) -{ - if (!m_state) { - return 0; - } - return xkb_state_serialize_mods(m_state, xkb_state_component(components)); -} - -quint32 Xkb::getGroup() -{ - if (!m_state) { - return 0; - } - return xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE); -} - InputEventFilter::InputEventFilter() = default; InputEventFilter::~InputEventFilter() @@ -388,7 +173,7 @@ public: if (!waylandServer()->isScreenLocked()) { return false; } - input()->updateKeyboardWindow(); + input()->keyboard()->update(); if (!keyboardSurfaceAllowed()) { // don't pass event to seat return true; @@ -754,7 +539,7 @@ public: return false; } auto seat = waylandServer()->seat(); - input()->updateKeyboardWindow(); + input()->keyboard()->update(); seat->setTimestamp(event->timestamp()); switch (event->type()) { case QEvent::KeyPress: @@ -822,9 +607,9 @@ KWIN_SINGLETON_FACTORY(InputRedirection) InputRedirection::InputRedirection(QObject *parent) : QObject(parent) + , m_keyboard(new KeyboardInputRedirection(this)) , m_pointer(new PointerInputRedirection(this)) , m_touch(new TouchInputRedirection(this)) - , m_xkb(new Xkb(this)) , m_shortcuts(new GlobalShortcutsManager(this)) { qRegisterMetaType(); @@ -867,7 +652,6 @@ void InputRedirection::init() void InputRedirection::setupWorkspace() { if (waylandServer()) { - connect(workspace(), &Workspace::clientActivated, this, &InputRedirection::updateKeyboardWindow); using namespace KWayland::Server; FakeInputInterface *fakeInput = waylandServer()->display()->createFakeInput(this); fakeInput->create(); @@ -923,18 +707,17 @@ void InputRedirection::setupWorkspace() if (!waylandServer()->seat()) { return; } - waylandServer()->seat()->updateKeyboardModifiers(m_xkb->getMods(XKB_STATE_MODS_DEPRESSED), - m_xkb->getMods(XKB_STATE_MODS_LATCHED), - m_xkb->getMods(XKB_STATE_MODS_LOCKED), - m_xkb->getGroup()); + waylandServer()->seat()->updateKeyboardModifiers(m_keyboard->xkb()->getMods(XKB_STATE_MODS_DEPRESSED), + m_keyboard->xkb()->getMods(XKB_STATE_MODS_LATCHED), + m_keyboard->xkb()->getMods(XKB_STATE_MODS_LOCKED), + m_keyboard->xkb()->getGroup()); } ); connect(workspace(), &Workspace::configChanged, this, &InputRedirection::reconfigure); + m_keyboard->init(); m_pointer->init(); m_touch->init(); - - connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this, &InputRedirection::updateKeyboardWindow); } setupInputFilters(); } @@ -1006,7 +789,7 @@ void InputRedirection::setupLibInput() ); connect(conn, &LibInput::Connection::pointerButtonChanged, m_pointer, &PointerInputRedirection::processButton); connect(conn, &LibInput::Connection::pointerAxisChanged, m_pointer, &PointerInputRedirection::processAxis); - connect(conn, &LibInput::Connection::keyChanged, this, &InputRedirection::processKeyboardKey); + connect(conn, &LibInput::Connection::keyChanged, m_keyboard, &KeyboardInputRedirection::processKey); connect(conn, &LibInput::Connection::pointerMotion, this, [this] (QPointF delta, uint32_t time) { m_pointer->processMotion(m_pointer->pos() + delta, time); @@ -1098,87 +881,19 @@ void InputRedirection::processPointerAxis(InputRedirection::PointerAxis axis, qr m_pointer->processAxis(axis, delta, time); } -void InputRedirection::updateKeyboardWindow() -{ - if (!workspace()) { - return; - } - if (auto seat = findSeat()) { - // TODO: this needs better integration - Toplevel *found = nullptr; - if (waylandServer()->isScreenLocked()) { - const ToplevelList &stacking = Workspace::self()->stackingOrder(); - if (!stacking.isEmpty()) { - auto it = stacking.end(); - do { - --it; - Toplevel *t = (*it); - if (t->isDeleted()) { - // a deleted window doesn't get mouse events - continue; - } - if (!t->isLockScreen()) { - continue; - } - if (!t->readyForPainting()) { - continue; - } - found = t; - break; - } while (it != stacking.begin()); - } - } else { - found = workspace()->activeClient(); - } - if (found && found->surface()) { - if (found->surface() != seat->focusedKeyboardSurface()) { - seat->setFocusedKeyboardSurface(found->surface()); - } - } else { - seat->setFocusedKeyboardSurface(nullptr); - } - } -} - void InputRedirection::processKeyboardKey(uint32_t key, InputRedirection::KeyboardKeyState state, uint32_t time) { - emit keyStateChanged(key, state); - const Qt::KeyboardModifiers oldMods = keyboardModifiers(); - m_xkb->updateKey(key, state); - if (oldMods != keyboardModifiers()) { - emit keyboardModifiersChanged(keyboardModifiers(), oldMods); - } - const xkb_keysym_t keySym = m_xkb->toKeysym(key); - QKeyEvent event((state == KeyboardKeyPressed) ? QEvent::KeyPress : QEvent::KeyRelease, - m_xkb->toQtKey(keySym), - m_xkb->modifiers(), - key, - keySym, - 0, - m_xkb->toString(m_xkb->toKeysym(key))); - event.setTimestamp(time); - - for (auto it = m_filters.constBegin(), end = m_filters.constEnd(); it != end; it++) { - if ((*it)->keyEvent(&event)) { - return; - } - } + m_keyboard->processKey(key, state, time); } void InputRedirection::processKeyboardModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group) { - // TODO: send to proper Client and also send when active Client changes - Qt::KeyboardModifiers oldMods = keyboardModifiers(); - m_xkb->updateModifiers(modsDepressed, modsLatched, modsLocked, group); - if (oldMods != keyboardModifiers()) { - emit keyboardModifiersChanged(keyboardModifiers(), oldMods); - } + m_keyboard->processModifiers(modsDepressed, modsLatched, modsLocked, group); } void InputRedirection::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_keyboard->processKeymapChange(fd, size); } void InputRedirection::processTouchDown(qint32 id, const QPointF &pos, quint32 time) @@ -1269,7 +984,7 @@ Toplevel *InputRedirection::findToplevel(const QPoint &pos) Qt::KeyboardModifiers InputRedirection::keyboardModifiers() const { - return m_xkb->modifiers(); + return m_keyboard->modifiers(); } void InputRedirection::registerShortcut(const QKeySequence &shortcut, QAction *action) diff --git a/input.h b/input.h index 2800aa1bb7..21b500b9c4 100644 --- a/input.h +++ b/input.h @@ -32,18 +32,12 @@ along with this program. If not, see . class KGlobalAccelInterface; class QKeySequence; -struct xkb_context; -struct xkb_keymap; -struct xkb_state; -typedef uint32_t xkb_mod_index_t; -typedef uint32_t xkb_keysym_t; - namespace KWin { class GlobalShortcutsManager; class Toplevel; -class Xkb; class InputEventFilter; +class KeyboardInputRedirection; class PointerInputRedirection; class TouchInputRedirection; @@ -146,11 +140,12 @@ public: return m_shortcuts; } - void updateKeyboardWindow(); - QVector filters() const { return m_filters; } + KeyboardInputRedirection *keyboard() const { + return m_keyboard; + } PointerInputRedirection *pointer() const { return m_pointer; } @@ -205,9 +200,9 @@ private: void reconfigure(); void setupInputFilters(); void installInputEventFilter(InputEventFilter *filter); + KeyboardInputRedirection *m_keyboard; PointerInputRedirection *m_pointer; TouchInputRedirection *m_touch; - QScopedPointer m_xkb; GlobalShortcutsManager *m_shortcuts; @@ -279,40 +274,6 @@ public: virtual bool touchUp(quint32 id, quint32 time); }; -class Xkb -{ -public: - Xkb(InputRedirection *input); - ~Xkb(); - void installKeymap(int fd, uint32_t size); - void updateModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group); - void updateKey(uint32_t key, InputRedirection::KeyboardKeyState state); - xkb_keysym_t toKeysym(uint32_t key); - QString toString(xkb_keysym_t keysym); - Qt::Key toQtKey(xkb_keysym_t keysym); - Qt::KeyboardModifiers modifiers() const; - - quint32 getMods(quint32 components); - quint32 getGroup(); -private: - void updateKeymap(xkb_keymap *keymap); - void createKeymapFile(); - void updateModifiers(); - InputRedirection *m_input; - xkb_context *m_context; - xkb_keymap *m_keymap; - xkb_state *m_state; - xkb_mod_index_t m_shiftModifier; - xkb_mod_index_t m_controlModifier; - xkb_mod_index_t m_altModifier; - xkb_mod_index_t m_metaModifier; - Qt::KeyboardModifiers m_modifiers; - struct { - uint pressCount = 0; - Qt::KeyboardModifier modifier = Qt::NoModifier; - } m_modOnlyShortcut; -}; - inline InputRedirection *input() { @@ -326,12 +287,6 @@ void InputRedirection::registerShortcut(const QKeySequence &shortcut, QAction *a connect(action, &QAction::triggered, receiver, slot); } -inline -Qt::KeyboardModifiers Xkb::modifiers() const -{ - return m_modifiers; -} - } // namespace KWin Q_DECLARE_METATYPE(KWin::InputRedirection::KeyboardKeyState) diff --git a/keyboard_input.cpp b/keyboard_input.cpp new file mode 100644 index 0000000000..489f0a9e7d --- /dev/null +++ b/keyboard_input.cpp @@ -0,0 +1,376 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2013, 2016 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_input.h" +#include "abstract_client.h" +#include "options.h" +#include "utils.h" +#include "toplevel.h" +#include "wayland_server.h" +#include "workspace.h" +// KWayland +#include +//screenlocker +#include +// Frameworks +#include +// Qt +#include +#include +#include +#include +#include +// xkbcommon +#include +#include +// system +#include +#include + +namespace KWin +{ +Xkb::Xkb(InputRedirection *input) + : m_input(input) + , m_context(xkb_context_new(static_cast(0))) + , m_keymap(NULL) + , m_state(NULL) + , m_shiftModifier(0) + , m_controlModifier(0) + , m_altModifier(0) + , m_metaModifier(0) + , m_modifiers(Qt::NoModifier) +{ + if (!m_context) { + qCDebug(KWIN_CORE) << "Could not create xkb context"; + } else { + // load default keymap + xkb_keymap *keymap = xkb_keymap_new_from_names(m_context, nullptr, XKB_KEYMAP_COMPILE_NO_FLAGS); + if (keymap) { + updateKeymap(keymap); + } else { + qCDebug(KWIN_CORE) << "Could not create default xkb keymap"; + } + } +} + +Xkb::~Xkb() +{ + xkb_state_unref(m_state); + xkb_keymap_unref(m_keymap); + xkb_context_unref(m_context); +} + +void Xkb::installKeymap(int fd, uint32_t size) +{ + if (!m_context) { + return; + } + char *map = reinterpret_cast(mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0)); + if (map == MAP_FAILED) { + return; + } + xkb_keymap *keymap = xkb_keymap_new_from_string(m_context, map, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_MAP_COMPILE_PLACEHOLDER); + munmap(map, size); + if (!keymap) { + qCDebug(KWIN_CORE) << "Could not map keymap from file"; + return; + } + updateKeymap(keymap); +} + +void Xkb::updateKeymap(xkb_keymap *keymap) +{ + Q_ASSERT(keymap); + xkb_state *state = xkb_state_new(keymap); + if (!state) { + qCDebug(KWIN_CORE) << "Could not create XKB state"; + xkb_keymap_unref(keymap); + return; + } + // now release the old ones + xkb_state_unref(m_state); + xkb_keymap_unref(m_keymap); + + m_keymap = keymap; + m_state = state; + + m_shiftModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_SHIFT); + m_controlModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_CTRL); + m_altModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_ALT); + m_metaModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_LOGO); + + createKeymapFile(); +} + +void Xkb::createKeymapFile() +{ + if (!waylandServer()) { + return; + } + // TODO: uninstall keymap on server? + if (!m_keymap) { + return; + } + + ScopedCPointer keymapString(xkb_keymap_get_as_string(m_keymap, XKB_KEYMAP_FORMAT_TEXT_V1)); + if (keymapString.isNull()) { + return; + } + const uint size = qstrlen(keymapString.data()) + 1; + + QTemporaryFile *tmp = new QTemporaryFile(m_input); + if (!tmp->open()) { + delete tmp; + return; + } + unlink(tmp->fileName().toUtf8().constData()); + if (!tmp->resize(size)) { + delete tmp; + return; + } + uchar *address = tmp->map(0, size); + if (!address) { + return; + } + if (qstrncpy(reinterpret_cast(address), keymapString.data(), size) == nullptr) { + delete tmp; + return; + } + waylandServer()->seat()->setKeymap(tmp->handle(), size); +} + +void Xkb::updateModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group) +{ + if (!m_keymap || !m_state) { + return; + } + xkb_state_update_mask(m_state, modsDepressed, modsLatched, modsLocked, 0, 0, group); + updateModifiers(); +} + +void Xkb::updateKey(uint32_t key, InputRedirection::KeyboardKeyState state) +{ + if (!m_keymap || !m_state) { + return; + } + xkb_state_update_key(m_state, key + 8, static_cast(state)); + updateModifiers(); + if (state == InputRedirection::KeyboardKeyPressed) { + m_modOnlyShortcut.pressCount++; + if (m_modOnlyShortcut.pressCount == 1) { + m_modOnlyShortcut.modifier = Qt::KeyboardModifier(int(m_modifiers)); + } else { + m_modOnlyShortcut.modifier = Qt::NoModifier; + } + } else { + m_modOnlyShortcut.pressCount--; + // TODO: ignore on lock screen + if (m_modOnlyShortcut.pressCount == 0) { + if (m_modOnlyShortcut.modifier != Qt::NoModifier) { + const auto list = options->modifierOnlyDBusShortcut(m_modOnlyShortcut.modifier); + if (list.size() >= 4) { + auto call = QDBusMessage::createMethodCall(list.at(0), list.at(1), list.at(2), list.at(3)); + QVariantList args; + for (int i = 4; i < list.size(); ++i) { + args << list.at(i); + } + call.setArguments(args); + QDBusConnection::sessionBus().asyncCall(call); + } + } + } + m_modOnlyShortcut.modifier = Qt::NoModifier; + } +} + +void Xkb::updateModifiers() +{ + Qt::KeyboardModifiers mods = Qt::NoModifier; + if (xkb_state_mod_index_is_active(m_state, m_shiftModifier, XKB_STATE_MODS_EFFECTIVE) == 1) { + mods |= Qt::ShiftModifier; + } + if (xkb_state_mod_index_is_active(m_state, m_altModifier, XKB_STATE_MODS_EFFECTIVE) == 1) { + mods |= Qt::AltModifier; + } + if (xkb_state_mod_index_is_active(m_state, m_controlModifier, XKB_STATE_MODS_EFFECTIVE) == 1) { + mods |= Qt::ControlModifier; + } + if (xkb_state_mod_index_is_active(m_state, m_metaModifier, XKB_STATE_MODS_EFFECTIVE) == 1) { + mods |= Qt::MetaModifier; + } + m_modifiers = mods; +} + +xkb_keysym_t Xkb::toKeysym(uint32_t key) +{ + if (!m_state) { + return XKB_KEY_NoSymbol; + } + return xkb_state_key_get_one_sym(m_state, key + 8); +} + +QString Xkb::toString(xkb_keysym_t keysym) +{ + if (!m_state || keysym == XKB_KEY_NoSymbol) { + return QString(); + } + QByteArray byteArray(7, 0); + int ok = xkb_keysym_to_utf8(keysym, byteArray.data(), byteArray.size()); + if (ok == -1 || ok == 0) { + return QString(); + } + return QString::fromUtf8(byteArray.constData()); +} + +Qt::Key Xkb::toQtKey(xkb_keysym_t keysym) +{ + int key = Qt::Key_unknown; + KKeyServer::symXToKeyQt(keysym, &key); + return static_cast(key); +} + +quint32 Xkb::getMods(quint32 components) +{ + if (!m_state) { + return 0; + } + return xkb_state_serialize_mods(m_state, xkb_state_component(components)); +} + +quint32 Xkb::getGroup() +{ + if (!m_state) { + return 0; + } + return xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE); +} + +KeyboardInputRedirection::KeyboardInputRedirection(InputRedirection *parent) + : QObject(parent) + , m_input(parent) + , m_xkb(new Xkb(parent)) +{ +} + +KeyboardInputRedirection::~KeyboardInputRedirection() = default; + +void KeyboardInputRedirection::init() +{ + Q_ASSERT(!m_inited); + m_inited = true; + + connect(workspace(), &QObject::destroyed, this, [this] { m_inited = false; }); + connect(waylandServer(), &QObject::destroyed, this, [this] { m_inited = false; }); + connect(workspace(), &Workspace::clientActivated, this, &KeyboardInputRedirection::update); + connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this, &KeyboardInputRedirection::update); +} + +void KeyboardInputRedirection::update() +{ + if (!m_inited) { + return; + } + auto seat = waylandServer()->seat(); + // TODO: this needs better integration + Toplevel *found = nullptr; + if (waylandServer()->isScreenLocked()) { + const ToplevelList &stacking = Workspace::self()->stackingOrder(); + if (!stacking.isEmpty()) { + auto it = stacking.end(); + do { + --it; + Toplevel *t = (*it); + if (t->isDeleted()) { + // a deleted window doesn't get mouse events + continue; + } + if (!t->isLockScreen()) { + continue; + } + if (!t->readyForPainting()) { + continue; + } + found = t; + break; + } while (it != stacking.begin()); + } + } else { + found = workspace()->activeClient(); + } + if (found && found->surface()) { + if (found->surface() != seat->focusedKeyboardSurface()) { + seat->setFocusedKeyboardSurface(found->surface()); + } + } else { + seat->setFocusedKeyboardSurface(nullptr); + } +} + +void KeyboardInputRedirection::processKey(uint32_t key, InputRedirection::KeyboardKeyState state, uint32_t time) +{ + if (!m_inited) { + return; + } + emit m_input->keyStateChanged(key, state); + const Qt::KeyboardModifiers oldMods = modifiers(); + m_xkb->updateKey(key, state); + if (oldMods != modifiers()) { + emit m_input->keyboardModifiersChanged(modifiers(), oldMods); + } + const xkb_keysym_t keySym = m_xkb->toKeysym(key); + QKeyEvent event((state == InputRedirection::KeyboardKeyPressed) ? QEvent::KeyPress : QEvent::KeyRelease, + m_xkb->toQtKey(keySym), + m_xkb->modifiers(), + key, + keySym, + 0, + m_xkb->toString(m_xkb->toKeysym(key))); + event.setTimestamp(time); + + const auto &filters = m_input->filters(); + for (auto it = filters.begin(), end = filters.end(); it != end; it++) { + if ((*it)->keyEvent(&event)) { + return; + } + } +} + +void KeyboardInputRedirection::processModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group) +{ + if (!m_inited) { + return; + } + // TODO: send to proper Client and also send when active Client changes + Qt::KeyboardModifiers oldMods = modifiers(); + m_xkb->updateModifiers(modsDepressed, modsLatched, modsLocked, group); + if (oldMods != modifiers()) { + emit m_input->keyboardModifiersChanged(modifiers(), oldMods); + } +} + +void KeyboardInputRedirection::processKeymapChange(int fd, uint32_t size) +{ + if (!m_inited) { + return; + } + // TODO: should we pass the keymap to our Clients? Or only to the currently active one and update + m_xkb->installKeymap(fd, size); +} + +} diff --git a/keyboard_input.h b/keyboard_input.h new file mode 100644 index 0000000000..7cada835cc --- /dev/null +++ b/keyboard_input.h @@ -0,0 +1,121 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2013, 2016 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_INPUT_H +#define KWIN_KEYBOARD_INPUT_H + +#include "input.h" + +#include +#include +#include + +class QWindow; +struct xkb_context; +struct xkb_keymap; +struct xkb_state; +typedef uint32_t xkb_mod_index_t; +typedef uint32_t xkb_keysym_t; + +namespace KWin +{ + +class InputRedirection; +class Toplevel; + +class Xkb +{ +public: + Xkb(InputRedirection *input); + ~Xkb(); + void installKeymap(int fd, uint32_t size); + void updateModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group); + void updateKey(uint32_t key, InputRedirection::KeyboardKeyState state); + xkb_keysym_t toKeysym(uint32_t key); + QString toString(xkb_keysym_t keysym); + Qt::Key toQtKey(xkb_keysym_t keysym); + Qt::KeyboardModifiers modifiers() const; + + quint32 getMods(quint32 components); + quint32 getGroup(); +private: + void updateKeymap(xkb_keymap *keymap); + void createKeymapFile(); + void updateModifiers(); + InputRedirection *m_input; + xkb_context *m_context; + xkb_keymap *m_keymap; + xkb_state *m_state; + xkb_mod_index_t m_shiftModifier; + xkb_mod_index_t m_controlModifier; + xkb_mod_index_t m_altModifier; + xkb_mod_index_t m_metaModifier; + Qt::KeyboardModifiers m_modifiers; + struct { + uint pressCount = 0; + Qt::KeyboardModifier modifier = Qt::NoModifier; + } m_modOnlyShortcut; +}; + +class KeyboardInputRedirection : public QObject +{ + Q_OBJECT +public: + explicit KeyboardInputRedirection(InputRedirection *parent); + virtual ~KeyboardInputRedirection(); + + void init(); + + void update(); + + /** + * @internal + */ + void processKey(uint32_t key, InputRedirection::KeyboardKeyState state, uint32_t time); + /** + * @internal + */ + void processModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group); + /** + * @internal + **/ + void processKeymapChange(int fd, uint32_t size); + + Xkb *xkb() const { + return m_xkb.data(); + } + Qt::KeyboardModifiers modifiers() const { + return m_xkb->modifiers(); + } + +private: + InputRedirection *m_input; + bool m_inited = false; + QScopedPointer m_xkb; +}; + +inline +Qt::KeyboardModifiers Xkb::modifiers() const +{ + return m_modifiers; +} + +} + +#endif