mirror of
https://github.com/DreamMaoMao/maomaowm.git
synced 2026-03-30 11:11:14 -04:00
feat: support switch keyboard layout for virtual keyboard
This commit is contained in:
parent
2a6a9ba46f
commit
2ebcd5b223
2 changed files with 165 additions and 125 deletions
|
|
@ -881,55 +881,70 @@ int32_t spawn_on_empty(const Arg *arg) {
|
|||
}
|
||||
|
||||
int32_t switch_keyboard_layout(const Arg *arg) {
|
||||
if (!kb_group || !kb_group->wlr_group || !seat) {
|
||||
wlr_log(WLR_ERROR, "Invalid keyboard group or seat");
|
||||
return 0;
|
||||
}
|
||||
KeyboardGroup *group = NULL;
|
||||
wl_list_for_each(group, &keyboard_groups, link) {
|
||||
struct wlr_keyboard *keyboard = group->is_virtual
|
||||
? group->virtual_keyboard
|
||||
: &group->wlr_group->keyboard;
|
||||
if (!keyboard || !keyboard->keymap) {
|
||||
wlr_log(WLR_ERROR, "Invalid keyboard or keymap");
|
||||
continue;
|
||||
}
|
||||
// 1. 获取当前布局和计算下一个布局
|
||||
xkb_layout_index_t current = xkb_state_serialize_layout(
|
||||
keyboard->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE);
|
||||
const int32_t num_layouts = xkb_keymap_num_layouts(keyboard->keymap);
|
||||
if (num_layouts < 2) {
|
||||
wlr_log(WLR_INFO, "Only one layout available");
|
||||
continue;
|
||||
}
|
||||
xkb_layout_index_t next = (current + 1) % num_layouts;
|
||||
|
||||
struct wlr_keyboard *keyboard = &kb_group->wlr_group->keyboard;
|
||||
if (!keyboard || !keyboard->keymap) {
|
||||
wlr_log(WLR_ERROR, "Invalid keyboard or keymap");
|
||||
return 0;
|
||||
}
|
||||
// 6. 应用新 keymap
|
||||
uint32_t depressed = keyboard->modifiers.depressed;
|
||||
uint32_t latched = keyboard->modifiers.latched;
|
||||
uint32_t locked = keyboard->modifiers.locked;
|
||||
|
||||
// 1. 获取当前布局和计算下一个布局
|
||||
xkb_layout_index_t current = xkb_state_serialize_layout(
|
||||
keyboard->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE);
|
||||
const int32_t num_layouts = xkb_keymap_num_layouts(keyboard->keymap);
|
||||
if (num_layouts < 2) {
|
||||
wlr_log(WLR_INFO, "Only one layout available");
|
||||
return 0;
|
||||
}
|
||||
xkb_layout_index_t next = (current + 1) % num_layouts;
|
||||
wlr_keyboard_set_keymap(keyboard, keyboard->keymap);
|
||||
wlr_keyboard_notify_modifiers(keyboard, depressed, latched, locked,
|
||||
next);
|
||||
keyboard->modifiers.group = 0;
|
||||
|
||||
// 6. 应用新 keymap
|
||||
uint32_t depressed = keyboard->modifiers.depressed;
|
||||
uint32_t latched = keyboard->modifiers.latched;
|
||||
uint32_t locked = keyboard->modifiers.locked;
|
||||
// 7. 更新 seat
|
||||
wlr_seat_set_keyboard(seat, keyboard);
|
||||
wlr_seat_keyboard_notify_modifiers(seat, &keyboard->modifiers);
|
||||
|
||||
wlr_keyboard_set_keymap(keyboard, keyboard->keymap);
|
||||
wlr_keyboard_notify_modifiers(keyboard, depressed, latched, locked, next);
|
||||
keyboard->modifiers.group = 0;
|
||||
if (group->is_virtual && group->virtual_keyboard) {
|
||||
|
||||
// 7. 更新 seat
|
||||
wlr_seat_set_keyboard(seat, keyboard);
|
||||
wlr_seat_keyboard_notify_modifiers(seat, &keyboard->modifiers);
|
||||
wlr_keyboard_set_keymap(group->virtual_keyboard, keyboard->keymap);
|
||||
wlr_keyboard_notify_modifiers(group->virtual_keyboard, depressed,
|
||||
latched, locked, next);
|
||||
group->virtual_keyboard->modifiers.group = 0;
|
||||
|
||||
InputDevice *id;
|
||||
wl_list_for_each(id, &inputdevices, link) {
|
||||
if (id->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) {
|
||||
// 7. 更新 seat
|
||||
wlr_seat_set_keyboard(seat, group->virtual_keyboard);
|
||||
wlr_seat_keyboard_notify_modifiers(
|
||||
seat, &group->virtual_keyboard->modifiers);
|
||||
continue;
|
||||
}
|
||||
|
||||
struct wlr_keyboard *tkb = (struct wlr_keyboard *)id->device_data;
|
||||
InputDevice *id;
|
||||
wl_list_for_each(id, &inputdevices, link) {
|
||||
if (id->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
wlr_keyboard_set_keymap(tkb, keyboard->keymap);
|
||||
wlr_keyboard_notify_modifiers(tkb, depressed, latched, locked, next);
|
||||
tkb->modifiers.group = 0;
|
||||
struct wlr_keyboard *tkb = (struct wlr_keyboard *)id->device_data;
|
||||
|
||||
// 7. 更新 seat
|
||||
wlr_seat_set_keyboard(seat, tkb);
|
||||
wlr_seat_keyboard_notify_modifiers(seat, &tkb->modifiers);
|
||||
wlr_keyboard_set_keymap(tkb, keyboard->keymap);
|
||||
wlr_keyboard_notify_modifiers(tkb, depressed, latched, locked,
|
||||
next);
|
||||
tkb->modifiers.group = 0;
|
||||
|
||||
// 7. 更新 seat
|
||||
wlr_seat_set_keyboard(seat, tkb);
|
||||
wlr_seat_keyboard_notify_modifiers(seat, &tkb->modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
printstatus();
|
||||
|
|
|
|||
199
src/mango.c
199
src/mango.c
|
|
@ -435,6 +435,7 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
struct wlr_keyboard_group *wlr_group;
|
||||
struct wl_list link;
|
||||
|
||||
int32_t nsyms;
|
||||
const xkb_keysym_t *keysyms; /* invalid if nsyms == 0 */
|
||||
|
|
@ -445,6 +446,8 @@ typedef struct {
|
|||
struct wl_listener modifiers;
|
||||
struct wl_listener key;
|
||||
struct wl_listener destroy;
|
||||
struct wlr_keyboard *virtual_keyboard;
|
||||
bool is_virtual;
|
||||
} KeyboardGroup;
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -838,6 +841,7 @@ static struct wlr_pointer_constraint_v1 *active_constraint;
|
|||
static struct wlr_seat *seat;
|
||||
static KeyboardGroup *kb_group;
|
||||
static struct wl_list inputdevices;
|
||||
static struct wl_list keyboard_groups;
|
||||
static struct wl_list keyboard_shortcut_inhibitors;
|
||||
static uint32_t cursor_mode;
|
||||
static Client *grabc;
|
||||
|
|
@ -2571,13 +2575,14 @@ void createkeyboard(struct wlr_keyboard *keyboard) {
|
|||
wlr_keyboard_group_add_keyboard(kb_group->wlr_group, keyboard);
|
||||
}
|
||||
|
||||
KeyboardGroup *createkeyboardgroup(void) {
|
||||
KeyboardGroup *createkeyboardgroup(bool is_virtual) {
|
||||
KeyboardGroup *group = ecalloc(1, sizeof(*group));
|
||||
struct xkb_context *context;
|
||||
struct xkb_keymap *keymap;
|
||||
|
||||
group->wlr_group = wlr_keyboard_group_create();
|
||||
group->wlr_group->data = group;
|
||||
group->is_virtual = is_virtual;
|
||||
|
||||
/* Prepare an XKB keymap and assign it to the keyboard group. */
|
||||
context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
|
|
@ -2625,6 +2630,8 @@ KeyboardGroup *createkeyboardgroup(void) {
|
|||
* for all of them. Set this combined wlr_keyboard as the seat keyboard.
|
||||
*/
|
||||
wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
|
||||
|
||||
wl_list_insert(&keyboard_groups, &group->link);
|
||||
return group;
|
||||
}
|
||||
|
||||
|
|
@ -3238,6 +3245,7 @@ void destroykeyboardgroup(struct wl_listener *listener, void *data) {
|
|||
wl_list_remove(&group->key.link);
|
||||
wl_list_remove(&group->modifiers.link);
|
||||
wl_list_remove(&group->destroy.link);
|
||||
wl_list_remove(&group->link);
|
||||
wlr_keyboard_group_destroy(group->wlr_group);
|
||||
free(group);
|
||||
}
|
||||
|
|
@ -3419,8 +3427,9 @@ void requestmonstate(struct wl_listener *listener, void *data) {
|
|||
|
||||
void inputdevice(struct wl_listener *listener, void *data) {
|
||||
/* This event is raised by the backend when a new input device becomes
|
||||
* available.
|
||||
* when the backend is a headless backend, this event will never be triggered.
|
||||
* available.
|
||||
* when the backend is a headless backend, this event will never be
|
||||
* triggered.
|
||||
*/
|
||||
struct wlr_input_device *device = data;
|
||||
uint32_t caps;
|
||||
|
|
@ -4917,96 +4926,109 @@ void setgaps(int32_t oh, int32_t ov, int32_t ih, int32_t iv) {
|
|||
}
|
||||
|
||||
void reset_keyboard_layout(void) {
|
||||
if (!kb_group || !kb_group->wlr_group || !seat) {
|
||||
wlr_log(WLR_ERROR, "Invalid keyboard group or seat");
|
||||
return;
|
||||
}
|
||||
|
||||
struct wlr_keyboard *keyboard = &kb_group->wlr_group->keyboard;
|
||||
if (!keyboard || !keyboard->keymap) {
|
||||
wlr_log(WLR_ERROR, "Invalid keyboard or keymap");
|
||||
return;
|
||||
}
|
||||
KeyboardGroup *group = NULL;
|
||||
|
||||
// Get current layout
|
||||
xkb_layout_index_t current = xkb_state_serialize_layout(
|
||||
keyboard->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE);
|
||||
const int32_t num_layouts = xkb_keymap_num_layouts(keyboard->keymap);
|
||||
if (num_layouts < 1) {
|
||||
wlr_log(WLR_INFO, "No layouts available");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create context
|
||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if (!context) {
|
||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||
return;
|
||||
}
|
||||
|
||||
// 现在安全地创建真正的keymap
|
||||
struct xkb_keymap *new_keymap = xkb_keymap_new_from_names(
|
||||
context, &xkb_rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
if (!new_keymap) {
|
||||
// 理论上这里不应该失败,因为前面已经验证过了
|
||||
wlr_log(WLR_ERROR,
|
||||
"Unexpected failure to create keymap after validation");
|
||||
goto cleanup_context;
|
||||
}
|
||||
|
||||
// 验证新keymap是否有布局
|
||||
const int32_t new_num_layouts = xkb_keymap_num_layouts(new_keymap);
|
||||
if (new_num_layouts < 1) {
|
||||
wlr_log(WLR_ERROR, "New keymap has no layouts");
|
||||
xkb_keymap_unref(new_keymap);
|
||||
goto cleanup_context;
|
||||
}
|
||||
|
||||
// 确保当前布局索引在新keymap中有效
|
||||
if (current >= new_num_layouts) {
|
||||
wlr_log(WLR_INFO,
|
||||
"Current layout index %u out of range for new keymap, "
|
||||
"resetting to 0",
|
||||
current);
|
||||
current = 0;
|
||||
}
|
||||
|
||||
// Apply the new keymap
|
||||
uint32_t depressed = keyboard->modifiers.depressed;
|
||||
uint32_t latched = keyboard->modifiers.latched;
|
||||
uint32_t locked = keyboard->modifiers.locked;
|
||||
|
||||
wlr_keyboard_set_keymap(keyboard, new_keymap);
|
||||
|
||||
wlr_keyboard_notify_modifiers(keyboard, depressed, latched, locked, 0);
|
||||
keyboard->modifiers.group = current; // Keep the same layout index
|
||||
|
||||
// Update seat
|
||||
wlr_seat_set_keyboard(seat, keyboard);
|
||||
wlr_seat_keyboard_notify_modifiers(seat, &keyboard->modifiers);
|
||||
|
||||
InputDevice *id;
|
||||
wl_list_for_each(id, &inputdevices, link) {
|
||||
if (id->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) {
|
||||
wl_list_for_each(group, &keyboard_groups, link) {
|
||||
struct wlr_keyboard *keyboard = group->is_virtual
|
||||
? group->virtual_keyboard
|
||||
: &group->wlr_group->keyboard;
|
||||
if (!keyboard || !keyboard->keymap) {
|
||||
wlr_log(WLR_ERROR, "Invalid keyboard or keymap");
|
||||
continue;
|
||||
}
|
||||
|
||||
struct wlr_keyboard *tkb = (struct wlr_keyboard *)id->device_data;
|
||||
// Get current layout
|
||||
xkb_layout_index_t current = xkb_state_serialize_layout(
|
||||
keyboard->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE);
|
||||
const int32_t num_layouts = xkb_keymap_num_layouts(keyboard->keymap);
|
||||
if (num_layouts < 1) {
|
||||
wlr_log(WLR_INFO, "No layouts available");
|
||||
continue;
|
||||
}
|
||||
|
||||
wlr_keyboard_set_keymap(tkb, keyboard->keymap);
|
||||
wlr_keyboard_notify_modifiers(tkb, depressed, latched, locked, 0);
|
||||
tkb->modifiers.group = 0;
|
||||
// Create context
|
||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if (!context) {
|
||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 7. 更新 seat
|
||||
wlr_seat_set_keyboard(seat, tkb);
|
||||
wlr_seat_keyboard_notify_modifiers(seat, &tkb->modifiers);
|
||||
// 创建keymap
|
||||
struct xkb_keymap *new_keymap = xkb_keymap_new_from_names(
|
||||
context, &xkb_rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
if (!new_keymap) {
|
||||
// 理论上这里不应该失败,因为前面已经验证过了
|
||||
wlr_log(WLR_ERROR,
|
||||
"Unexpected failure to create keymap after validation");
|
||||
goto cleanup_context;
|
||||
}
|
||||
|
||||
// 验证新keymap是否有布局
|
||||
const int32_t new_num_layouts = xkb_keymap_num_layouts(new_keymap);
|
||||
if (new_num_layouts < 1) {
|
||||
wlr_log(WLR_ERROR, "New keymap has no layouts");
|
||||
goto cleanup_context;
|
||||
}
|
||||
|
||||
// 确保当前布局索引在新keymap中有效
|
||||
if (current >= new_num_layouts) {
|
||||
wlr_log(WLR_INFO,
|
||||
"Current layout index %u out of range for new keymap, "
|
||||
"resetting to 0",
|
||||
current);
|
||||
current = 0;
|
||||
}
|
||||
|
||||
// Apply the new keymap
|
||||
uint32_t depressed = keyboard->modifiers.depressed;
|
||||
uint32_t latched = keyboard->modifiers.latched;
|
||||
uint32_t locked = keyboard->modifiers.locked;
|
||||
|
||||
wlr_keyboard_set_keymap(keyboard, new_keymap);
|
||||
|
||||
wlr_keyboard_notify_modifiers(keyboard, depressed, latched, locked, 0);
|
||||
keyboard->modifiers.group = current; // Keep the same layout index
|
||||
|
||||
// Update seat
|
||||
wlr_seat_set_keyboard(seat, keyboard);
|
||||
wlr_seat_keyboard_notify_modifiers(seat, &keyboard->modifiers);
|
||||
|
||||
if (group->is_virtual && group->virtual_keyboard) {
|
||||
|
||||
wlr_keyboard_set_keymap(group->virtual_keyboard, keyboard->keymap);
|
||||
wlr_keyboard_notify_modifiers(group->virtual_keyboard, depressed,
|
||||
latched, locked, 0);
|
||||
group->virtual_keyboard->modifiers.group = 0;
|
||||
|
||||
// 7. 更新 seat
|
||||
wlr_seat_set_keyboard(seat, group->virtual_keyboard);
|
||||
wlr_seat_keyboard_notify_modifiers(
|
||||
seat, &group->virtual_keyboard->modifiers);
|
||||
goto cleanup_context;
|
||||
}
|
||||
|
||||
InputDevice *id;
|
||||
wl_list_for_each(id, &inputdevices, link) {
|
||||
if (id->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct wlr_keyboard *tkb = (struct wlr_keyboard *)id->device_data;
|
||||
|
||||
wlr_keyboard_set_keymap(tkb, keyboard->keymap);
|
||||
wlr_keyboard_notify_modifiers(tkb, depressed, latched, locked, 0);
|
||||
tkb->modifiers.group = 0;
|
||||
|
||||
// 7. 更新 seat
|
||||
wlr_seat_set_keyboard(seat, tkb);
|
||||
wlr_seat_keyboard_notify_modifiers(seat, &tkb->modifiers);
|
||||
}
|
||||
cleanup_context:
|
||||
// Cleanup
|
||||
xkb_keymap_unref(new_keymap);
|
||||
xkb_context_unref(context);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
xkb_keymap_unref(new_keymap);
|
||||
|
||||
cleanup_context:
|
||||
xkb_context_unref(context);
|
||||
}
|
||||
|
||||
void setmon(Client *c, Monitor *m, uint32_t newtags, bool focus) {
|
||||
|
|
@ -5334,6 +5356,7 @@ void setup(void) {
|
|||
* to let us know when new input devices are available on the backend.
|
||||
*/
|
||||
wl_list_init(&inputdevices);
|
||||
wl_list_init(&keyboard_groups);
|
||||
wl_list_init(&keyboard_shortcut_inhibitors);
|
||||
wl_signal_add(&backend->events.new_input, &new_input_device);
|
||||
virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy);
|
||||
|
|
@ -5361,7 +5384,8 @@ void setup(void) {
|
|||
wl_signal_add(&seat->events.request_start_drag, &request_start_drag);
|
||||
wl_signal_add(&seat->events.start_drag, &start_drag);
|
||||
|
||||
kb_group = createkeyboardgroup();
|
||||
kb_group = createkeyboardgroup(false);
|
||||
kb_group->virtual_keyboard = NULL;
|
||||
wl_list_init(&kb_group->destroy.link);
|
||||
|
||||
keyboard_shortcuts_inhibit = wlr_keyboard_shortcuts_inhibit_v1_create(dpy);
|
||||
|
|
@ -5977,7 +6001,8 @@ void virtualkeyboard(struct wl_listener *listener, void *data) {
|
|||
/* virtual keyboards shouldn't share keyboard group */
|
||||
wlr_seat_set_capabilities(seat,
|
||||
seat->capabilities | WL_SEAT_CAPABILITY_KEYBOARD);
|
||||
KeyboardGroup *group = createkeyboardgroup();
|
||||
KeyboardGroup *group = createkeyboardgroup(true);
|
||||
group->virtual_keyboard = &kb->keyboard;
|
||||
/* Set the keymap to match the group keymap */
|
||||
wlr_keyboard_set_keymap(&kb->keyboard, group->wlr_group->keyboard.keymap);
|
||||
LISTEN(&kb->keyboard.base.events.destroy, &group->destroy,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue