Split KWin::Xkb into a dedicated .h and .cpp
Summary: Closes T5221 Test Plan: Compiles Reviewers: #kwin, #plasma Subscribers: plasma-devel, kwin Tags: #kwin Maniphest Tasks: T5221 Differential Revision: https://phabricator.kde.org/D4623
This commit is contained in:
parent
65ddd32d1a
commit
58f26b8f55
5 changed files with 627 additions and 549 deletions
|
@ -435,6 +435,7 @@ set(kwin_KDEINIT_SRCS
|
|||
virtualkeyboard.cpp
|
||||
appmenu.cpp
|
||||
modifier_only_shortcuts.cpp
|
||||
xkb.cpp
|
||||
)
|
||||
|
||||
if(KWIN_BUILD_TABBOX)
|
||||
|
|
|
@ -35,465 +35,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
//screenlocker
|
||||
#include <KScreenLocker/KsldApp>
|
||||
// Frameworks
|
||||
#include <KKeyServer>
|
||||
#include <KGlobalAccel>
|
||||
// Qt
|
||||
#include <QKeyEvent>
|
||||
#include <QTemporaryFile>
|
||||
// xkbcommon
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <xkbcommon/xkbcommon-compose.h>
|
||||
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||
// system
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
Q_LOGGING_CATEGORY(KWIN_XKB, "kwin_xkbcommon", QtCriticalMsg)
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static void xkbLogHandler(xkb_context *context, xkb_log_level priority, const char *format, va_list args)
|
||||
{
|
||||
Q_UNUSED(context)
|
||||
char buf[1024];
|
||||
if (std::vsnprintf(buf, 1023, format, args) <= 0) {
|
||||
return;
|
||||
}
|
||||
switch (priority) {
|
||||
case XKB_LOG_LEVEL_DEBUG:
|
||||
qCDebug(KWIN_XKB) << "XKB:" << buf;
|
||||
break;
|
||||
case XKB_LOG_LEVEL_INFO:
|
||||
qCInfo(KWIN_XKB) << "XKB:" << buf;
|
||||
break;
|
||||
case XKB_LOG_LEVEL_WARNING:
|
||||
qCWarning(KWIN_XKB) << "XKB:" << buf;
|
||||
break;
|
||||
case XKB_LOG_LEVEL_ERROR:
|
||||
case XKB_LOG_LEVEL_CRITICAL:
|
||||
default:
|
||||
qCCritical(KWIN_XKB) << "XKB:" << buf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Xkb::Xkb(InputRedirection *input)
|
||||
: m_input(input)
|
||||
, m_context(xkb_context_new(static_cast<xkb_context_flags>(0)))
|
||||
, m_keymap(NULL)
|
||||
, m_state(NULL)
|
||||
, m_shiftModifier(0)
|
||||
, m_capsModifier(0)
|
||||
, m_controlModifier(0)
|
||||
, m_altModifier(0)
|
||||
, m_metaModifier(0)
|
||||
, m_numLock(0)
|
||||
, m_capsLock(0)
|
||||
, m_scrollLock(0)
|
||||
, m_modifiers(Qt::NoModifier)
|
||||
, m_consumedModifiers(Qt::NoModifier)
|
||||
, m_keysym(XKB_KEY_NoSymbol)
|
||||
, m_leds()
|
||||
{
|
||||
qRegisterMetaType<KWin::Xkb::LEDs>();
|
||||
if (!m_context) {
|
||||
qCDebug(KWIN_XKB) << "Could not create xkb context";
|
||||
} else {
|
||||
xkb_context_set_log_level(m_context, XKB_LOG_LEVEL_DEBUG);
|
||||
xkb_context_set_log_fn(m_context, &xkbLogHandler);
|
||||
|
||||
// get locale as described in xkbcommon doc
|
||||
// cannot use QLocale as it drops the modifier part
|
||||
QByteArray locale = qgetenv("LC_ALL");
|
||||
if (locale.isEmpty()) {
|
||||
locale = qgetenv("LC_CTYPE");
|
||||
}
|
||||
if (locale.isEmpty()) {
|
||||
locale = qgetenv("LANG");
|
||||
}
|
||||
if (locale.isEmpty()) {
|
||||
locale = QByteArrayLiteral("C");
|
||||
}
|
||||
|
||||
m_compose.table = xkb_compose_table_new_from_locale(m_context, locale.constData(), XKB_COMPOSE_COMPILE_NO_FLAGS);
|
||||
if (m_compose.table) {
|
||||
m_compose.state = xkb_compose_state_new(m_compose.table, XKB_COMPOSE_STATE_NO_FLAGS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Xkb::~Xkb()
|
||||
{
|
||||
xkb_compose_state_unref(m_compose.state);
|
||||
xkb_compose_table_unref(m_compose.table);
|
||||
xkb_state_unref(m_state);
|
||||
xkb_keymap_unref(m_keymap);
|
||||
xkb_context_unref(m_context);
|
||||
}
|
||||
|
||||
void Xkb::reconfigure()
|
||||
{
|
||||
if (!m_context) {
|
||||
return;
|
||||
}
|
||||
|
||||
xkb_keymap *keymap = nullptr;
|
||||
if (!qEnvironmentVariableIsSet("KWIN_XKB_DEFAULT_KEYMAP")) {
|
||||
keymap = loadKeymapFromConfig();
|
||||
}
|
||||
if (!keymap) {
|
||||
qCDebug(KWIN_XKB) << "Could not create xkb keymap from configuration";
|
||||
keymap = loadDefaultKeymap();
|
||||
}
|
||||
if (keymap) {
|
||||
updateKeymap(keymap);
|
||||
} else {
|
||||
qCDebug(KWIN_XKB) << "Could not create default xkb keymap";
|
||||
}
|
||||
}
|
||||
|
||||
xkb_keymap *Xkb::loadKeymapFromConfig()
|
||||
{
|
||||
// load config
|
||||
if (!m_config) {
|
||||
return nullptr;
|
||||
}
|
||||
const KConfigGroup config = m_config->group("Layout");
|
||||
const QByteArray model = config.readEntry("Model", "pc104").toLocal8Bit();
|
||||
const QByteArray layout = config.readEntry("LayoutList", "").toLocal8Bit();
|
||||
const QByteArray options = config.readEntry("Options", "").toLocal8Bit();
|
||||
|
||||
xkb_rule_names ruleNames = {
|
||||
.rules = nullptr,
|
||||
.model = model.constData(),
|
||||
.layout = layout.constData(),
|
||||
.variant = nullptr,
|
||||
.options = options.constData()
|
||||
};
|
||||
return xkb_keymap_new_from_names(m_context, &ruleNames, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
}
|
||||
|
||||
xkb_keymap *Xkb::loadDefaultKeymap()
|
||||
{
|
||||
return xkb_keymap_new_from_names(m_context, nullptr, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
}
|
||||
|
||||
void Xkb::installKeymap(int fd, uint32_t size)
|
||||
{
|
||||
if (!m_context) {
|
||||
return;
|
||||
}
|
||||
char *map = reinterpret_cast<char*>(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_XKB) << "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_XKB) << "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_capsModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_CAPS);
|
||||
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);
|
||||
|
||||
m_numLock = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_NUM);
|
||||
m_capsLock = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_CAPS);
|
||||
m_scrollLock = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_SCROLL);
|
||||
|
||||
m_currentLayout = xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE);
|
||||
|
||||
createKeymapFile();
|
||||
}
|
||||
|
||||
void Xkb::createKeymapFile()
|
||||
{
|
||||
if (!waylandServer()) {
|
||||
return;
|
||||
}
|
||||
// TODO: uninstall keymap on server?
|
||||
if (!m_keymap) {
|
||||
return;
|
||||
}
|
||||
|
||||
ScopedCPointer<char> 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<char*>(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<xkb_key_direction>(state));
|
||||
if (state == InputRedirection::KeyboardKeyPressed) {
|
||||
const auto sym = toKeysym(key);
|
||||
if (m_compose.state && xkb_compose_state_feed(m_compose.state, sym) == XKB_COMPOSE_FEED_ACCEPTED) {
|
||||
switch (xkb_compose_state_get_status(m_compose.state)) {
|
||||
case XKB_COMPOSE_NOTHING:
|
||||
m_keysym = sym;
|
||||
break;
|
||||
case XKB_COMPOSE_COMPOSED:
|
||||
m_keysym = xkb_compose_state_get_one_sym(m_compose.state);
|
||||
break;
|
||||
default:
|
||||
m_keysym = XKB_KEY_NoSymbol;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
m_keysym = sym;
|
||||
}
|
||||
}
|
||||
updateModifiers();
|
||||
updateConsumedModifiers(key);
|
||||
}
|
||||
|
||||
void Xkb::updateModifiers()
|
||||
{
|
||||
Qt::KeyboardModifiers mods = Qt::NoModifier;
|
||||
if (xkb_state_mod_index_is_active(m_state, m_shiftModifier, XKB_STATE_MODS_EFFECTIVE) == 1 ||
|
||||
xkb_state_mod_index_is_active(m_state, m_capsModifier, 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;
|
||||
|
||||
// update LEDs
|
||||
LEDs leds;
|
||||
if (xkb_state_led_index_is_active(m_state, m_numLock) == 1) {
|
||||
leds = leds | LED::NumLock;
|
||||
}
|
||||
if (xkb_state_led_index_is_active(m_state, m_capsLock) == 1) {
|
||||
leds = leds | LED::CapsLock;
|
||||
}
|
||||
if (xkb_state_led_index_is_active(m_state, m_scrollLock) == 1) {
|
||||
leds = leds | LED::ScrollLock;
|
||||
}
|
||||
if (m_leds != leds) {
|
||||
m_leds = leds;
|
||||
emit m_input->keyboard()->ledsChanged(m_leds);
|
||||
}
|
||||
|
||||
const xkb_layout_index_t layout = xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE);
|
||||
if (layout != m_currentLayout) {
|
||||
m_currentLayout = layout;
|
||||
}
|
||||
if (waylandServer()) {
|
||||
waylandServer()->seat()->updateKeyboardModifiers(xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_DEPRESSED)),
|
||||
xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LATCHED)),
|
||||
xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LOCKED)),
|
||||
layout);
|
||||
}
|
||||
}
|
||||
|
||||
QString Xkb::layoutName() const
|
||||
{
|
||||
return layoutName(m_currentLayout);
|
||||
}
|
||||
|
||||
QString Xkb::layoutName(xkb_layout_index_t layout) const
|
||||
{
|
||||
return QString::fromLocal8Bit(xkb_keymap_layout_get_name(m_keymap, layout));
|
||||
}
|
||||
|
||||
QMap<xkb_layout_index_t, QString> Xkb::layoutNames() const
|
||||
{
|
||||
QMap<xkb_layout_index_t, QString> layouts;
|
||||
const auto size = xkb_keymap_num_layouts(m_keymap);
|
||||
for (xkb_layout_index_t i = 0; i < size; i++) {
|
||||
layouts.insert(i, layoutName(i));
|
||||
}
|
||||
return layouts;
|
||||
}
|
||||
|
||||
void Xkb::updateConsumedModifiers(uint32_t key)
|
||||
{
|
||||
Qt::KeyboardModifiers mods = Qt::NoModifier;
|
||||
if (xkb_state_mod_index_is_consumed2(m_state, key + 8, m_shiftModifier, XKB_CONSUMED_MODE_GTK) == 1) {
|
||||
mods |= Qt::ShiftModifier;
|
||||
}
|
||||
if (xkb_state_mod_index_is_consumed2(m_state, key + 8, m_altModifier, XKB_CONSUMED_MODE_GTK) == 1) {
|
||||
mods |= Qt::AltModifier;
|
||||
}
|
||||
if (xkb_state_mod_index_is_consumed2(m_state, key + 8, m_controlModifier, XKB_CONSUMED_MODE_GTK) == 1) {
|
||||
mods |= Qt::ControlModifier;
|
||||
}
|
||||
if (xkb_state_mod_index_is_consumed2(m_state, key + 8, m_metaModifier, XKB_CONSUMED_MODE_GTK) == 1) {
|
||||
mods |= Qt::MetaModifier;
|
||||
}
|
||||
m_consumedModifiers = mods;
|
||||
}
|
||||
|
||||
Qt::KeyboardModifiers Xkb::modifiersRelevantForGlobalShortcuts() const
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
Qt::KeyboardModifiers consumedMods = m_consumedModifiers;
|
||||
if ((mods & Qt::ShiftModifier) && (consumedMods == Qt::ShiftModifier)) {
|
||||
// test whether current keysym is a letter
|
||||
// in that case the shift should be removed from the consumed modifiers again
|
||||
// otherwise it would not be possible to trigger e.g. Shift+W as a shortcut
|
||||
// see BUG: 370341
|
||||
if (QChar(toQtKey(m_keysym)).isLetter()) {
|
||||
consumedMods = Qt::KeyboardModifiers();
|
||||
}
|
||||
}
|
||||
|
||||
return mods & ~consumedMods;
|
||||
}
|
||||
|
||||
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) const
|
||||
{
|
||||
int key = Qt::Key_unknown;
|
||||
KKeyServer::symXToKeyQt(keysym, &key);
|
||||
return static_cast<Qt::Key>(key);
|
||||
}
|
||||
|
||||
bool Xkb::shouldKeyRepeat(quint32 key) const
|
||||
{
|
||||
if (!m_keymap) {
|
||||
return false;
|
||||
}
|
||||
return xkb_keymap_key_repeats(m_keymap, key + 8) != 0;
|
||||
}
|
||||
|
||||
void Xkb::switchToNextLayout()
|
||||
{
|
||||
if (!m_keymap || !m_state) {
|
||||
return;
|
||||
}
|
||||
const xkb_layout_index_t numLayouts = xkb_keymap_num_layouts(m_keymap);
|
||||
const xkb_layout_index_t nextLayout = (xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE) + 1) % numLayouts;
|
||||
switchToLayout(nextLayout);
|
||||
}
|
||||
|
||||
void Xkb::switchToPreviousLayout()
|
||||
{
|
||||
if (!m_keymap || !m_state) {
|
||||
return;
|
||||
}
|
||||
const xkb_layout_index_t previousLayout = m_currentLayout == 0 ? numberOfLayouts() - 1 : m_currentLayout -1;
|
||||
switchToLayout(previousLayout);
|
||||
}
|
||||
|
||||
void Xkb::switchToLayout(xkb_layout_index_t layout)
|
||||
{
|
||||
if (!m_keymap || !m_state) {
|
||||
return;
|
||||
}
|
||||
if (layout >= numberOfLayouts()) {
|
||||
return;
|
||||
}
|
||||
const xkb_mod_mask_t depressed = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_DEPRESSED));
|
||||
const xkb_mod_mask_t latched = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LATCHED));
|
||||
const xkb_mod_mask_t locked = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LOCKED));
|
||||
xkb_state_update_mask(m_state, depressed, latched, locked, 0, 0, layout);
|
||||
updateModifiers();
|
||||
}
|
||||
|
||||
quint32 Xkb::numberOfLayouts() const
|
||||
{
|
||||
if (!m_keymap) {
|
||||
return 0;
|
||||
}
|
||||
return xkb_keymap_num_layouts(m_keymap);
|
||||
}
|
||||
|
||||
KeyboardInputRedirection::KeyboardInputRedirection(InputRedirection *parent)
|
||||
: QObject(parent)
|
||||
, m_input(parent)
|
||||
|
|
|
@ -21,14 +21,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#define KWIN_KEYBOARD_INPUT_H
|
||||
|
||||
#include "input.h"
|
||||
#include "xkb.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QPointF>
|
||||
|
||||
#include <QLoggingCategory>
|
||||
Q_DECLARE_LOGGING_CATEGORY(KWIN_XKB)
|
||||
|
||||
#include <KSharedConfig>
|
||||
|
||||
class QWindow;
|
||||
|
@ -55,91 +53,6 @@ namespace LibInput
|
|||
class Device;
|
||||
}
|
||||
|
||||
class KWIN_EXPORT Xkb
|
||||
{
|
||||
public:
|
||||
Xkb(InputRedirection *input);
|
||||
~Xkb();
|
||||
void setConfig(KSharedConfigPtr config) {
|
||||
m_config = config;
|
||||
}
|
||||
void reconfigure();
|
||||
|
||||
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);
|
||||
xkb_keysym_t currentKeysym() const {
|
||||
return m_keysym;
|
||||
}
|
||||
QString toString(xkb_keysym_t keysym);
|
||||
Qt::Key toQtKey(xkb_keysym_t keysym) const;
|
||||
Qt::KeyboardModifiers modifiers() const;
|
||||
Qt::KeyboardModifiers modifiersRelevantForGlobalShortcuts() const;
|
||||
bool shouldKeyRepeat(quint32 key) const;
|
||||
|
||||
void switchToNextLayout();
|
||||
void switchToPreviousLayout();
|
||||
void switchToLayout(xkb_layout_index_t layout);
|
||||
|
||||
enum class LED {
|
||||
NumLock = 1 << 0,
|
||||
CapsLock = 1 << 1,
|
||||
ScrollLock = 1 << 2
|
||||
};
|
||||
Q_DECLARE_FLAGS(LEDs, LED)
|
||||
LEDs leds() const {
|
||||
return m_leds;
|
||||
}
|
||||
|
||||
xkb_keymap *keymap() const {
|
||||
return m_keymap;
|
||||
}
|
||||
|
||||
xkb_state *state() const {
|
||||
return m_state;
|
||||
}
|
||||
|
||||
quint32 currentLayout() const {
|
||||
return m_currentLayout;
|
||||
}
|
||||
QString layoutName() const;
|
||||
QMap<xkb_layout_index_t, QString> layoutNames() const;
|
||||
quint32 numberOfLayouts() const;
|
||||
|
||||
private:
|
||||
xkb_keymap *loadKeymapFromConfig();
|
||||
xkb_keymap *loadDefaultKeymap();
|
||||
void updateKeymap(xkb_keymap *keymap);
|
||||
void createKeymapFile();
|
||||
void updateModifiers();
|
||||
void updateConsumedModifiers(uint32_t key);
|
||||
QString layoutName(xkb_layout_index_t layout) const;
|
||||
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_capsModifier;
|
||||
xkb_mod_index_t m_controlModifier;
|
||||
xkb_mod_index_t m_altModifier;
|
||||
xkb_mod_index_t m_metaModifier;
|
||||
xkb_led_index_t m_numLock;
|
||||
xkb_led_index_t m_capsLock;
|
||||
xkb_led_index_t m_scrollLock;
|
||||
Qt::KeyboardModifiers m_modifiers;
|
||||
Qt::KeyboardModifiers m_consumedModifiers;
|
||||
xkb_keysym_t m_keysym;
|
||||
quint32 m_currentLayout = 0;
|
||||
|
||||
struct {
|
||||
xkb_compose_table *table = nullptr;
|
||||
xkb_compose_state *state = nullptr;
|
||||
} m_compose;
|
||||
LEDs m_leds;
|
||||
KSharedConfigPtr m_config;
|
||||
};
|
||||
|
||||
class KWIN_EXPORT KeyboardInputRedirection : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -186,15 +99,6 @@ private:
|
|||
KeyboardLayout *m_keyboardLayout = nullptr;
|
||||
};
|
||||
|
||||
inline
|
||||
Qt::KeyboardModifiers Xkb::modifiers() const
|
||||
{
|
||||
return m_modifiers;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(KWin::Xkb::LED)
|
||||
Q_DECLARE_METATYPE(KWin::Xkb::LEDs)
|
||||
|
||||
#endif
|
||||
|
|
485
xkb.cpp
Normal file
485
xkb.cpp
Normal file
|
@ -0,0 +1,485 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2013, 2016, 2017 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#include "xkb.h"
|
||||
#include "keyboard_input.h"
|
||||
#include "utils.h"
|
||||
#include "wayland_server.h"
|
||||
// frameworks
|
||||
#include <KConfigGroup>
|
||||
#include <KKeyServer>
|
||||
// KWayland
|
||||
#include <KWayland/Server/seat_interface.h>
|
||||
// Qt
|
||||
#include <QTemporaryFile>
|
||||
// xkbcommon
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <xkbcommon/xkbcommon-compose.h>
|
||||
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||
// system
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
Q_LOGGING_CATEGORY(KWIN_XKB, "kwin_xkbcommon", QtCriticalMsg)
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static void xkbLogHandler(xkb_context *context, xkb_log_level priority, const char *format, va_list args)
|
||||
{
|
||||
Q_UNUSED(context)
|
||||
char buf[1024];
|
||||
if (std::vsnprintf(buf, 1023, format, args) <= 0) {
|
||||
return;
|
||||
}
|
||||
switch (priority) {
|
||||
case XKB_LOG_LEVEL_DEBUG:
|
||||
qCDebug(KWIN_XKB) << "XKB:" << buf;
|
||||
break;
|
||||
case XKB_LOG_LEVEL_INFO:
|
||||
qCInfo(KWIN_XKB) << "XKB:" << buf;
|
||||
break;
|
||||
case XKB_LOG_LEVEL_WARNING:
|
||||
qCWarning(KWIN_XKB) << "XKB:" << buf;
|
||||
break;
|
||||
case XKB_LOG_LEVEL_ERROR:
|
||||
case XKB_LOG_LEVEL_CRITICAL:
|
||||
default:
|
||||
qCCritical(KWIN_XKB) << "XKB:" << buf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Xkb::Xkb(InputRedirection *input)
|
||||
: m_input(input)
|
||||
, m_context(xkb_context_new(static_cast<xkb_context_flags>(0)))
|
||||
, m_keymap(NULL)
|
||||
, m_state(NULL)
|
||||
, m_shiftModifier(0)
|
||||
, m_capsModifier(0)
|
||||
, m_controlModifier(0)
|
||||
, m_altModifier(0)
|
||||
, m_metaModifier(0)
|
||||
, m_numLock(0)
|
||||
, m_capsLock(0)
|
||||
, m_scrollLock(0)
|
||||
, m_modifiers(Qt::NoModifier)
|
||||
, m_consumedModifiers(Qt::NoModifier)
|
||||
, m_keysym(XKB_KEY_NoSymbol)
|
||||
, m_leds()
|
||||
{
|
||||
qRegisterMetaType<KWin::Xkb::LEDs>();
|
||||
if (!m_context) {
|
||||
qCDebug(KWIN_XKB) << "Could not create xkb context";
|
||||
} else {
|
||||
xkb_context_set_log_level(m_context, XKB_LOG_LEVEL_DEBUG);
|
||||
xkb_context_set_log_fn(m_context, &xkbLogHandler);
|
||||
|
||||
// get locale as described in xkbcommon doc
|
||||
// cannot use QLocale as it drops the modifier part
|
||||
QByteArray locale = qgetenv("LC_ALL");
|
||||
if (locale.isEmpty()) {
|
||||
locale = qgetenv("LC_CTYPE");
|
||||
}
|
||||
if (locale.isEmpty()) {
|
||||
locale = qgetenv("LANG");
|
||||
}
|
||||
if (locale.isEmpty()) {
|
||||
locale = QByteArrayLiteral("C");
|
||||
}
|
||||
|
||||
m_compose.table = xkb_compose_table_new_from_locale(m_context, locale.constData(), XKB_COMPOSE_COMPILE_NO_FLAGS);
|
||||
if (m_compose.table) {
|
||||
m_compose.state = xkb_compose_state_new(m_compose.table, XKB_COMPOSE_STATE_NO_FLAGS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Xkb::~Xkb()
|
||||
{
|
||||
xkb_compose_state_unref(m_compose.state);
|
||||
xkb_compose_table_unref(m_compose.table);
|
||||
xkb_state_unref(m_state);
|
||||
xkb_keymap_unref(m_keymap);
|
||||
xkb_context_unref(m_context);
|
||||
}
|
||||
|
||||
void Xkb::reconfigure()
|
||||
{
|
||||
if (!m_context) {
|
||||
return;
|
||||
}
|
||||
|
||||
xkb_keymap *keymap = nullptr;
|
||||
if (!qEnvironmentVariableIsSet("KWIN_XKB_DEFAULT_KEYMAP")) {
|
||||
keymap = loadKeymapFromConfig();
|
||||
}
|
||||
if (!keymap) {
|
||||
qCDebug(KWIN_XKB) << "Could not create xkb keymap from configuration";
|
||||
keymap = loadDefaultKeymap();
|
||||
}
|
||||
if (keymap) {
|
||||
updateKeymap(keymap);
|
||||
} else {
|
||||
qCDebug(KWIN_XKB) << "Could not create default xkb keymap";
|
||||
}
|
||||
}
|
||||
|
||||
xkb_keymap *Xkb::loadKeymapFromConfig()
|
||||
{
|
||||
// load config
|
||||
if (!m_config) {
|
||||
return nullptr;
|
||||
}
|
||||
const KConfigGroup config = m_config->group("Layout");
|
||||
const QByteArray model = config.readEntry("Model", "pc104").toLocal8Bit();
|
||||
const QByteArray layout = config.readEntry("LayoutList", "").toLocal8Bit();
|
||||
const QByteArray options = config.readEntry("Options", "").toLocal8Bit();
|
||||
|
||||
xkb_rule_names ruleNames = {
|
||||
.rules = nullptr,
|
||||
.model = model.constData(),
|
||||
.layout = layout.constData(),
|
||||
.variant = nullptr,
|
||||
.options = options.constData()
|
||||
};
|
||||
return xkb_keymap_new_from_names(m_context, &ruleNames, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
}
|
||||
|
||||
xkb_keymap *Xkb::loadDefaultKeymap()
|
||||
{
|
||||
return xkb_keymap_new_from_names(m_context, nullptr, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
}
|
||||
|
||||
void Xkb::installKeymap(int fd, uint32_t size)
|
||||
{
|
||||
if (!m_context) {
|
||||
return;
|
||||
}
|
||||
char *map = reinterpret_cast<char*>(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_XKB) << "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_XKB) << "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_capsModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_CAPS);
|
||||
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);
|
||||
|
||||
m_numLock = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_NUM);
|
||||
m_capsLock = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_CAPS);
|
||||
m_scrollLock = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_SCROLL);
|
||||
|
||||
m_currentLayout = xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE);
|
||||
|
||||
createKeymapFile();
|
||||
}
|
||||
|
||||
void Xkb::createKeymapFile()
|
||||
{
|
||||
if (!waylandServer()) {
|
||||
return;
|
||||
}
|
||||
// TODO: uninstall keymap on server?
|
||||
if (!m_keymap) {
|
||||
return;
|
||||
}
|
||||
|
||||
ScopedCPointer<char> 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<char*>(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<xkb_key_direction>(state));
|
||||
if (state == InputRedirection::KeyboardKeyPressed) {
|
||||
const auto sym = toKeysym(key);
|
||||
if (m_compose.state && xkb_compose_state_feed(m_compose.state, sym) == XKB_COMPOSE_FEED_ACCEPTED) {
|
||||
switch (xkb_compose_state_get_status(m_compose.state)) {
|
||||
case XKB_COMPOSE_NOTHING:
|
||||
m_keysym = sym;
|
||||
break;
|
||||
case XKB_COMPOSE_COMPOSED:
|
||||
m_keysym = xkb_compose_state_get_one_sym(m_compose.state);
|
||||
break;
|
||||
default:
|
||||
m_keysym = XKB_KEY_NoSymbol;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
m_keysym = sym;
|
||||
}
|
||||
}
|
||||
updateModifiers();
|
||||
updateConsumedModifiers(key);
|
||||
}
|
||||
|
||||
void Xkb::updateModifiers()
|
||||
{
|
||||
Qt::KeyboardModifiers mods = Qt::NoModifier;
|
||||
if (xkb_state_mod_index_is_active(m_state, m_shiftModifier, XKB_STATE_MODS_EFFECTIVE) == 1 ||
|
||||
xkb_state_mod_index_is_active(m_state, m_capsModifier, 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;
|
||||
|
||||
// update LEDs
|
||||
LEDs leds;
|
||||
if (xkb_state_led_index_is_active(m_state, m_numLock) == 1) {
|
||||
leds = leds | LED::NumLock;
|
||||
}
|
||||
if (xkb_state_led_index_is_active(m_state, m_capsLock) == 1) {
|
||||
leds = leds | LED::CapsLock;
|
||||
}
|
||||
if (xkb_state_led_index_is_active(m_state, m_scrollLock) == 1) {
|
||||
leds = leds | LED::ScrollLock;
|
||||
}
|
||||
if (m_leds != leds) {
|
||||
m_leds = leds;
|
||||
emit m_input->keyboard()->ledsChanged(m_leds);
|
||||
}
|
||||
|
||||
const xkb_layout_index_t layout = xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE);
|
||||
if (layout != m_currentLayout) {
|
||||
m_currentLayout = layout;
|
||||
}
|
||||
if (waylandServer()) {
|
||||
waylandServer()->seat()->updateKeyboardModifiers(xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_DEPRESSED)),
|
||||
xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LATCHED)),
|
||||
xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LOCKED)),
|
||||
layout);
|
||||
}
|
||||
}
|
||||
|
||||
QString Xkb::layoutName() const
|
||||
{
|
||||
return layoutName(m_currentLayout);
|
||||
}
|
||||
|
||||
QString Xkb::layoutName(xkb_layout_index_t layout) const
|
||||
{
|
||||
return QString::fromLocal8Bit(xkb_keymap_layout_get_name(m_keymap, layout));
|
||||
}
|
||||
|
||||
QMap<xkb_layout_index_t, QString> Xkb::layoutNames() const
|
||||
{
|
||||
QMap<xkb_layout_index_t, QString> layouts;
|
||||
const auto size = xkb_keymap_num_layouts(m_keymap);
|
||||
for (xkb_layout_index_t i = 0; i < size; i++) {
|
||||
layouts.insert(i, layoutName(i));
|
||||
}
|
||||
return layouts;
|
||||
}
|
||||
|
||||
void Xkb::updateConsumedModifiers(uint32_t key)
|
||||
{
|
||||
Qt::KeyboardModifiers mods = Qt::NoModifier;
|
||||
if (xkb_state_mod_index_is_consumed2(m_state, key + 8, m_shiftModifier, XKB_CONSUMED_MODE_GTK) == 1) {
|
||||
mods |= Qt::ShiftModifier;
|
||||
}
|
||||
if (xkb_state_mod_index_is_consumed2(m_state, key + 8, m_altModifier, XKB_CONSUMED_MODE_GTK) == 1) {
|
||||
mods |= Qt::AltModifier;
|
||||
}
|
||||
if (xkb_state_mod_index_is_consumed2(m_state, key + 8, m_controlModifier, XKB_CONSUMED_MODE_GTK) == 1) {
|
||||
mods |= Qt::ControlModifier;
|
||||
}
|
||||
if (xkb_state_mod_index_is_consumed2(m_state, key + 8, m_metaModifier, XKB_CONSUMED_MODE_GTK) == 1) {
|
||||
mods |= Qt::MetaModifier;
|
||||
}
|
||||
m_consumedModifiers = mods;
|
||||
}
|
||||
|
||||
Qt::KeyboardModifiers Xkb::modifiersRelevantForGlobalShortcuts() const
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
Qt::KeyboardModifiers consumedMods = m_consumedModifiers;
|
||||
if ((mods & Qt::ShiftModifier) && (consumedMods == Qt::ShiftModifier)) {
|
||||
// test whether current keysym is a letter
|
||||
// in that case the shift should be removed from the consumed modifiers again
|
||||
// otherwise it would not be possible to trigger e.g. Shift+W as a shortcut
|
||||
// see BUG: 370341
|
||||
if (QChar(toQtKey(m_keysym)).isLetter()) {
|
||||
consumedMods = Qt::KeyboardModifiers();
|
||||
}
|
||||
}
|
||||
|
||||
return mods & ~consumedMods;
|
||||
}
|
||||
|
||||
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) const
|
||||
{
|
||||
int key = Qt::Key_unknown;
|
||||
KKeyServer::symXToKeyQt(keysym, &key);
|
||||
return static_cast<Qt::Key>(key);
|
||||
}
|
||||
|
||||
bool Xkb::shouldKeyRepeat(quint32 key) const
|
||||
{
|
||||
if (!m_keymap) {
|
||||
return false;
|
||||
}
|
||||
return xkb_keymap_key_repeats(m_keymap, key + 8) != 0;
|
||||
}
|
||||
|
||||
void Xkb::switchToNextLayout()
|
||||
{
|
||||
if (!m_keymap || !m_state) {
|
||||
return;
|
||||
}
|
||||
const xkb_layout_index_t numLayouts = xkb_keymap_num_layouts(m_keymap);
|
||||
const xkb_layout_index_t nextLayout = (xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE) + 1) % numLayouts;
|
||||
switchToLayout(nextLayout);
|
||||
}
|
||||
|
||||
void Xkb::switchToPreviousLayout()
|
||||
{
|
||||
if (!m_keymap || !m_state) {
|
||||
return;
|
||||
}
|
||||
const xkb_layout_index_t previousLayout = m_currentLayout == 0 ? numberOfLayouts() - 1 : m_currentLayout -1;
|
||||
switchToLayout(previousLayout);
|
||||
}
|
||||
|
||||
void Xkb::switchToLayout(xkb_layout_index_t layout)
|
||||
{
|
||||
if (!m_keymap || !m_state) {
|
||||
return;
|
||||
}
|
||||
if (layout >= numberOfLayouts()) {
|
||||
return;
|
||||
}
|
||||
const xkb_mod_mask_t depressed = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_DEPRESSED));
|
||||
const xkb_mod_mask_t latched = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LATCHED));
|
||||
const xkb_mod_mask_t locked = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LOCKED));
|
||||
xkb_state_update_mask(m_state, depressed, latched, locked, 0, 0, layout);
|
||||
updateModifiers();
|
||||
}
|
||||
|
||||
quint32 Xkb::numberOfLayouts() const
|
||||
{
|
||||
if (!m_keymap) {
|
||||
return 0;
|
||||
}
|
||||
return xkb_keymap_num_layouts(m_keymap);
|
||||
}
|
||||
|
||||
}
|
140
xkb.h
Normal file
140
xkb.h
Normal file
|
@ -0,0 +1,140 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2013, 2016, 2017 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#ifndef KWIN_XKB_H
|
||||
#define KWIN_XKB_H
|
||||
#include "input.h"
|
||||
|
||||
#include <kwin_export.h>
|
||||
|
||||
#include <KSharedConfig>
|
||||
|
||||
#include <QLoggingCategory>
|
||||
Q_DECLARE_LOGGING_CATEGORY(KWIN_XKB)
|
||||
|
||||
struct xkb_context;
|
||||
struct xkb_keymap;
|
||||
struct xkb_state;
|
||||
struct xkb_compose_table;
|
||||
struct xkb_compose_state;
|
||||
typedef uint32_t xkb_mod_index_t;
|
||||
typedef uint32_t xkb_led_index_t;
|
||||
typedef uint32_t xkb_keysym_t;
|
||||
typedef uint32_t xkb_layout_index_t;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class KWIN_EXPORT Xkb
|
||||
{
|
||||
public:
|
||||
Xkb(InputRedirection *input);
|
||||
~Xkb();
|
||||
void setConfig(KSharedConfigPtr config) {
|
||||
m_config = config;
|
||||
}
|
||||
void reconfigure();
|
||||
|
||||
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);
|
||||
xkb_keysym_t currentKeysym() const {
|
||||
return m_keysym;
|
||||
}
|
||||
QString toString(xkb_keysym_t keysym);
|
||||
Qt::Key toQtKey(xkb_keysym_t keysym) const;
|
||||
Qt::KeyboardModifiers modifiers() const;
|
||||
Qt::KeyboardModifiers modifiersRelevantForGlobalShortcuts() const;
|
||||
bool shouldKeyRepeat(quint32 key) const;
|
||||
|
||||
void switchToNextLayout();
|
||||
void switchToPreviousLayout();
|
||||
void switchToLayout(xkb_layout_index_t layout);
|
||||
|
||||
enum class LED {
|
||||
NumLock = 1 << 0,
|
||||
CapsLock = 1 << 1,
|
||||
ScrollLock = 1 << 2
|
||||
};
|
||||
Q_DECLARE_FLAGS(LEDs, LED)
|
||||
LEDs leds() const {
|
||||
return m_leds;
|
||||
}
|
||||
|
||||
xkb_keymap *keymap() const {
|
||||
return m_keymap;
|
||||
}
|
||||
|
||||
xkb_state *state() const {
|
||||
return m_state;
|
||||
}
|
||||
|
||||
quint32 currentLayout() const {
|
||||
return m_currentLayout;
|
||||
}
|
||||
QString layoutName() const;
|
||||
QMap<xkb_layout_index_t, QString> layoutNames() const;
|
||||
quint32 numberOfLayouts() const;
|
||||
|
||||
private:
|
||||
xkb_keymap *loadKeymapFromConfig();
|
||||
xkb_keymap *loadDefaultKeymap();
|
||||
void updateKeymap(xkb_keymap *keymap);
|
||||
void createKeymapFile();
|
||||
void updateModifiers();
|
||||
void updateConsumedModifiers(uint32_t key);
|
||||
QString layoutName(xkb_layout_index_t layout) const;
|
||||
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_capsModifier;
|
||||
xkb_mod_index_t m_controlModifier;
|
||||
xkb_mod_index_t m_altModifier;
|
||||
xkb_mod_index_t m_metaModifier;
|
||||
xkb_led_index_t m_numLock;
|
||||
xkb_led_index_t m_capsLock;
|
||||
xkb_led_index_t m_scrollLock;
|
||||
Qt::KeyboardModifiers m_modifiers;
|
||||
Qt::KeyboardModifiers m_consumedModifiers;
|
||||
xkb_keysym_t m_keysym;
|
||||
quint32 m_currentLayout = 0;
|
||||
|
||||
struct {
|
||||
xkb_compose_table *table = nullptr;
|
||||
xkb_compose_state *state = nullptr;
|
||||
} m_compose;
|
||||
LEDs m_leds;
|
||||
KSharedConfigPtr m_config;
|
||||
};
|
||||
|
||||
inline
|
||||
Qt::KeyboardModifiers Xkb::modifiers() const
|
||||
{
|
||||
return m_modifiers;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(KWin::Xkb::LED)
|
||||
Q_DECLARE_METATYPE(KWin::Xkb::LEDs)
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue