2020-08-02 22:22:19 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
2016-02-15 12:42:48 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 2013, 2016 Martin Gräßlin <mgraesslin@kde.org>
|
2016-02-15 12:42:48 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2016-02-15 12:42:48 +00:00
|
|
|
#include "keyboard_input.h"
|
2022-03-23 10:13:38 +00:00
|
|
|
|
|
|
|
#include <config-kwin.h>
|
|
|
|
|
2016-05-24 08:57:57 +00:00
|
|
|
#include "input_event.h"
|
2016-12-27 19:16:50 +00:00
|
|
|
#include "input_event_spy.h"
|
2021-12-29 17:19:43 +00:00
|
|
|
#include "inputmethod.h"
|
2017-01-14 17:06:41 +00:00
|
|
|
#include "keyboard_layout.h"
|
2017-01-27 05:50:50 +00:00
|
|
|
#include "keyboard_repeat.h"
|
2017-02-03 16:26:51 +00:00
|
|
|
#include "modifier_only_shortcuts.h"
|
2022-01-18 08:35:52 +00:00
|
|
|
#include "utils/common.h"
|
2022-04-22 09:27:33 +00:00
|
|
|
#include "wayland/datadevice_interface.h"
|
|
|
|
#include "wayland/keyboard_interface.h"
|
|
|
|
#include "wayland/seat_interface.h"
|
2016-02-15 12:42:48 +00:00
|
|
|
#include "wayland_server.h"
|
2022-04-22 17:39:12 +00:00
|
|
|
#include "window.h"
|
2016-02-15 12:42:48 +00:00
|
|
|
#include "workspace.h"
|
2022-03-23 10:13:38 +00:00
|
|
|
// screenlocker
|
2022-02-28 18:58:35 +00:00
|
|
|
#if KWIN_BUILD_SCREENLOCKER
|
2016-02-15 12:42:48 +00:00
|
|
|
#include <KScreenLocker/KsldApp>
|
2022-02-23 13:27:05 +00:00
|
|
|
#endif
|
2016-02-15 12:42:48 +00:00
|
|
|
// Frameworks
|
2016-02-19 12:50:39 +00:00
|
|
|
#include <KGlobalAccel>
|
2016-02-15 12:42:48 +00:00
|
|
|
// Qt
|
|
|
|
#include <QKeyEvent>
|
2016-02-19 08:08:32 +00:00
|
|
|
|
2021-10-29 22:41:16 +00:00
|
|
|
#include <cmath>
|
|
|
|
|
2016-02-15 12:42:48 +00:00
|
|
|
namespace KWin
|
|
|
|
{
|
2016-02-19 08:08:56 +00:00
|
|
|
|
2016-02-15 12:42:48 +00:00
|
|
|
KeyboardInputRedirection::KeyboardInputRedirection(InputRedirection *parent)
|
|
|
|
: QObject(parent)
|
|
|
|
, m_input(parent)
|
|
|
|
, m_xkb(new Xkb(parent))
|
|
|
|
{
|
2022-08-01 21:29:02 +00:00
|
|
|
connect(m_xkb.get(), &Xkb::ledsChanged, this, &KeyboardInputRedirection::ledsChanged);
|
2017-08-13 18:53:36 +00:00
|
|
|
if (waylandServer()) {
|
|
|
|
m_xkb->setSeat(waylandServer()->seat());
|
|
|
|
}
|
2016-02-15 12:42:48 +00:00
|
|
|
}
|
|
|
|
|
2016-10-05 06:26:29 +00:00
|
|
|
KeyboardInputRedirection::~KeyboardInputRedirection() = default;
|
2016-02-15 12:42:48 +00:00
|
|
|
|
2017-01-13 19:41:21 +00:00
|
|
|
class KeyStateChangedSpy : public InputEventSpy
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
KeyStateChangedSpy(InputRedirection *input)
|
|
|
|
: m_input(input)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void keyEvent(KeyEvent *event) override
|
|
|
|
{
|
|
|
|
if (event->isAutoRepeat()) {
|
|
|
|
return;
|
|
|
|
}
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT m_input->keyStateChanged(event->nativeScanCode(), event->type() == QEvent::KeyPress ? InputRedirection::KeyboardKeyPressed : InputRedirection::KeyboardKeyReleased);
|
2017-01-13 19:41:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
InputRedirection *m_input;
|
|
|
|
};
|
|
|
|
|
|
|
|
class ModifiersChangedSpy : public InputEventSpy
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ModifiersChangedSpy(InputRedirection *input)
|
|
|
|
: m_input(input)
|
|
|
|
, m_modifiers()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void keyEvent(KeyEvent *event) override
|
|
|
|
{
|
|
|
|
if (event->isAutoRepeat()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
updateModifiers(event->modifiers());
|
|
|
|
}
|
|
|
|
|
|
|
|
void updateModifiers(Qt::KeyboardModifiers mods)
|
|
|
|
{
|
|
|
|
if (mods == m_modifiers) {
|
|
|
|
return;
|
|
|
|
}
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT m_input->keyboardModifiersChanged(mods, m_modifiers);
|
2017-01-13 19:41:21 +00:00
|
|
|
m_modifiers = mods;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
InputRedirection *m_input;
|
|
|
|
Qt::KeyboardModifiers m_modifiers;
|
|
|
|
};
|
|
|
|
|
2016-02-15 12:42:48 +00:00
|
|
|
void KeyboardInputRedirection::init()
|
|
|
|
{
|
|
|
|
Q_ASSERT(!m_inited);
|
|
|
|
m_inited = true;
|
2017-01-27 16:15:32 +00:00
|
|
|
const auto config = kwinApp()->kxkbConfig();
|
2020-07-13 01:56:45 +00:00
|
|
|
m_xkb->setNumLockConfig(InputConfig::self()->inputConfig());
|
2017-01-27 16:15:32 +00:00
|
|
|
m_xkb->setConfig(config);
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
// Workaround for QTBUG-54371: if there is no real keyboard Qt doesn't request virtual keyboard
|
|
|
|
waylandServer()->seat()->setHasKeyboard(true);
|
|
|
|
// connect(m_input, &InputRedirection::hasAlphaNumericKeyboardChanged,
|
|
|
|
// waylandServer()->seat(), &KWaylandServer::SeatInterface::setHasKeyboard);
|
|
|
|
|
2017-01-13 19:41:21 +00:00
|
|
|
m_input->installInputEventSpy(new KeyStateChangedSpy(m_input));
|
|
|
|
m_modifiersChangedSpy = new ModifiersChangedSpy(m_input);
|
|
|
|
m_input->installInputEventSpy(m_modifiersChangedSpy);
|
2022-08-01 21:29:02 +00:00
|
|
|
m_keyboardLayout = new KeyboardLayout(m_xkb.get(), config);
|
2017-01-14 17:06:41 +00:00
|
|
|
m_keyboardLayout->init();
|
|
|
|
m_input->installInputEventSpy(m_keyboardLayout);
|
2016-02-15 12:42:48 +00:00
|
|
|
|
2018-12-02 12:09:37 +00:00
|
|
|
if (waylandServer()->hasGlobalShortcutSupport()) {
|
|
|
|
m_input->installInputEventSpy(new ModifierOnlyShortcuts);
|
|
|
|
}
|
2017-02-03 16:26:51 +00:00
|
|
|
|
2022-08-01 21:29:02 +00:00
|
|
|
KeyboardRepeat *keyRepeatSpy = new KeyboardRepeat(m_xkb.get());
|
2017-01-27 05:50:50 +00:00
|
|
|
connect(keyRepeatSpy, &KeyboardRepeat::keyRepeat, this,
|
2022-03-23 10:13:38 +00:00
|
|
|
std::bind(&KeyboardInputRedirection::processKey, this, std::placeholders::_1, InputRedirection::KeyboardKeyAutoRepeat, std::placeholders::_2, nullptr));
|
2017-01-27 05:50:50 +00:00
|
|
|
m_input->installInputEventSpy(keyRepeatSpy);
|
2016-10-05 06:26:29 +00:00
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
connect(workspace(), &QObject::destroyed, this, [this] {
|
|
|
|
m_inited = false;
|
|
|
|
});
|
|
|
|
connect(waylandServer(), &QObject::destroyed, this, [this] {
|
|
|
|
m_inited = false;
|
|
|
|
});
|
2022-04-23 08:33:23 +00:00
|
|
|
connect(workspace(), &Workspace::windowActivated, this, [this] {
|
|
|
|
disconnect(m_activeWindowSurfaceChangedConnection);
|
|
|
|
if (auto window = workspace()->activeWindow()) {
|
|
|
|
m_activeWindowSurfaceChangedConnection = connect(window, &Window::surfaceChanged, this, &KeyboardInputRedirection::update);
|
2022-03-23 10:13:38 +00:00
|
|
|
} else {
|
2022-04-23 08:33:23 +00:00
|
|
|
m_activeWindowSurfaceChangedConnection = QMetaObject::Connection();
|
2016-06-26 13:57:38 +00:00
|
|
|
}
|
2022-03-23 10:13:38 +00:00
|
|
|
update();
|
|
|
|
});
|
2022-02-28 18:58:35 +00:00
|
|
|
#if KWIN_BUILD_SCREENLOCKER
|
2016-04-25 06:51:33 +00:00
|
|
|
if (waylandServer()->hasScreenLockerIntegration()) {
|
|
|
|
connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this, &KeyboardInputRedirection::update);
|
|
|
|
}
|
2022-02-23 13:27:05 +00:00
|
|
|
#endif
|
2021-10-02 08:26:51 +00:00
|
|
|
|
|
|
|
reconfigure();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KeyboardInputRedirection::reconfigure()
|
|
|
|
{
|
|
|
|
if (waylandServer()->seat()->keyboard()) {
|
|
|
|
const auto config = InputConfig::self()->inputConfig()->group(QStringLiteral("Keyboard"));
|
|
|
|
const int delay = config.readEntry("RepeatDelay", 660);
|
2021-10-29 22:41:16 +00:00
|
|
|
const int rate = std::ceil(config.readEntry("RepeatRate", 25.0));
|
2021-10-02 08:26:51 +00:00
|
|
|
const QString repeatMode = config.readEntry("KeyRepeat", "repeat");
|
|
|
|
// when the clients will repeat the character or turn repeat key events into an accent character selection, we want
|
|
|
|
// to tell the clients that we are indeed repeating keys.
|
|
|
|
const bool enabled = repeatMode == QLatin1String("accent") || repeatMode == QLatin1String("repeat");
|
|
|
|
|
|
|
|
waylandServer()->seat()->keyboard()->setRepeatInfo(enabled ? rate : 0, delay);
|
|
|
|
}
|
2016-02-15 12:42:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void KeyboardInputRedirection::update()
|
|
|
|
{
|
|
|
|
if (!m_inited) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
// TODO: this needs better integration
|
2022-04-22 17:39:12 +00:00
|
|
|
Window *found = nullptr;
|
2016-02-15 12:42:48 +00:00
|
|
|
if (waylandServer()->isScreenLocked()) {
|
2022-04-22 17:39:12 +00:00
|
|
|
const QList<Window *> &stacking = Workspace::self()->stackingOrder();
|
2016-02-15 12:42:48 +00:00
|
|
|
if (!stacking.isEmpty()) {
|
|
|
|
auto it = stacking.end();
|
|
|
|
do {
|
|
|
|
--it;
|
2022-04-22 17:39:12 +00:00
|
|
|
Window *t = (*it);
|
2016-02-15 12:42:48 +00:00
|
|
|
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());
|
|
|
|
}
|
2017-04-18 05:10:46 +00:00
|
|
|
} else if (!input()->isSelectingWindow()) {
|
2022-04-23 08:33:23 +00:00
|
|
|
found = workspace()->activeWindow();
|
2016-02-15 12:42:48 +00:00
|
|
|
}
|
|
|
|
if (found && found->surface()) {
|
|
|
|
if (found->surface() != seat->focusedKeyboardSurface()) {
|
|
|
|
seat->setFocusedKeyboardSurface(found->surface());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
seat->setFocusedKeyboardSurface(nullptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void KeyboardInputRedirection::processKey(uint32_t key, InputRedirection::KeyboardKeyState state, uint32_t time, InputDevice *device)
|
2016-02-15 12:42:48 +00:00
|
|
|
{
|
2016-02-19 06:53:20 +00:00
|
|
|
QEvent::Type type;
|
|
|
|
bool autoRepeat = false;
|
|
|
|
switch (state) {
|
|
|
|
case InputRedirection::KeyboardKeyAutoRepeat:
|
|
|
|
autoRepeat = true;
|
|
|
|
// fall through
|
|
|
|
case InputRedirection::KeyboardKeyPressed:
|
|
|
|
type = QEvent::KeyPress;
|
|
|
|
break;
|
|
|
|
case InputRedirection::KeyboardKeyReleased:
|
|
|
|
type = QEvent::KeyRelease;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
}
|
|
|
|
|
2020-12-13 03:06:16 +00:00
|
|
|
const quint32 previousLayout = m_xkb->currentLayout();
|
2016-02-19 06:53:20 +00:00
|
|
|
if (!autoRepeat) {
|
|
|
|
m_xkb->updateKey(key, state);
|
2016-02-15 12:42:48 +00:00
|
|
|
}
|
2016-02-19 06:53:20 +00:00
|
|
|
|
2016-08-29 15:04:13 +00:00
|
|
|
const xkb_keysym_t keySym = m_xkb->currentKeysym();
|
2021-03-15 17:34:41 +00:00
|
|
|
const Qt::KeyboardModifiers globalShortcutsModifiers = m_xkb->modifiersRelevantForGlobalShortcuts(key);
|
2016-05-24 08:57:57 +00:00
|
|
|
KeyEvent event(type,
|
2021-03-15 17:34:41 +00:00
|
|
|
m_xkb->toQtKey(keySym, key, globalShortcutsModifiers ? Qt::ControlModifier : Qt::KeyboardModifiers()),
|
2016-05-24 08:57:57 +00:00
|
|
|
m_xkb->modifiers(),
|
|
|
|
key,
|
|
|
|
keySym,
|
2016-08-29 15:04:13 +00:00
|
|
|
m_xkb->toString(keySym),
|
2016-05-24 08:57:57 +00:00
|
|
|
autoRepeat,
|
|
|
|
time,
|
|
|
|
device);
|
2021-03-15 17:34:41 +00:00
|
|
|
event.setModifiersRelevantForGlobalShortcuts(globalShortcutsModifiers);
|
2016-02-15 12:42:48 +00:00
|
|
|
|
2016-12-27 19:16:50 +00:00
|
|
|
m_input->processSpies(std::bind(&InputEventSpy::keyEvent, std::placeholders::_1, &event));
|
2017-02-03 16:26:51 +00:00
|
|
|
if (!m_inited) {
|
|
|
|
return;
|
|
|
|
}
|
2022-02-11 18:12:48 +00:00
|
|
|
input()->setLastInputHandler(this);
|
2016-12-24 10:00:08 +00:00
|
|
|
m_input->processFilters(std::bind(&InputEventFilter::keyEvent, std::placeholders::_1, &event));
|
2017-04-14 19:01:20 +00:00
|
|
|
|
|
|
|
m_xkb->forwardModifiers();
|
2022-07-20 10:14:27 +00:00
|
|
|
if (auto *inputmethod = kwinApp()->inputMethod()) {
|
2022-01-03 04:55:05 +00:00
|
|
|
inputmethod->forwardModifiers(InputMethod::NoForce);
|
|
|
|
}
|
2020-12-13 03:06:16 +00:00
|
|
|
|
|
|
|
if (event.modifiersRelevantForGlobalShortcuts() == Qt::KeyboardModifier::NoModifier && type != QEvent::KeyRelease) {
|
|
|
|
m_keyboardLayout->checkLayoutChange(previousLayout);
|
|
|
|
}
|
2016-02-15 12:42:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void KeyboardInputRedirection::processModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
|
|
|
|
{
|
|
|
|
if (!m_inited) {
|
|
|
|
return;
|
|
|
|
}
|
2020-08-24 13:51:43 +00:00
|
|
|
const quint32 previousLayout = m_xkb->currentLayout();
|
2016-02-15 12:42:48 +00:00
|
|
|
// TODO: send to proper Client and also send when active Client changes
|
|
|
|
m_xkb->updateModifiers(modsDepressed, modsLatched, modsLocked, group);
|
2017-01-13 19:41:21 +00:00
|
|
|
m_modifiersChangedSpy->updateModifiers(modifiers());
|
2020-08-24 13:51:43 +00:00
|
|
|
m_keyboardLayout->checkLayoutChange(previousLayout);
|
2016-02-15 12:42:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2017-01-14 17:06:41 +00:00
|
|
|
m_keyboardLayout->resetLayout();
|
2016-02-15 12:42:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|