diff --git a/src/config/parse_config.h b/src/config/parse_config.h index f865782..82fc2ec 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -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) { diff --git a/src/data/static_keymap.h b/src/data/static_keymap.h new file mode 100644 index 0000000..87db83c --- /dev/null +++ b/src/data/static_keymap.h @@ -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} // 结束标记 +}; \ No newline at end of file diff --git a/src/dispatch/dispatch.h b/src/dispatch/dispatch.h index b6cb3d3..5632feb 100644 --- a/src/dispatch/dispatch.h +++ b/src/dispatch/dispatch.h @@ -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); diff --git a/src/maomao.c b/src/maomao.c index 78e27b4..036f4ad 100644 --- a/src/maomao.c +++ b/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;