mirror of
https://github.com/DreamMaoMao/maomaowm.git
synced 2025-10-29 05:40:21 -04:00
feat: add switch_keyboard_layout dispatch
This commit is contained in:
parent
26400b06a8
commit
c8e7e3d528
4 changed files with 256 additions and 2 deletions
|
|
@ -665,6 +665,8 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value,
|
|||
(*arg).v = strdup(arg_value);
|
||||
} else if (strcmp(func_name, "switch_layout") == 0) {
|
||||
func = switch_layout;
|
||||
} else if (strcmp(func_name, "switch_keyboard_layout") == 0) {
|
||||
func = switch_keyboard_layout;
|
||||
} else if (strcmp(func_name, "togglefloating") == 0) {
|
||||
func = togglefloating;
|
||||
} else if (strcmp(func_name, "togglefullscreen") == 0) {
|
||||
|
|
@ -883,7 +885,9 @@ void parse_config_line(Config *config, const char *line) {
|
|||
} else if (!xkb_rules.model && strcmp(key, "xkb_rules_model") == 0) {
|
||||
xkb_rules.model = strdup(value);
|
||||
} else if (!xkb_rules.layout && strcmp(key, "xkb_rules_layout") == 0) {
|
||||
xkb_rules.layout = strdup(value);
|
||||
char *new_layouts_str = malloc(256); // 不要释放,只赋值一次
|
||||
strcpy(new_layouts_str, value);
|
||||
xkb_rules.layout = new_layouts_str;
|
||||
} else if (!xkb_rules.variant && strcmp(key, "xkb_rules_variant") == 0) {
|
||||
xkb_rules.variant = strdup(value);
|
||||
} else if (!xkb_rules.options && strcmp(key, "xkb_rules_options") == 0) {
|
||||
|
|
|
|||
79
src/data/static_keymap.h
Normal file
79
src/data/static_keymap.h
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
typedef struct {
|
||||
const char *full_name;
|
||||
const char *abbr; // 全部使用小写
|
||||
} LayoutMapping;
|
||||
|
||||
static const LayoutMapping layout_mappings[] = {
|
||||
{"English (US)", "us"},
|
||||
{"English (UK)", "uk"},
|
||||
{"Russian", "ru"},
|
||||
{"German", "de"},
|
||||
{"French", "fr"},
|
||||
{"Spanish", "es"},
|
||||
{"Italian", "it"},
|
||||
{"Japanese", "jp"},
|
||||
{"Chinese", "cn"},
|
||||
{"Korean", "kr"},
|
||||
{"Arabic", "ar"},
|
||||
{"Hebrew", "il"},
|
||||
{"Greek", "gr"},
|
||||
{"Turkish", "tr"},
|
||||
{"Portuguese", "pt"},
|
||||
{"Portuguese (Brazil)", "br"},
|
||||
{"Swedish", "se"},
|
||||
{"Norwegian", "no"},
|
||||
{"Danish", "dk"},
|
||||
{"Finnish", "fi"},
|
||||
{"Polish", "pl"},
|
||||
{"Czech", "cz"},
|
||||
{"Hungarian", "hu"},
|
||||
{"Ukrainian", "ua"},
|
||||
{"Belarusian", "by"},
|
||||
{"Bulgarian", "bg"},
|
||||
{"Croatian", "hr"},
|
||||
{"Romanian", "ro"},
|
||||
{"Serbian", "rs"},
|
||||
{"Slovak", "sk"},
|
||||
{"Slovenian", "si"},
|
||||
{"Estonian", "ee"},
|
||||
{"Latvian", "lv"},
|
||||
{"Lithuanian", "lt"},
|
||||
{"Dutch", "nl"},
|
||||
{"Flemish", "be"},
|
||||
{"Swiss German", "ch"},
|
||||
{"French (Canada)", "ca"},
|
||||
{"French (Switzerland)", "ch-fr"},
|
||||
{"Icelandic", "is"},
|
||||
{"Maltese", "mt"},
|
||||
{"Irish", "ie"},
|
||||
{"Albanian", "al"},
|
||||
{"Macedonian", "mk"},
|
||||
{"Bosnian", "ba"},
|
||||
{"Montenegrin", "me"},
|
||||
{"Dvorak", "dv"},
|
||||
{"Colemak", "cm"},
|
||||
{"Workman", "wm"},
|
||||
{"Norman", "nm"},
|
||||
{"QGMLWY", "qg"},
|
||||
{"AZERTY", "az"},
|
||||
{"QWERTZ", "qz"},
|
||||
{"BÉPO (French ergonomic)", "bepo"},
|
||||
{"Neo", "neo"},
|
||||
{"Turkish F", "trf"},
|
||||
{"Tibetan", "bo"},
|
||||
{"Thai", "th"},
|
||||
{"Vietnamese", "vn"},
|
||||
{"Lao", "la"},
|
||||
{"Khmer", "kh"},
|
||||
{"Hindi", "in"},
|
||||
{"Persian", "ir"},
|
||||
{"Urdu", "pk"},
|
||||
{"Bangla", "bd"},
|
||||
{"Sinhala", "lk"},
|
||||
{"Nepali", "np"},
|
||||
{"Tamil", "ta"},
|
||||
{"Telugu", "te"},
|
||||
{"Kannada", "kn"},
|
||||
{"Malayalam", "ml"},
|
||||
{NULL, NULL} // 结束标记
|
||||
};
|
||||
|
|
@ -23,6 +23,7 @@ void spawn(const Arg *arg);
|
|||
void spawn_on_empty(const Arg *arg);
|
||||
void setlayout(const Arg *arg);
|
||||
void switch_layout(const Arg *arg);
|
||||
void switch_keyboard_layout(const Arg *arg);
|
||||
void setmfact(const Arg *arg);
|
||||
void setsmfact(const Arg *arg);
|
||||
void quit(const Arg *arg);
|
||||
|
|
|
|||
172
src/maomao.c
172
src/maomao.c
|
|
@ -651,6 +651,7 @@ static void handlecursoractivity(void);
|
|||
static int hidecursor(void *data);
|
||||
static bool check_hit_no_border(Client *c);
|
||||
|
||||
#include "data/static_keymap.h"
|
||||
#include "dispatch/dispatch.h"
|
||||
|
||||
/* variables */
|
||||
|
|
@ -4242,7 +4243,9 @@ bool keypressglobal(struct wlr_surface *last_surface,
|
|||
break;
|
||||
r = &config.window_rules[ji];
|
||||
|
||||
if (!r->globalkeybinding.mod || (!r->globalkeybinding.keysymcode.keysym && !r->globalkeybinding.keysymcode.keycode))
|
||||
if (!r->globalkeybinding.mod ||
|
||||
(!r->globalkeybinding.keysymcode.keysym &&
|
||||
!r->globalkeybinding.keysymcode.keycode))
|
||||
continue;
|
||||
|
||||
/* match key only (case insensitive) ignoring mods */
|
||||
|
|
@ -5738,6 +5741,173 @@ setlayout(const Arg *arg) {
|
|||
}
|
||||
}
|
||||
|
||||
char *get_layout_abbr(const char *full_name) {
|
||||
// 1. 尝试在映射表中查找
|
||||
for (int i = 0; layout_mappings[i].full_name != NULL; i++) {
|
||||
if (strcmp(full_name, layout_mappings[i].full_name) == 0) {
|
||||
return strdup(layout_mappings[i].abbr);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 尝试从名称中提取并转换为小写
|
||||
const char *open = strrchr(full_name, '(');
|
||||
const char *close = strrchr(full_name, ')');
|
||||
if (open && close && close > open) {
|
||||
size_t len = close - open - 1;
|
||||
if (len > 0 && len <= 4) {
|
||||
char *abbr = malloc(len + 1);
|
||||
if (abbr) {
|
||||
// 提取并转换为小写
|
||||
for (size_t j = 0; j < len; j++) {
|
||||
abbr[j] = tolower(open[j + 1]);
|
||||
}
|
||||
abbr[len] = '\0';
|
||||
return abbr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 提取前2-3个字母并转换为小写
|
||||
char *abbr = malloc(4);
|
||||
if (abbr) {
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; full_name[i] != '\0' && j < 3; i++) {
|
||||
if (isalpha(full_name[i])) {
|
||||
abbr[j++] = tolower(full_name[i]);
|
||||
}
|
||||
}
|
||||
abbr[j] = '\0';
|
||||
|
||||
// 确保至少2个字符
|
||||
if (j >= 2)
|
||||
return abbr;
|
||||
free(abbr);
|
||||
}
|
||||
|
||||
// 4. 回退方案:使用首字母小写
|
||||
char *fallback = malloc(3);
|
||||
if (fallback) {
|
||||
fallback[0] = tolower(full_name[0]);
|
||||
fallback[1] = full_name[1] ? tolower(full_name[1]) : '\0';
|
||||
fallback[2] = '\0';
|
||||
return fallback;
|
||||
}
|
||||
|
||||
// 5. 最终回退:返回 "xx"
|
||||
return strdup("xx");
|
||||
}
|
||||
|
||||
void switch_keyboard_layout(const Arg *arg) {
|
||||
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;
|
||||
}
|
||||
|
||||
// 1. 获取当前布局和计算下一个布局
|
||||
xkb_layout_index_t current = xkb_state_serialize_layout(
|
||||
keyboard->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE);
|
||||
const int num_layouts = xkb_keymap_num_layouts(keyboard->keymap);
|
||||
if (num_layouts < 2) {
|
||||
wlr_log(WLR_INFO, "Only one layout available");
|
||||
return;
|
||||
}
|
||||
xkb_layout_index_t next = (current + 1) % num_layouts;
|
||||
|
||||
// 2. 创建上下文
|
||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if (!context) {
|
||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 分配并获取布局缩写
|
||||
char **layout_ids = calloc(num_layouts, sizeof(char *));
|
||||
if (!layout_ids) {
|
||||
wlr_log(WLR_ERROR, "Failed to allocate layout IDs");
|
||||
goto cleanup_context;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_layouts; i++) {
|
||||
layout_ids[i] =
|
||||
get_layout_abbr(xkb_keymap_layout_get_name(keyboard->keymap, i));
|
||||
if (!layout_ids[i]) {
|
||||
wlr_log(WLR_ERROR, "Failed to get layout abbreviation");
|
||||
goto cleanup_layouts;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 直接修改 rules.layout(保持原有逻辑)
|
||||
struct xkb_rule_names rules = xkb_rules;
|
||||
char *layout_buf = (char *)rules.layout; // 假设这是可修改的
|
||||
|
||||
// 清空原有内容(安全方式)
|
||||
size_t layout_buf_size = strlen(layout_buf) + 1;
|
||||
memset(layout_buf, 0, layout_buf_size);
|
||||
|
||||
// 构建新的布局字符串
|
||||
for (int i = 0; i < num_layouts; i++) {
|
||||
const char *layout = layout_ids[(next + i) % num_layouts];
|
||||
|
||||
if (i > 0) {
|
||||
strncat(layout_buf, ",", layout_buf_size - strlen(layout_buf) - 1);
|
||||
}
|
||||
|
||||
if (strchr(layout, ',')) {
|
||||
// 处理包含逗号的布局名
|
||||
char *quoted = malloc(strlen(layout) + 3);
|
||||
if (quoted) {
|
||||
snprintf(quoted, strlen(layout) + 3, "\"%s\"", layout);
|
||||
strncat(layout_buf, quoted,
|
||||
layout_buf_size - strlen(layout_buf) - 1);
|
||||
free(quoted);
|
||||
}
|
||||
} else {
|
||||
strncat(layout_buf, layout,
|
||||
layout_buf_size - strlen(layout_buf) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 创建新 keymap
|
||||
struct xkb_keymap *new_keymap =
|
||||
xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
if (!new_keymap) {
|
||||
wlr_log(WLR_ERROR, "Failed to create keymap for layouts: %s",
|
||||
rules.layout);
|
||||
goto cleanup_layouts;
|
||||
}
|
||||
|
||||
// 6. 应用新 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 = 0;
|
||||
|
||||
// 7. 更新 seat
|
||||
wlr_seat_set_keyboard(seat, keyboard);
|
||||
wlr_seat_keyboard_notify_modifiers(seat, &keyboard->modifiers);
|
||||
|
||||
// 8. 清理资源
|
||||
xkb_keymap_unref(new_keymap);
|
||||
|
||||
cleanup_layouts:
|
||||
for (int i = 0; i < num_layouts; i++) {
|
||||
free(layout_ids[i]);
|
||||
}
|
||||
free(layout_ids);
|
||||
|
||||
cleanup_context:
|
||||
xkb_context_unref(context);
|
||||
}
|
||||
|
||||
void switch_layout(const Arg *arg) {
|
||||
|
||||
int jk, ji;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue