Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/plugin-qt/shortcut/src/backend/abstractkeyhandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ class AbstractKeyHandler : public QObject

signals:
void keyActivated(const QString &shortcutId);
void numLockStateChanged(bool on);
void capsLockStateChanged(bool on);
void captureStarted();
void captureKeyEvent(bool pressed, const QString &keystroke);
void captureFinished();
Expand Down
48 changes: 48 additions & 0 deletions src/plugin-qt/shortcut/src/backend/x11/x11keyhandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@
#include <QVariant>

#include <xcb/xtest.h>
#include <X11/Xlib.h>

Check warning on line 26 in src/plugin-qt/shortcut/src/backend/x11/x11keyhandler.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <X11/Xlib.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <X11/Xlib-xcb.h>

Check warning on line 27 in src/plugin-qt/shortcut/src/backend/x11/x11keyhandler.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <X11/Xlib-xcb.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <X11/XKBlib.h>

Check warning on line 28 in src/plugin-qt/shortcut/src/backend/x11/x11keyhandler.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <X11/XKBlib.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <X11/extensions/XKBproto.h>

Check warning on line 29 in src/plugin-qt/shortcut/src/backend/x11/x11keyhandler.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <X11/extensions/XKBproto.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <X11/keysym.h>

Check warning on line 30 in src/plugin-qt/shortcut/src/backend/x11/x11keyhandler.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <X11/keysym.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <X11/Xutil.h>

Check warning on line 31 in src/plugin-qt/shortcut/src/backend/x11/x11keyhandler.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <X11/Xutil.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <algorithm>

Check warning on line 33 in src/plugin-qt/shortcut/src/backend/x11/x11keyhandler.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <algorithm> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <cstring>

Check warning on line 34 in src/plugin-qt/shortcut/src/backend/x11/x11keyhandler.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <cstring> not found. Please note: Cppcheck does not need standard library headers to get proper results.

// Need to define XK_MISCELLANY before including keysymdef.h to get Caps_Lock, Num_Lock etc.
#define XK_MISCELLANY
Expand Down Expand Up @@ -164,6 +166,7 @@
}
refreshModifierMasks();
enableDetectableAutoRepeat();
enableLockStateMonitoring();

