backends/x11: Implement own keyboard interception
At the moment, the keyboard interception code in the effects system relies on Qt code processing key events. However, since QDesktopWidget is removed in Qt 6, this is a blocker for Qt 6 port. This change ports the X11 backend to private xkb keymap as indicates in the todo comment. It allows us to drop the last QDesktopWidget usage.master
parent
7aee88581f
commit
bcd43ff44d
@ -0,0 +1,61 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "x11_standalone_effects_keyboard_interception_filter.h"
|
||||
#include "x11_standalone_effects.h"
|
||||
#include "x11_standalone_keyboard.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QKeyEvent>
|
||||
#include <QtXkbCommonSupport/private/qxkbcommon_p.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
EffectsKeyboardInterceptionX11Filter::EffectsKeyboardInterceptionX11Filter(EffectsHandlerImpl *effects, X11Keyboard *keyboard)
|
||||
: X11EventFilter(QVector<int>{XCB_KEY_PRESS, XCB_KEY_RELEASE})
|
||||
, m_effects(effects)
|
||||
, m_keyboard(keyboard)
|
||||
{
|
||||
}
|
||||
|
||||
bool EffectsKeyboardInterceptionX11Filter::event(xcb_generic_event_t *event)
|
||||
{
|
||||
switch (event->response_type & ~0x80) {
|
||||
case XCB_KEY_PRESS: {
|
||||
const auto keyEvent = reinterpret_cast<xcb_key_press_event_t *>(event);
|
||||
processKey(true, keyEvent->detail, keyEvent->time);
|
||||
return true;
|
||||
}
|
||||
case XCB_KEY_RELEASE: {
|
||||
const auto keyEvent = reinterpret_cast<xcb_key_release_event_t *>(event);
|
||||
processKey(false, keyEvent->detail, keyEvent->time);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void EffectsKeyboardInterceptionX11Filter::processKey(bool press, xcb_keycode_t keycode, xcb_timestamp_t timestamp)
|
||||
{
|
||||
const xkb_keysym_t keysym = xkb_state_key_get_one_sym(m_keyboard->xkbState(), keycode);
|
||||
|
||||
Qt::KeyboardModifiers modifiers = m_keyboard->modifiers();
|
||||
if (QXkbCommon::isKeypad(keysym)) {
|
||||
modifiers |= Qt::KeypadModifier;
|
||||
}
|
||||
|
||||
const int qtKey = QXkbCommon::keysymToQtKey(keysym, modifiers, m_keyboard->xkbState(), keycode);
|
||||
const QString text = QXkbCommon::lookupString(m_keyboard->xkbState(), keycode);
|
||||
|
||||
QKeyEvent keyEvent(press ? QEvent::KeyPress : QEvent::KeyRelease, qtKey, modifiers, text);
|
||||
keyEvent.setTimestamp(timestamp);
|
||||
|
||||
m_effects->grabbedKeyboardEvent(&keyEvent);
|
||||
}
|
||||
|
||||
} // namespace KWin
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "x11eventfilter.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class EffectsHandlerImpl;
|
||||
class X11Keyboard;
|
||||
|
||||
class EffectsKeyboardInterceptionX11Filter : public X11EventFilter
|
||||
{
|
||||
public:
|
||||
explicit EffectsKeyboardInterceptionX11Filter(EffectsHandlerImpl *effects, X11Keyboard *keyboard);
|
||||
|
||||
bool event(xcb_generic_event_t *event) override;
|
||||
|
||||
private:
|
||||
void processKey(bool press, xcb_keycode_t keycode, xcb_timestamp_t timestamp);
|
||||
|
||||
EffectsHandlerImpl *m_effects;
|
||||
X11Keyboard *m_keyboard;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
@ -0,0 +1,224 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "x11_standalone_keyboard.h"
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#define explicit dont_use_cxx_explicit
|
||||
#include <xcb/xkb.h>
|
||||
#undef explicit
|
||||
#include <xkbcommon/xkbcommon-x11.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class X11KeyboardFilter : public X11EventFilter
|
||||
{
|
||||
public:
|
||||
X11KeyboardFilter(X11Keyboard *kbd, int eventType)
|
||||
: X11EventFilter(eventType)
|
||||
, m_kbd(kbd)
|
||||
{
|
||||
}
|
||||
|
||||
bool event(xcb_generic_event_t *event) override
|
||||
{
|
||||
return m_kbd->event(event);
|
||||
}
|
||||
|
||||
private:
|
||||
X11Keyboard *m_kbd;
|
||||
};
|
||||
|
||||
X11Keyboard::X11Keyboard()
|
||||
: m_xkbContext(xkb_context_new(XKB_CONTEXT_NO_FLAGS))
|
||||
{
|
||||
const xcb_query_extension_reply_t *reply = xcb_get_extension_data(kwinApp()->x11Connection(), &xcb_xkb_id);
|
||||
if (!reply || !reply->present) {
|
||||
qWarning() << "XKeyboard extension is unavailable";
|
||||
return;
|
||||
}
|
||||
|
||||
m_deviceId = xkb_x11_get_core_keyboard_device_id(kwinApp()->x11Connection());
|
||||
if (m_deviceId == -1) {
|
||||
qWarning() << "xkb_x11_get_core_keyboard_device_id() failed";
|
||||
return;
|
||||
}
|
||||
|
||||
enum {
|
||||
requiredEvents =
|
||||
(XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY
|
||||
| XCB_XKB_EVENT_TYPE_MAP_NOTIFY
|
||||
| XCB_XKB_EVENT_TYPE_STATE_NOTIFY),
|
||||
|
||||
requiredNknDetails =
|
||||
(XCB_XKB_NKN_DETAIL_KEYCODES),
|
||||
|
||||
requiredMapParts =
|
||||
(XCB_XKB_MAP_PART_KEY_TYPES
|
||||
| XCB_XKB_MAP_PART_KEY_SYMS
|
||||
| XCB_XKB_MAP_PART_MODIFIER_MAP
|
||||
| XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS
|
||||
| XCB_XKB_MAP_PART_KEY_ACTIONS
|
||||
| XCB_XKB_MAP_PART_VIRTUAL_MODS
|
||||
| XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP),
|
||||
|
||||
requiredStateDetails =
|
||||
(XCB_XKB_STATE_PART_MODIFIER_BASE
|
||||
| XCB_XKB_STATE_PART_MODIFIER_LATCH
|
||||
| XCB_XKB_STATE_PART_MODIFIER_LOCK
|
||||
| XCB_XKB_STATE_PART_GROUP_BASE
|
||||
| XCB_XKB_STATE_PART_GROUP_LATCH
|
||||
| XCB_XKB_STATE_PART_GROUP_LOCK),
|
||||
};
|
||||
|
||||
static const xcb_xkb_select_events_details_t details = {
|
||||
.affectNewKeyboard = requiredNknDetails,
|
||||
.newKeyboardDetails = requiredNknDetails,
|
||||
.affectState = requiredStateDetails,
|
||||
.stateDetails = requiredStateDetails,
|
||||
};
|
||||
|
||||
xcb_void_cookie_t cookie =
|
||||
xcb_xkb_select_events_aux_checked(kwinApp()->x11Connection(),
|
||||
m_deviceId,
|
||||
requiredEvents,
|
||||
0,
|
||||
0,
|
||||
requiredMapParts,
|
||||
requiredMapParts,
|
||||
&details);
|
||||
|
||||
xcb_generic_error_t *error = xcb_request_check(kwinApp()->x11Connection(), cookie);
|
||||
if (error) {
|
||||
free(error);
|
||||
return;
|
||||
}
|
||||
|
||||
updateKeymap();
|
||||
|
||||
m_filter = std::make_unique<X11KeyboardFilter>(this, reply->first_event);
|
||||
}
|
||||
|
||||
X11Keyboard::~X11Keyboard()
|
||||
{
|
||||
if (m_xkbState) {
|
||||
xkb_state_unref(m_xkbState);
|
||||
m_xkbState = nullptr;
|
||||
}
|
||||
if (m_xkbKeymap) {
|
||||
xkb_keymap_unref(m_xkbKeymap);
|
||||
m_xkbKeymap = nullptr;
|
||||
}
|
||||
if (m_xkbContext) {
|
||||
xkb_context_unref(m_xkbContext);
|
||||
m_xkbContext = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool X11Keyboard::event(xcb_generic_event_t *gevent)
|
||||
{
|
||||
union xkb_event {
|
||||
struct
|
||||
{
|
||||
uint8_t response_type;
|
||||
uint8_t xkbType;
|
||||
uint16_t sequence;
|
||||
xcb_timestamp_t time;
|
||||
uint8_t deviceID;
|
||||
} any;
|
||||
xcb_xkb_new_keyboard_notify_event_t new_keyboard_notify;
|
||||
xcb_xkb_map_notify_event_t map_notify;
|
||||
xcb_xkb_state_notify_event_t state_notify;
|
||||
} *event = reinterpret_cast<union xkb_event *>(gevent);
|
||||
|
||||
if (event->any.deviceID == m_deviceId) {
|
||||
switch (event->any.xkbType) {
|
||||
case XCB_XKB_NEW_KEYBOARD_NOTIFY:
|
||||
if (event->new_keyboard_notify.changed & XCB_XKB_NKN_DETAIL_KEYCODES) {
|
||||
updateKeymap();
|
||||
}
|
||||
break;
|
||||
|
||||
case XCB_XKB_MAP_NOTIFY:
|
||||
updateKeymap();
|
||||
break;
|
||||
|
||||
case XCB_XKB_STATE_NOTIFY:
|
||||
xkb_state_update_mask(m_xkbState,
|
||||
event->state_notify.baseMods,
|
||||
event->state_notify.latchedMods,
|
||||
event->state_notify.lockedMods,
|
||||
event->state_notify.baseGroup,
|
||||
event->state_notify.latchedGroup,
|
||||
event->state_notify.lockedGroup);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void X11Keyboard::updateKeymap()
|
||||
{
|
||||
xkb_keymap *keymap = xkb_x11_keymap_new_from_device(m_xkbContext, kwinApp()->x11Connection(), m_deviceId, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
if (!keymap) {
|
||||
qWarning() << "xkb_x11_keymap_new_from_device() failed";
|
||||
return;
|
||||
}
|
||||
|
||||
xkb_state *state = xkb_x11_state_new_from_device(keymap, kwinApp()->x11Connection(), m_deviceId);
|
||||
if (!state) {
|
||||
xkb_keymap_unref(keymap);
|
||||
qWarning() << "xkb_x11_state_new_from_device() failed";
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_xkbState) {
|
||||
xkb_state_unref(m_xkbState);
|
||||
}
|
||||
if (m_xkbKeymap) {
|
||||
xkb_keymap_unref(m_xkbKeymap);
|
||||
}
|
||||
|
||||
m_xkbState = state;
|
||||
m_xkbKeymap = keymap;
|
||||
}
|
||||
|
||||
xkb_keymap *X11Keyboard::xkbKeymap() const
|
||||
{
|
||||
return m_xkbKeymap;
|
||||
}
|
||||
|
||||
xkb_state *X11Keyboard::xkbState() const
|
||||
{
|
||||
return m_xkbState;
|
||||
}
|
||||
|
||||
Qt::KeyboardModifiers X11Keyboard::modifiers() const
|
||||
{
|
||||
Qt::KeyboardModifiers mods;
|
||||
|
||||
if (xkb_state_mod_name_is_active(m_xkbState, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE) == 1 || xkb_state_mod_name_is_active(m_xkbState, XKB_MOD_NAME_CAPS, XKB_STATE_MODS_EFFECTIVE) == 1) {
|
||||
mods |= Qt::ShiftModifier;
|
||||
}
|
||||
if (xkb_state_mod_name_is_active(m_xkbState, XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE) == 1) {
|
||||
mods |= Qt::AltModifier;
|
||||
}
|
||||
if (xkb_state_mod_name_is_active(m_xkbState, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) == 1) {
|
||||
mods |= Qt::ControlModifier;
|
||||
}
|
||||
if (xkb_state_mod_name_is_active(m_xkbState, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_EFFECTIVE) == 1) {
|
||||
mods |= Qt::MetaModifier;
|
||||
}
|
||||
|
||||
return mods;
|
||||
}
|
||||
|
||||
} // namespace KWin
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "x11eventfilter.h"
|
||||
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class X11KeyboardFilter;
|
||||
|
||||
class X11Keyboard
|
||||
{
|
||||
public:
|
||||
X11Keyboard();
|
||||
~X11Keyboard();
|
||||
|
||||
xkb_keymap *xkbKeymap() const;
|
||||
xkb_state *xkbState() const;
|
||||
Qt::KeyboardModifiers modifiers() const;
|
||||
|
||||
bool event(xcb_generic_event_t *event);
|
||||
|
||||
private:
|
||||
void updateKeymap();
|
||||
|
||||
xkb_context *m_xkbContext = nullptr;
|
||||
xkb_keymap *m_xkbKeymap = nullptr;
|
||||
xkb_state *m_xkbState = nullptr;
|
||||
int32_t m_deviceId = 0;
|
||||
|
||||
std::unique_ptr<X11KeyboardFilter> m_filter;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
Loading…
Reference in New Issue