File indexing completed on 2024-11-10 04:57:48
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2013, 2016 Martin Gräßlin <mgraesslin@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 #include "keyboard_input.h" 0010 0011 #include <config-kwin.h> 0012 0013 #include "input_event.h" 0014 #include "input_event_spy.h" 0015 #include "inputmethod.h" 0016 #include "keyboard_layout.h" 0017 #include "keyboard_repeat.h" 0018 #include "modifier_only_shortcuts.h" 0019 #include "wayland/datadevice.h" 0020 #include "wayland/keyboard.h" 0021 #include "wayland/seat.h" 0022 #include "wayland_server.h" 0023 #include "window.h" 0024 #include "workspace.h" 0025 #include "xkb.h" 0026 // screenlocker 0027 #if KWIN_BUILD_SCREENLOCKER 0028 #include <KScreenLocker/KsldApp> 0029 #endif 0030 // Frameworks 0031 #include <KGlobalAccel> 0032 // Qt 0033 #include <QKeyEvent> 0034 0035 #include <cmath> 0036 0037 namespace KWin 0038 { 0039 0040 KeyboardInputRedirection::KeyboardInputRedirection(InputRedirection *parent) 0041 : QObject(parent) 0042 , m_input(parent) 0043 , m_xkb(new Xkb(kwinApp()->followLocale1())) 0044 { 0045 connect(m_xkb.get(), &Xkb::ledsChanged, this, &KeyboardInputRedirection::ledsChanged); 0046 if (waylandServer()) { 0047 m_xkb->setSeat(waylandServer()->seat()); 0048 } 0049 } 0050 0051 KeyboardInputRedirection::~KeyboardInputRedirection() = default; 0052 0053 Xkb *KeyboardInputRedirection::xkb() const 0054 { 0055 return m_xkb.get(); 0056 } 0057 0058 Qt::KeyboardModifiers KeyboardInputRedirection::modifiers() const 0059 { 0060 return m_xkb->modifiers(); 0061 } 0062 0063 Qt::KeyboardModifiers KeyboardInputRedirection::modifiersRelevantForGlobalShortcuts() const 0064 { 0065 return m_xkb->modifiersRelevantForGlobalShortcuts(); 0066 } 0067 0068 class KeyStateChangedSpy : public InputEventSpy 0069 { 0070 public: 0071 KeyStateChangedSpy(InputRedirection *input) 0072 : m_input(input) 0073 { 0074 } 0075 0076 void keyEvent(KeyEvent *event) override 0077 { 0078 if (event->isAutoRepeat()) { 0079 return; 0080 } 0081 Q_EMIT m_input->keyStateChanged(event->nativeScanCode(), event->type() == QEvent::KeyPress ? InputRedirection::KeyboardKeyPressed : InputRedirection::KeyboardKeyReleased); 0082 } 0083 0084 private: 0085 InputRedirection *m_input; 0086 }; 0087 0088 class ModifiersChangedSpy : public InputEventSpy 0089 { 0090 public: 0091 ModifiersChangedSpy(InputRedirection *input) 0092 : m_input(input) 0093 , m_modifiers() 0094 { 0095 } 0096 0097 void keyEvent(KeyEvent *event) override 0098 { 0099 if (event->isAutoRepeat()) { 0100 return; 0101 } 0102 const Qt::KeyboardModifiers mods = event->modifiers(); 0103 if (mods == m_modifiers) { 0104 return; 0105 } 0106 Q_EMIT m_input->keyboardModifiersChanged(mods, m_modifiers); 0107 m_modifiers = mods; 0108 } 0109 0110 private: 0111 InputRedirection *m_input; 0112 Qt::KeyboardModifiers m_modifiers; 0113 }; 0114 0115 void KeyboardInputRedirection::init() 0116 { 0117 Q_ASSERT(!m_inited); 0118 m_inited = true; 0119 const auto config = kwinApp()->kxkbConfig(); 0120 m_xkb->setNumLockConfig(kwinApp()->inputConfig()); 0121 m_xkb->setConfig(config); 0122 0123 // Workaround for QTBUG-54371: if there is no real keyboard Qt doesn't request virtual keyboard 0124 waylandServer()->seat()->setHasKeyboard(true); 0125 // connect(m_input, &InputRedirection::hasAlphaNumericKeyboardChanged, 0126 // waylandServer()->seat(), &KWin::SeatInterface::setHasKeyboard); 0127 0128 m_input->installInputEventSpy(new KeyStateChangedSpy(m_input)); 0129 m_modifiersChangedSpy = new ModifiersChangedSpy(m_input); 0130 m_input->installInputEventSpy(m_modifiersChangedSpy); 0131 m_keyboardLayout = new KeyboardLayout(m_xkb.get(), config); 0132 m_keyboardLayout->init(); 0133 m_input->installInputEventSpy(m_keyboardLayout); 0134 0135 if (waylandServer()->hasGlobalShortcutSupport()) { 0136 m_input->installInputEventSpy(new ModifierOnlyShortcuts); 0137 } 0138 0139 KeyboardRepeat *keyRepeatSpy = new KeyboardRepeat(m_xkb.get()); 0140 connect(keyRepeatSpy, &KeyboardRepeat::keyRepeat, this, 0141 std::bind(&KeyboardInputRedirection::processKey, this, std::placeholders::_1, InputRedirection::KeyboardKeyAutoRepeat, std::placeholders::_2, nullptr)); 0142 m_input->installInputEventSpy(keyRepeatSpy); 0143 0144 connect(workspace(), &QObject::destroyed, this, [this] { 0145 m_inited = false; 0146 }); 0147 connect(waylandServer(), &QObject::destroyed, this, [this] { 0148 m_inited = false; 0149 }); 0150 connect(workspace(), &Workspace::windowActivated, this, [this] { 0151 disconnect(m_activeWindowSurfaceChangedConnection); 0152 if (auto window = workspace()->activeWindow()) { 0153 m_activeWindowSurfaceChangedConnection = connect(window, &Window::surfaceChanged, this, &KeyboardInputRedirection::update); 0154 } else { 0155 m_activeWindowSurfaceChangedConnection = QMetaObject::Connection(); 0156 } 0157 update(); 0158 }); 0159 #if KWIN_BUILD_SCREENLOCKER 0160 if (waylandServer()->hasScreenLockerIntegration()) { 0161 connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this, &KeyboardInputRedirection::update); 0162 } 0163 #endif 0164 0165 reconfigure(); 0166 } 0167 0168 void KeyboardInputRedirection::reconfigure() 0169 { 0170 if (!m_inited) { 0171 return; 0172 } 0173 if (waylandServer()->seat()->keyboard()) { 0174 const auto config = kwinApp()->inputConfig()->group(QStringLiteral("Keyboard")); 0175 const int delay = config.readEntry("RepeatDelay", 660); 0176 const int rate = std::ceil(config.readEntry("RepeatRate", 25.0)); 0177 const QString repeatMode = config.readEntry("KeyRepeat", "repeat"); 0178 // when the clients will repeat the character or turn repeat key events into an accent character selection, we want 0179 // to tell the clients that we are indeed repeating keys. 0180 const bool enabled = repeatMode == QLatin1String("accent") || repeatMode == QLatin1String("repeat"); 0181 0182 waylandServer()->seat()->keyboard()->setRepeatInfo(enabled ? rate : 0, delay); 0183 } 0184 } 0185 0186 void KeyboardInputRedirection::update() 0187 { 0188 if (!m_inited) { 0189 return; 0190 } 0191 auto seat = waylandServer()->seat(); 0192 // TODO: this needs better integration 0193 Window *found = nullptr; 0194 if (waylandServer()->isScreenLocked()) { 0195 const QList<Window *> &stacking = Workspace::self()->stackingOrder(); 0196 if (!stacking.isEmpty()) { 0197 auto it = stacking.end(); 0198 do { 0199 --it; 0200 Window *t = (*it); 0201 if (t->isDeleted()) { 0202 // a deleted window doesn't get mouse events 0203 continue; 0204 } 0205 if (!t->isLockScreen()) { 0206 continue; 0207 } 0208 if (!t->readyForPainting()) { 0209 continue; 0210 } 0211 found = t; 0212 break; 0213 } while (it != stacking.begin()); 0214 } 0215 } else if (!input()->isSelectingWindow()) { 0216 found = workspace()->activeWindow(); 0217 } 0218 if (found && found->surface()) { 0219 if (found->surface() != seat->focusedKeyboardSurface()) { 0220 seat->setFocusedKeyboardSurface(found->surface()); 0221 } 0222 } else { 0223 seat->setFocusedKeyboardSurface(nullptr); 0224 } 0225 } 0226 0227 void KeyboardInputRedirection::processKey(uint32_t key, InputRedirection::KeyboardKeyState state, std::chrono::microseconds time, InputDevice *device) 0228 { 0229 QEvent::Type type; 0230 bool autoRepeat = false; 0231 switch (state) { 0232 case InputRedirection::KeyboardKeyAutoRepeat: 0233 autoRepeat = true; 0234 // fall through 0235 case InputRedirection::KeyboardKeyPressed: 0236 type = QEvent::KeyPress; 0237 break; 0238 case InputRedirection::KeyboardKeyReleased: 0239 type = QEvent::KeyRelease; 0240 break; 0241 default: 0242 Q_UNREACHABLE(); 0243 } 0244 0245 const quint32 previousLayout = m_xkb->currentLayout(); 0246 if (!autoRepeat) { 0247 m_xkb->updateKey(key, state); 0248 } 0249 0250 const xkb_keysym_t keySym = m_xkb->currentKeysym(); 0251 const Qt::KeyboardModifiers globalShortcutsModifiers = m_xkb->modifiersRelevantForGlobalShortcuts(key); 0252 KeyEvent event(type, 0253 m_xkb->toQtKey(keySym, key, globalShortcutsModifiers ? Qt::ControlModifier : Qt::KeyboardModifiers()), 0254 m_xkb->modifiers(), 0255 key, 0256 keySym, 0257 m_xkb->toString(keySym), 0258 autoRepeat, 0259 time, 0260 device); 0261 event.setModifiersRelevantForGlobalShortcuts(globalShortcutsModifiers); 0262 event.setModifiersRelevantForTabBox(m_xkb->modifiersRelevantForTabBox()); 0263 0264 m_input->processSpies(std::bind(&InputEventSpy::keyEvent, std::placeholders::_1, &event)); 0265 if (!m_inited) { 0266 return; 0267 } 0268 input()->setLastInputHandler(this); 0269 m_input->processFilters(std::bind(&InputEventFilter::keyEvent, std::placeholders::_1, &event)); 0270 0271 m_xkb->forwardModifiers(); 0272 if (auto *inputmethod = kwinApp()->inputMethod()) { 0273 inputmethod->forwardModifiers(InputMethod::NoForce); 0274 } 0275 0276 if (event.modifiersRelevantForGlobalShortcuts() == Qt::KeyboardModifier::NoModifier && type != QEvent::KeyRelease) { 0277 m_keyboardLayout->checkLayoutChange(previousLayout); 0278 } 0279 } 0280 0281 } 0282 0283 #include "moc_keyboard_input.cpp"