134 lines
4.0 KiB
C++
134 lines
4.0 KiB
C++
// SPDX-License-Identifier: MIT
|
|
|
|
#include "keyboardlayout.h"
|
|
|
|
#include <QDBusArgument>
|
|
#include <QDBusInterface>
|
|
#include <QDBusMetaType>
|
|
#include <QDBusReply>
|
|
#include <QDebug>
|
|
|
|
#include <memory>
|
|
|
|
#include <xkbcommon/xkbcommon.h>
|
|
|
|
namespace KeyboardLayoutDbus
|
|
{
|
|
struct Layout
|
|
{
|
|
QString name;
|
|
QString variant;
|
|
QString displayName;
|
|
};
|
|
|
|
using Layouts = QList<Layout>;
|
|
|
|
QDBusArgument &operator<<(QDBusArgument &argument, const Layout &layout)
|
|
{
|
|
argument.beginStructure();
|
|
argument << layout.name << layout.variant << layout.displayName;
|
|
argument.endStructure();
|
|
return argument;
|
|
}
|
|
|
|
const QDBusArgument &operator>>(const QDBusArgument &argument, Layout &layout)
|
|
{
|
|
argument.beginStructure();
|
|
argument >> layout.name >> layout.variant >> layout.displayName;
|
|
argument.endStructure();
|
|
return argument;
|
|
}
|
|
}
|
|
|
|
Q_DECLARE_METATYPE(KeyboardLayoutDbus::Layout)
|
|
Q_DECLARE_METATYPE(KeyboardLayoutDbus::Layouts)
|
|
|
|
namespace
|
|
{
|
|
KeyboardLayoutDbus::Layouts configuredLayouts()
|
|
{
|
|
qDBusRegisterMetaType<KeyboardLayoutDbus::Layout>();
|
|
qDBusRegisterMetaType<KeyboardLayoutDbus::Layouts>();
|
|
|
|
QDBusInterface layouts(QStringLiteral("org.kde.KWin"), QStringLiteral("/Layouts"),
|
|
QStringLiteral("org.kde.KeyboardLayouts"));
|
|
const QDBusReply<KeyboardLayoutDbus::Layouts> reply = layouts.call(QStringLiteral("getLayoutsList"));
|
|
if (!reply.isValid()) {
|
|
qWarning() << "Could not read KWin keyboard layouts:" << reply.error().message();
|
|
return {};
|
|
}
|
|
return reply.value();
|
|
}
|
|
}
|
|
|
|
std::optional<Qt::Key> numberRowKey(QStringView layout, QStringView variant, int number, bool shifted)
|
|
{
|
|
if (number < 1 || number > 9) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
using Context = std::unique_ptr<xkb_context, decltype(&xkb_context_unref)>;
|
|
using Keymap = std::unique_ptr<xkb_keymap, decltype(&xkb_keymap_unref)>;
|
|
using State = std::unique_ptr<xkb_state, decltype(&xkb_state_unref)>;
|
|
|
|
const Context context(xkb_context_new(XKB_CONTEXT_NO_FLAGS), xkb_context_unref);
|
|
if (!context) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
const QByteArray layoutName = layout.toLatin1();
|
|
const QByteArray variantName = variant.toLatin1();
|
|
const xkb_rule_names names{
|
|
.layout = layoutName.constData(),
|
|
.variant = variantName.isEmpty() ? nullptr : variantName.constData(),
|
|
};
|
|
const Keymap keymap(xkb_keymap_new_from_names(context.get(), &names, XKB_KEYMAP_COMPILE_NO_FLAGS),
|
|
xkb_keymap_unref);
|
|
if (!keymap) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
const State state(xkb_state_new(keymap.get()), xkb_state_unref);
|
|
if (!state) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
if (shifted) {
|
|
const xkb_mod_index_t shift = xkb_keymap_mod_get_index(keymap.get(), XKB_MOD_NAME_SHIFT);
|
|
if (shift == XKB_MOD_INVALID) {
|
|
return std::nullopt;
|
|
}
|
|
xkb_state_update_mask(state.get(), xkb_mod_mask_t{1} << shift, 0, 0, 0, 0, 0);
|
|
}
|
|
const QByteArray keyName = QByteArrayLiteral("AE0") + QByteArray::number(number);
|
|
const xkb_keycode_t keycode = xkb_keymap_key_by_name(keymap.get(), keyName.constData());
|
|
if (keycode == XKB_KEYCODE_INVALID) {
|
|
return std::nullopt;
|
|
}
|
|
const uint32_t codePoint = xkb_keysym_to_utf32(xkb_state_key_get_one_sym(state.get(), keycode));
|
|
if (codePoint == 0) {
|
|
return std::nullopt;
|
|
}
|
|
return static_cast<Qt::Key>(codePoint);
|
|
}
|
|
|
|
QList<QKeySequence> numberRowShortcuts(int number)
|
|
{
|
|
QList<QKeySequence> shortcuts;
|
|
for (const auto &layout : configuredLayouts()) {
|
|
for (const bool shifted : {false, true}) {
|
|
const auto key = numberRowKey(layout.name, layout.variant, number, shifted);
|
|
if (!key) {
|
|
qWarning() << "Could not map number-row key" << number << "for keyboard layout" << layout.name;
|
|
continue;
|
|
}
|
|
|
|
const QKeySequence sequence(Qt::META | *key);
|
|
if (!shortcuts.contains(sequence)) {
|
|
shortcuts.append(sequence);
|
|
}
|
|
}
|
|
}
|
|
return shortcuts;
|
|
}
|