m_releaseTimer->setSingleShot(true);
m_releaseTimer->setInterval(0);
Expand Down Expand Up @@ -647,6 +650,11 @@
while ((event = xcb_poll_for_event(m_connection))) {
uint8_t responseType = event->response_type & ~0x80;

if (m_xkbEventBase >= 0
&& responseType == static_cast<uint8_t>(m_xkbEventBase)) {
notifyLockStateChange(event);
}

if (m_capture.active && responseType == XCB_KEY_PRESS) {
const CapturedKey captured = captureKey(
reinterpret_cast<xcb_key_press_event_t *>(event));
Expand Down Expand Up @@ -896,6 +904,29 @@
qCWarning(logShortcut) << "X11KeyHandler: falling back to Release/Press autorepeat detection";
}

void X11KeyHandler::enableLockStateMonitoring()
{
int opcode = 0;
int errorBase = 0;
int major = XkbMajorVersion;
int minor = XkbMinorVersion;
if (!m_display || !XkbQueryExtension(m_display, &opcode, &m_xkbEventBase,
&errorBase, &major, &minor)) {
m_xkbEventBase = -1;
qCWarning(logShortcut) << "X11KeyHandler: XKB extension is unavailable;"
" lock state changes cannot be monitored";
return;
}

if (!XkbSelectEventDetails(m_display, XkbUseCoreKbd, XkbStateNotify,
XkbModifierLockMask, XkbModifierLockMask)) {
m_xkbEventBase = -1;
qCWarning(logShortcut) << "X11KeyHandler: Failed to subscribe to XKB lock state changes";
return;
}
XFlush(m_display);
}

void X11KeyHandler::handleKeyPress(const xcb_key_press_event_t *event)
{
const auto pending = m_pendingReleases.constFind(event->detail);
Expand Down Expand Up @@ -937,6 +968,23 @@
activate(shortcutId, KeyEventFlag::Press);
}

void X11KeyHandler::notifyLockStateChange(const xcb_generic_event_t *event)
{
if (!event)
return;

static_assert(sizeof(xkbStateNotify) == sz_xkbStateNotify);
xkbStateNotify stateEvent{};
std::memcpy(&stateEvent, event, sizeof(stateEvent));
if (stateEvent.xkbType != XkbStateNotify
|| !(stateEvent.changed & XkbModifierLockMask)) {
return;
}

emit numLockStateChanged(stateEvent.lockedMods & m_numLockMask);
emit capsLockStateChanged(stateEvent.lockedMods & m_capsLockMask);
}

void X11KeyHandler::handleKeyRelease(const xcb_key_release_event_t *event)
{
if (!m_pressedBindings.contains(event->detail))
Expand Down
3 changes: 3 additions & 0 deletions src/plugin-qt/shortcut/src/backend/x11/x11keyhandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,10 @@ private slots:
QList<uint16_t> ignoredModifierCombinations() const;
void refreshModifierMasks();
void enableDetectableAutoRepeat();
void enableLockStateMonitoring();
void handleKeyPress(const xcb_key_press_event_t *event);
void handleKeyRelease(const xcb_key_release_event_t *event);
void notifyLockStateChange(const xcb_generic_event_t *event);
CapturedKey captureKey(const xcb_key_press_event_t *event) const;
bool isCapturedKeyValid(const CapturedKey &key) const;
bool hasAnyMask(uint16_t state, const QList<uint16_t> &masks) const;
Expand All @@ -107,6 +109,7 @@ private slots:
QList<xcb_window_t> m_rootWindows;
xcb_key_symbols_t *m_keySymbols = nullptr;
QSocketNotifier *m_notifier = nullptr;
int m_xkbEventBase = -1;

// Standalone modifier shortcut monitoring.
ModifierKeyMonitor *m_modifierMonitor = nullptr;
Expand Down
30 changes: 28 additions & 2 deletions src/plugin-qt/shortcut/src/core/keybindingmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,12 @@ KeybindingManager::KeybindingManager(ConfigLoader *loader, ActionExecutor *execu

// Connect signals from key handler
connect(m_keyHandler, &AbstractKeyHandler::keyActivated, this, &KeybindingManager::onKeyActivated);
connect(m_keyHandler, &AbstractKeyHandler::numLockStateChanged,
this, &KeybindingManager::updateNumLockState);
connect(m_keyHandler, &AbstractKeyHandler::capsLockStateChanged,
this, &KeybindingManager::updateCapsLockState);
m_lastNumLockState = GetNumLockState();
m_lastCapsLockState = GetCapsLockState();
if (!m_isWayland) {
connect(m_keyHandler, &AbstractKeyHandler::captureStarted,
this, [this] { m_specialKeyHandler->setEnabled(false); });
Expand Down Expand Up @@ -1279,6 +1285,26 @@ void KeybindingManager::onCaptureKeyEvent(bool pressed, const QString &keystroke
emit KeyEvent(pressed, keystroke);
}

void KeybindingManager::updateNumLockState(bool on)
{
const uint state = on ? 1U : 0U;
if (m_lastNumLockState == state)
return;

m_lastNumLockState = state;
emit NumLockStateChanged(state);
}

void KeybindingManager::updateCapsLockState(bool on)
{
const uint state = on ? 1U : 0U;
if (m_lastCapsLockState == state)
return;

m_lastCapsLockState = state;
emit CapsLockStateChanged(state);
}

bool KeybindingManager::registerShortcut(const KeyConfig &config, const QStringList &excludeIds)
{
if (!config.canRegister()) {
Expand Down Expand Up @@ -1515,7 +1541,7 @@ void KeybindingManager::SetNumLockState(uint state)
uint oldState = GetNumLockState();
m_keyHandler->setNumLockState(state != 0);
if (oldState != state) {
emit NumLockStateChanged(state);
updateNumLockState(state != 0);
}
}
}
Expand All @@ -1526,7 +1552,7 @@ void KeybindingManager::SetCapsLockState(uint state)
uint oldState = GetCapsLockState();
m_keyHandler->setCapsLockState(state != 0);
if (oldState != state) {
emit CapsLockStateChanged(state);
updateCapsLockState(state != 0);
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/plugin-qt/shortcut/src/core/keybindingmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ private slots:
void onConfigRemoved(const QString &id);
void onKeyActivated(const QString &shortcutId);
void onCaptureKeyEvent(bool pressed, const QString &keystroke);
void updateNumLockState(bool on);
void updateCapsLockState(bool on);
void onBackendKeymapAboutToChange();
void onBackendKeymapChanged();
ShortcutInfo toShortcutInfo(const KeyConfig &config);
Expand Down Expand Up @@ -181,6 +183,8 @@ private slots:
QMap<QString, KeyConfig> m_keyConfigsMap;
QSet<QString> m_activeShortcutIds;
QSet<QString> m_resetInProgressIds;
uint m_lastNumLockState = 0;
uint m_lastCapsLockState = 0;
bool m_isWayland = false;
};

Expand Down
Loading