diff --git a/misc/dconfig/org.deepin.dde.treeland.user.seat.json b/misc/dconfig/org.deepin.dde.treeland.user.seat.json index 4289602a4f..e4414d10c6 100644 --- a/misc/dconfig/org.deepin.dde.treeland.user.seat.json +++ b/misc/dconfig/org.deepin.dde.treeland.user.seat.json @@ -155,6 +155,50 @@ "description[zh_CN]": "收到 libinput HOLD BEGIN 事件后等待多少毫秒再触发 hold 手势动作。", "permissions": "readwrite", "visibility": "public" + }, + "xkbLayout": { + "value": "", + "serial": 0, + "flags": ["global"], + "name": "Keyboard Layout", + "name[zh_CN]": "键盘布局", + "description": "XKB keyboard layout(s). Comma-separated list for multiple layouts, e.g. \"us\" or \"cz,us\". Empty string means XKB default (\"us\").", + "description[zh_CN]": "XKB 键盘布局。多个布局用逗号分隔,如 \"us\" 或 \"cz,us\"。空字符串表示 XKB 默认值(\"us\")。", + "permissions": "readwrite", + "visibility": "public" + }, + "xkbModel": { + "value": "", + "serial": 0, + "flags": ["global"], + "name": "Keyboard Model", + "name[zh_CN]": "键盘型号", + "description": "XKB keyboard model, e.g. \"pc104\" or \"pc105\". Empty string means XKB default.", + "description[zh_CN]": "XKB 键盘型号,如 \"pc104\" 或 \"pc105\"。空字符串表示 XKB 默认值。", + "permissions": "readwrite", + "visibility": "public" + }, + "xkbVariant": { + "value": "", + "serial": 0, + "flags": ["global"], + "name": "Keyboard Variant", + "name[zh_CN]": "键盘变体", + "description": "XKB keyboard variant(s). Comma-separated list matching the layouts, e.g. \"\" or \",dvorak\". Empty string means no variant (XKB default).", + "description[zh_CN]": "XKB 键盘变体。用逗号分隔以对应各个布局,如 \"\" 或 \",dvorak\"。空字符串表示无变体(XKB 默认值)。", + "permissions": "readwrite", + "visibility": "public" + }, + "xkbOptions": { + "value": "", + "serial": 0, + "flags": ["global"], + "name": "Keyboard Options", + "name[zh_CN]": "键盘选项", + "description": "XKB keyboard options, comma-separated, e.g. \"grp:win_space_toggle\". Empty string means no extra options.", + "description[zh_CN]": "XKB 键盘选项,逗号分隔,如 \"grp:win_space_toggle\"。空字符串表示无额外选项。", + "permissions": "readwrite", + "visibility": "public" } } } diff --git a/src/input/inputmanager.cpp b/src/input/inputmanager.cpp index 41f92d8d5a..6c81c699b4 100644 --- a/src/input/inputmanager.cpp +++ b/src/input/inputmanager.cpp @@ -6,12 +6,14 @@ #include "treelandconfig.hpp" #include "helper.h" #include "inputdevice.h" +#include "common/treelandlogging.h" #include "modules/input-manager/inputmanagerinterfacev1.h" #include "seat/seatsmanager.h" #include "session/session.h" #include "xsettings/settingmanager.h" #include +#include #include @@ -94,6 +96,13 @@ void InputManager::onConfigInitializeSucceed() InputDevice::instance()->setHoldTimeout(m_seatDConfig->touchpadHoldTimeoutMs()); + applyXkbConfig(); + + connect(m_seatDConfig, &SeatUserDConfig::xkbLayoutChanged, this, &InputManager::applyXkbConfig); + connect(m_seatDConfig, &SeatUserDConfig::xkbModelChanged, this, &InputManager::applyXkbConfig); + connect(m_seatDConfig, &SeatUserDConfig::xkbVariantChanged, this, &InputManager::applyXkbConfig); + connect(m_seatDConfig, &SeatUserDConfig::xkbOptionsChanged, this, &InputManager::applyXkbConfig); + auto backend = Helper::instance()->backend(); connect(backend, &WBackend::inputAdded, @@ -571,6 +580,32 @@ void InputManager::applyNumLockToKeyboards() } } +void InputManager::applyXkbConfig() +{ + if (!m_seatDConfig || !isSeatDConfigInitialized(m_seatDConfig)) + return; + + struct xkb_rule_names rules = {}; + QByteArray layout = m_seatDConfig->xkbLayout().toUtf8(); + QByteArray model = m_seatDConfig->xkbModel().toUtf8(); + QByteArray variant = m_seatDConfig->xkbVariant().toUtf8(); + QByteArray options = m_seatDConfig->xkbOptions().toUtf8(); + rules.layout = layout.constData(); + rules.model = model.constData(); + rules.variant = variant.constData(); + rules.options = options.constData(); + + auto *seatManager = Helper::instance()->seatManager(); + if (seatManager) { + const auto seats = seatManager->seats(); + for (WSeat *seat : seats) { + seat->setXkbRuleNames(rules); + } + } + + applyNumLockToKeyboards(); +} + void InputManager::setNumLockForSeat(WSeat *seat, bool enabled) { if (!seat) diff --git a/src/input/inputmanager.h b/src/input/inputmanager.h index 5152ef33c6..e6190e9822 100644 --- a/src/input/inputmanager.h +++ b/src/input/inputmanager.h @@ -43,6 +43,7 @@ private Q_SLOTS: private: bool initializeKeyboardSettings(KeyboardSettingsInterfaceV1 *interface); void applyNumLockToKeyboards(); + void applyXkbConfig(); static void setNumLockForSeat(WSeat *seat, bool enabled); SeatUserDConfig* m_seatDConfig = nullptr; diff --git a/waylib/src/server/kernel/wseat.cpp b/waylib/src/server/kernel/wseat.cpp index 0e6324101f..1a92a369c0 100644 --- a/waylib/src/server/kernel/wseat.cpp +++ b/waylib/src/server/kernel/wseat.cpp @@ -98,6 +98,12 @@ class Q_DECL_HIDDEN WSeatPrivate : public WObjectPrivate detachInputDevice(device); destroyKeyboardGroup(); + + free(const_cast(xkbRules.rules)); + free(const_cast(xkbRules.layout)); + free(const_cast(xkbRules.model)); + free(const_cast(xkbRules.variant)); + free(const_cast(xkbRules.options)); } // Release the keyboard group and its WInputDevice wrapper. Shared by @@ -367,6 +373,7 @@ class Q_DECL_HIDDEN WSeatPrivate : public WObjectPrivate void detachInputDevice(WInputDevice *device); // handle spontaneous & synthetic key event for focusWindow void handleKeyEvent(QKeyEvent &e); + void applyXkbConfig(); W_DECLARE_PUBLIC(WSeat) @@ -387,6 +394,7 @@ class Q_DECL_HIDDEN WSeatPrivate : public WObjectPrivate qreal lastScale = 1.0; wlr_keyboard_group *group = nullptr; WInputDevice *groupkeyboardDevice = nullptr; + struct xkb_rule_names xkbRules = {}; struct EventState { // Don't use it, its may be a invalid pointer @@ -673,11 +681,9 @@ void WSeatPrivate::attachInputDevice(WInputDevice *device) auto keyboard = wlr_keyboard_from_input_device(device->handle()); if (device == groupkeyboardDevice || device->isVirtual()) { - /* We need to prepare an XKB keymap and assign it to the keyboard. - * This assumes the defaults (e.g. layout = "us"). */ - struct xkb_rule_names rules = {}; + /* We need to prepare an XKB keymap and assign it to the keyboard. */ struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules, + struct xkb_keymap *keymap = xkb_map_new_from_names(context, &xkbRules, XKB_KEYMAP_COMPILE_NO_FLAGS); wlr_keyboard_set_keymap(keyboard, keymap); xkb_keymap_unref(keymap); @@ -702,9 +708,8 @@ void WSeatPrivate::attachInputDevice(WInputDevice *device) qCWarning(lcWlSeat, "WSeat: group keyboard has no keymap for physical keyboard '%s'", qPrintable(device->name())); - struct xkb_rule_names rules = {}; struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules, + struct xkb_keymap *keymap = xkb_map_new_from_names(context, &xkbRules, XKB_KEYMAP_COMPILE_NO_FLAGS); wlr_keyboard_set_keymap(keyboard, keymap); xkb_keymap_unref(keymap); @@ -743,6 +748,42 @@ void WSeatPrivate::detachInputDevice(WInputDevice *device) Q_ASSERT(ok); } +void WSeatPrivate::applyXkbConfig() +{ + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (!context) { + qCWarning(lcWlSeat) << "Failed to create XKB context"; + return; + } + + struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, &xkbRules, + XKB_KEYMAP_COMPILE_NO_FLAGS); + xkb_context_unref(context); + if (!keymap) { + qCWarning(lcWlSeat) << "Failed to compile XKB keymap from rule names"; + return; + } + + if (groupkeyboardDevice && groupkeyboardDevice->handle()->type == WLR_INPUT_DEVICE_KEYBOARD) { + auto *keyboard = wlr_keyboard_from_input_device(groupkeyboardDevice->handle()); + wlr_keyboard_set_keymap(keyboard, keymap); + } + + for (auto *device : std::as_const(deviceList)) { + if (device->type() != WInputDevice::Type::Keyboard) + continue; + + if (device->handle()->type != WLR_INPUT_DEVICE_KEYBOARD) + continue; + + auto *keyboard = wlr_keyboard_from_input_device(device->handle()); + if (keyboard) + wlr_keyboard_set_keymap(keyboard, keymap); + } + + xkb_keymap_unref(keymap); +} + WSeat::WSeat(const QString &name) : QObject(nullptr) , WObject(*new WSeatPrivate(this, name)) @@ -1193,6 +1234,25 @@ void WSeat::setKeyboard(WInputDevice *newKeyboard) Q_EMIT this->keyboardChanged(); } +void WSeat::setXkbRuleNames(const struct xkb_rule_names &rules) +{ + W_D(WSeat); + + free(const_cast(d->xkbRules.rules)); + free(const_cast(d->xkbRules.layout)); + free(const_cast(d->xkbRules.model)); + free(const_cast(d->xkbRules.variant)); + free(const_cast(d->xkbRules.options)); + + d->xkbRules.rules = rules.rules ? strdup(rules.rules) : nullptr; + d->xkbRules.layout = rules.layout ? strdup(rules.layout) : nullptr; + d->xkbRules.model = rules.model ? strdup(rules.model) : nullptr; + d->xkbRules.variant = rules.variant ? strdup(rules.variant) : nullptr; + d->xkbRules.options = rules.options ? strdup(rules.options) : nullptr; + + d->applyXkbConfig(); +} + bool WSeat::alwaysUpdateHoverTarget() const { W_DC(WSeat); diff --git a/waylib/src/server/kernel/wseat.h b/waylib/src/server/kernel/wseat.h index 54b6c9b0ec..5cf5308fb2 100644 --- a/waylib/src/server/kernel/wseat.h +++ b/waylib/src/server/kernel/wseat.h @@ -10,6 +10,7 @@ #include #include +#include Q_MOC_INCLUDE() @@ -77,6 +78,8 @@ class WAYLIB_SERVER_EXPORT WSeat : public QObject, public WObject, public WServe void attachInputDevice(WInputDevice *device); void detachInputDevice(WInputDevice *device); + void setXkbRuleNames(const struct xkb_rule_names &rules); + // WSurfaceItem is a kind of shellObject static bool sendEvent(WSurface *target, QObject *shellObject, QObject *eventObject, QInputEvent *event); static WSeat *get(QInputEvent *event);