mirror of
				https://github.com/DreamMaoMao/maomaowm.git
				synced 2025-10-29 05:40:21 -04:00 
			
		
		
		
	Merge pull request #326 from DreamMaoMao/bindsymtocode
feat: auto convert keysym to keycode bind
This commit is contained in:
		
						commit
						a31b95b349
					
				
					 4 changed files with 232 additions and 41 deletions
				
			
		|  | @ -20,9 +20,15 @@ enum { NUM_TYPE_MINUS, NUM_TYPE_PLUS, NUM_TYPE_DEFAULT }; | |||
| 
 | ||||
| enum { KEY_TYPE_SYM, KEY_TYPE_CODE }; | ||||
| 
 | ||||
| typedef struct { | ||||
| 	uint32_t keycode1; | ||||
| 	uint32_t keycode2; | ||||
| 	uint32_t keycode3; | ||||
| } MultiKeycode; | ||||
| 
 | ||||
| typedef struct { | ||||
| 	xkb_keysym_t keysym; | ||||
| 	uint32_t keycode; | ||||
| 	MultiKeycode keycode; | ||||
| 	int type; | ||||
| } KeySymCode; | ||||
| 
 | ||||
|  | @ -325,6 +331,8 @@ typedef struct { | |||
| 
 | ||||
| 	char keymode[28]; | ||||
| 
 | ||||
| 	struct xkb_context *ctx; | ||||
| 	struct xkb_keymap *keymap; | ||||
| } Config; | ||||
| 
 | ||||
| typedef int (*FuncType)(const Arg *); | ||||
|  | @ -613,23 +621,120 @@ uint32_t parse_mod(const char *mod_str) { | |||
| 	return mod; | ||||
| } | ||||
| 
 | ||||
| KeySymCode parse_key(const char *key_str) { | ||||
| 	KeySymCode kc; | ||||
| // 定义辅助函数:在 keymap 中查找 keysym 对应的多个 keycode
 | ||||
| static int find_keycodes_for_keysym(struct xkb_keymap *keymap, xkb_keysym_t sym, | ||||
| 									MultiKeycode *multi_kc) { | ||||
| 	xkb_keycode_t min_keycode = xkb_keymap_min_keycode(keymap); | ||||
| 	xkb_keycode_t max_keycode = xkb_keymap_max_keycode(keymap); | ||||
| 
 | ||||
| 	multi_kc->keycode1 = 0; | ||||
| 	multi_kc->keycode2 = 0; | ||||
| 	multi_kc->keycode3 = 0; | ||||
| 
 | ||||
| 	int found_count = 0; | ||||
| 
 | ||||
| 	for (xkb_keycode_t keycode = min_keycode; | ||||
| 		 keycode <= max_keycode && found_count < 3; keycode++) { | ||||
| 		// 使用布局0和层级0
 | ||||
| 		const xkb_keysym_t *syms; | ||||
| 		int num_syms = | ||||
| 			xkb_keymap_key_get_syms_by_level(keymap, keycode, 0, 0, &syms); | ||||
| 
 | ||||
| 		for (int i = 0; i < num_syms; i++) { | ||||
| 			if (syms[i] == sym) { | ||||
| 				switch (found_count) { | ||||
| 				case 0: | ||||
| 					multi_kc->keycode1 = keycode; | ||||
| 					break; | ||||
| 				case 1: | ||||
| 					multi_kc->keycode2 = keycode; | ||||
| 					break; | ||||
| 				case 2: | ||||
| 					multi_kc->keycode3 = keycode; | ||||
| 					break; | ||||
| 				} | ||||
| 				found_count++; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return found_count; | ||||
| } | ||||
| 
 | ||||
| void cleanup_config_keymap(void) { | ||||
| 	if (config.keymap != NULL) { | ||||
| 		xkb_keymap_unref(config.keymap); | ||||
| 		config.keymap = NULL; | ||||
| 	} | ||||
| 	if (config.ctx != NULL) { | ||||
| 		xkb_context_unref(config.ctx); | ||||
| 		config.ctx = NULL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void create_config_keymap(void) { | ||||
| 	// 初始化 xkb 上下文和 keymap
 | ||||
| 
 | ||||
| 	if (config.ctx == NULL) { | ||||
| 		config.ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); | ||||
| 	} | ||||
| 
 | ||||
| 	if (config.keymap == NULL) { | ||||
| 		config.keymap = xkb_keymap_new_from_names( | ||||
| 			config.ctx, &xkb_fallback_rules, XKB_KEYMAP_COMPILE_NO_FLAGS); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| KeySymCode parse_key(const char *key_str, bool isbindsym) { | ||||
| 	KeySymCode kc = {0}; // 初始化为0
 | ||||
| 
 | ||||
| 	if (config.keymap == NULL || config.ctx == NULL) { | ||||
| 		// 处理错误
 | ||||
| 		kc.type = KEY_TYPE_SYM; | ||||
| 		kc.keysym = XKB_KEY_NoSymbol; | ||||
| 		return kc; | ||||
| 	} | ||||
| 
 | ||||
| 	// 处理 code: 前缀的情况
 | ||||
| 	if (strncmp(key_str, "code:", 5) == 0) { | ||||
| 		char *endptr; | ||||
| 		xkb_keycode_t keycode = (xkb_keycode_t)strtol(key_str + 5, &endptr, 10); | ||||
| 		kc.type = KEY_TYPE_CODE; | ||||
| 		kc.keycode = keycode; | ||||
| 		kc.keycode.keycode1 = keycode; // 只设置第一个
 | ||||
| 		kc.keycode.keycode2 = 0; | ||||
| 		kc.keycode.keycode3 = 0; | ||||
| 		return kc; | ||||
| 	} | ||||
| 
 | ||||
| 	// 普通键名直接转换
 | ||||
| 	xkb_keysym_t sym = xkb_keysym_from_name(key_str, XKB_KEYSYM_NO_FLAGS); | ||||
| 
 | ||||
| 	if (isbindsym) { | ||||
| 		kc.type = KEY_TYPE_SYM; | ||||
| 	kc.keycode = 0; | ||||
| 		kc.keysym = sym; | ||||
| 		return kc; | ||||
| 	} | ||||
| 
 | ||||
| 	if (sym != XKB_KEY_NoSymbol) { | ||||
| 		// 尝试找到对应的多个 keycode
 | ||||
| 		int found_count = | ||||
| 			find_keycodes_for_keysym(config.keymap, sym, &kc.keycode); | ||||
| 		if (found_count > 0) { | ||||
| 			kc.type = KEY_TYPE_CODE; | ||||
| 			kc.keysym = sym; // 仍然保存 keysym 供参考
 | ||||
| 		} else { | ||||
| 			kc.type = KEY_TYPE_SYM; | ||||
| 			kc.keysym = sym; | ||||
| 			// keycode 字段保持为0
 | ||||
| 		} | ||||
| 	} else { | ||||
| 		// 无法解析的键名
 | ||||
| 		kc.type = KEY_TYPE_SYM; | ||||
| 		kc.keysym = XKB_KEY_NoSymbol; | ||||
| 		// keycode 字段保持为0
 | ||||
| 	} | ||||
| 
 | ||||
| 	return kc; | ||||
| } | ||||
| 
 | ||||
|  | @ -1650,7 +1755,8 @@ void parse_option(Config *config, char *key, char *value) { | |||
| 					trim_whitespace(mod_str); | ||||
| 					trim_whitespace(keysym_str); | ||||
| 					rule->globalkeybinding.mod = parse_mod(mod_str); | ||||
| 					rule->globalkeybinding.keysymcode = parse_key(keysym_str); | ||||
| 					rule->globalkeybinding.keysymcode = | ||||
| 						parse_key(keysym_str, false); | ||||
| 				} | ||||
| 			} | ||||
| 			token = strtok(NULL, ","); | ||||
|  | @ -1787,7 +1893,8 @@ void parse_option(Config *config, char *key, char *value) { | |||
| 
 | ||||
| 		config->exec_once_count++; | ||||
| 
 | ||||
| 	} else if (strncmp(key, "bind", 4) == 0) { | ||||
| 	} else if (strncmp(key, "bind", 4) == 0 || | ||||
| 			   strncmp(key, "bindsym", 7) == 0) { | ||||
| 		config->key_bindings = | ||||
| 			realloc(config->key_bindings, | ||||
| 					(config->key_bindings_count + 1) * sizeof(KeyBinding)); | ||||
|  | @ -1834,7 +1941,8 @@ void parse_option(Config *config, char *key, char *value) { | |||
| 		} | ||||
| 
 | ||||
| 		binding->mod = parse_mod(mod_str); | ||||
| 		binding->keysymcode = parse_key(keysym_str); | ||||
| 		binding->keysymcode = | ||||
| 			parse_key(keysym_str, strncmp(key, "bindsym", 7) == 0); | ||||
| 		binding->arg.v = NULL; | ||||
| 		binding->arg.v2 = NULL; | ||||
| 		binding->arg.v3 = NULL; | ||||
|  | @ -2432,6 +2540,9 @@ void free_config(void) { | |||
| 
 | ||||
| 	// 释放动画资源
 | ||||
| 	free_baked_points(); | ||||
| 
 | ||||
| 	// 清理解析按键用的keymap
 | ||||
| 	cleanup_config_keymap(); | ||||
| } | ||||
| 
 | ||||
| void override_config(void) { | ||||
|  | @ -2815,6 +2926,8 @@ void parse_config(void) { | |||
| 	config.cursor_theme = NULL; | ||||
| 	strcpy(config.keymode, "default"); | ||||
| 
 | ||||
| 	create_config_keymap(); | ||||
| 
 | ||||
| 	// 获取 MANGOCONFIG 环境变量
 | ||||
| 	const char *mangoconfig = getenv("MANGOCONFIG"); | ||||
| 
 | ||||
|  |  | |||
|  | @ -114,6 +114,15 @@ char xkb_rules_layout[256]; | |||
| char xkb_rules_variant[256]; | ||||
| char xkb_rules_options[256]; | ||||
| 
 | ||||
| /* keyboard */ | ||||
| static const struct xkb_rule_names xkb_fallback_rules = { | ||||
| 	.layout = "us", | ||||
| 	.variant = NULL, | ||||
| 	.model = NULL, | ||||
| 	.rules = NULL, | ||||
| 	.options = NULL, | ||||
| }; | ||||
| 
 | ||||
| struct xkb_rule_names xkb_rules = { | ||||
| 	/* can specify fields: rules, model, layout, variant, options */ | ||||
| 	/* example:
 | ||||
|  |  | |||
|  | @ -861,6 +861,13 @@ int switch_keyboard_layout(const Arg *arg) { | |||
| 
 | ||||
| 	// 4. 直接修改 rules.layout(保持原有逻辑)
 | ||||
| 	struct xkb_rule_names rules = xkb_rules; | ||||
| 	// 验证规则是否有效
 | ||||
| 	if (!check_keyboard_rules_validate(&rules)) { | ||||
| 		wlr_log(WLR_ERROR, | ||||
| 				"Keyboard rules validation failed, skipping layout reset"); | ||||
| 		rules = xkb_fallback_rules; | ||||
| 	} | ||||
| 
 | ||||
| 	char *layout_buf = (char *)rules.layout; // 假设这是可修改的
 | ||||
| 
 | ||||
| 	// 清空原有内容(安全方式)
 | ||||
|  |  | |||
							
								
								
									
										124
									
								
								src/mango.c
									
										
									
									
									
								
							
							
						
						
									
										124
									
								
								src/mango.c
									
										
									
									
									
								
							|  | @ -690,6 +690,7 @@ static void resize_tile_client(Client *grabc, bool isdrag, int offsetx, | |||
| 							   int offsety, unsigned int time); | ||||
| static void refresh_monitors_workspaces_status(Monitor *m); | ||||
| static void init_client_properties(Client *c); | ||||
| static bool check_keyboard_rules_validate(struct xkb_rule_names *rules); | ||||
| 
 | ||||
| #include "data/static_keymap.h" | ||||
| #include "dispatch/bind_declare.h" | ||||
|  | @ -2365,9 +2366,18 @@ KeyboardGroup *createkeyboardgroup(void) { | |||
| 	group->wlr_group = wlr_keyboard_group_create(); | ||||
| 	group->wlr_group->data = group; | ||||
| 
 | ||||
| 	// 4. 直接修改 rules.layout(保持原有逻辑)
 | ||||
| 	struct xkb_rule_names rules = xkb_rules; | ||||
| 	// 验证规则是否有效
 | ||||
| 	if (!check_keyboard_rules_validate(&rules)) { | ||||
| 		wlr_log(WLR_ERROR, | ||||
| 				"Keyboard rules validation failed, skipping layout reset"); | ||||
| 		rules = xkb_fallback_rules; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Prepare an XKB keymap and assign it to the keyboard group. */ | ||||
| 	context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); | ||||
| 	if (!(keymap = xkb_keymap_new_from_names(context, &xkb_rules, | ||||
| 	if (!(keymap = xkb_keymap_new_from_names(context, &rules, | ||||
| 											 XKB_KEYMAP_COMPILE_NO_FLAGS))) | ||||
| 		die("failed to compile keymap"); | ||||
| 
 | ||||
|  | @ -3226,7 +3236,9 @@ keybinding(unsigned int mods, xkb_keysym_t sym, unsigned int keycode) { | |||
| 			  normalize_keysym(sym) == | ||||
| 				  normalize_keysym(k->keysymcode.keysym)) || | ||||
| 			 (k->keysymcode.type == KEY_TYPE_CODE && | ||||
| 			  keycode == k->keysymcode.keycode)) && | ||||
| 			  (keycode == k->keysymcode.keycode.keycode1 || | ||||
| 			   keycode == k->keysymcode.keycode.keycode2 || | ||||
| 			   keycode == k->keysymcode.keycode.keycode3))) && | ||||
| 			k->func) { | ||||
| 
 | ||||
| 			isbreak = k->func(&k->arg); | ||||
|  | @ -3258,14 +3270,18 @@ bool keypressglobal(struct wlr_surface *last_surface, | |||
| 
 | ||||
| 		if (!r->globalkeybinding.mod || | ||||
| 			(!r->globalkeybinding.keysymcode.keysym && | ||||
| 			 !r->globalkeybinding.keysymcode.keycode)) | ||||
| 			 !r->globalkeybinding.keysymcode.keycode.keycode1 && | ||||
| 			 !r->globalkeybinding.keysymcode.keycode.keycode2 && | ||||
| 			 !r->globalkeybinding.keysymcode.keycode.keycode3)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		/* match key only (case insensitive) ignoring mods */ | ||||
| 		if (((r->globalkeybinding.keysymcode.type == KEY_TYPE_SYM && | ||||
| 			  r->globalkeybinding.keysymcode.keysym == keysym) || | ||||
| 			 (r->globalkeybinding.keysymcode.type == KEY_TYPE_CODE && | ||||
| 			  r->globalkeybinding.keysymcode.keycode == keycode)) && | ||||
| 			  (r->globalkeybinding.keysymcode.keycode.keycode1 == keycode || | ||||
| 			   r->globalkeybinding.keysymcode.keycode.keycode2 == keycode || | ||||
| 			   r->globalkeybinding.keysymcode.keycode.keycode3 == keycode))) && | ||||
| 			r->globalkeybinding.mod == mods) { | ||||
| 			wl_list_for_each(c, &clients, link) { | ||||
| 				if (c && c != lastc) { | ||||
|  | @ -4526,6 +4542,46 @@ void setgaps(int oh, int ov, int ih, int iv) { | |||
| 	arrange(selmon, false); | ||||
| } | ||||
| 
 | ||||
| // 验证键盘规则是否有效
 | ||||
| bool check_keyboard_rules_validate(struct xkb_rule_names *rules) { | ||||
| 	if (!rules) { | ||||
| 		wlr_log(WLR_ERROR, "Keyboard rules are NULL"); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); | ||||
| 	if (!context) { | ||||
| 		wlr_log(WLR_ERROR, "Failed to create XKB context for validation"); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	bool valid = false; | ||||
| 	struct xkb_keymap *test_keymap = | ||||
| 		xkb_keymap_new_from_names(context, rules, XKB_KEYMAP_COMPILE_NO_FLAGS); | ||||
| 
 | ||||
| 	if (test_keymap) { | ||||
| 		// 检查keymap是否至少有一个布局
 | ||||
| 		if (xkb_keymap_num_layouts(test_keymap) > 0) { | ||||
| 			valid = true; | ||||
| 		} else { | ||||
| 			wlr_log(WLR_ERROR, "Keymap has no layouts"); | ||||
| 		} | ||||
| 		xkb_keymap_unref(test_keymap); | ||||
| 	} else { | ||||
| 		wlr_log(WLR_ERROR, | ||||
| 				"Invalid keyboard rules: rules=%s, model=%s, layout=%s, " | ||||
| 				"variant=%s, options=%s", | ||||
| 				rules->rules ? rules->rules : "NULL", | ||||
| 				rules->model ? rules->model : "NULL", | ||||
| 				rules->layout ? rules->layout : "NULL", | ||||
| 				rules->variant ? rules->variant : "NULL", | ||||
| 				rules->options ? rules->options : "NULL"); | ||||
| 	} | ||||
| 
 | ||||
| 	xkb_context_unref(context); | ||||
| 	return valid; | ||||
| } | ||||
| 
 | ||||
| void reset_keyboard_layout(void) { | ||||
| 	if (!kb_group || !kb_group->wlr_group || !seat) { | ||||
| 		wlr_log(WLR_ERROR, "Invalid keyboard group or seat"); | ||||
|  | @ -4547,6 +4603,16 @@ void reset_keyboard_layout(void) { | |||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	// Keep the same rules but just reapply them
 | ||||
| 	struct xkb_rule_names rules = xkb_rules; | ||||
| 
 | ||||
| 	// 验证规则是否有效
 | ||||
| 	if (!check_keyboard_rules_validate(&rules)) { | ||||
| 		wlr_log(WLR_ERROR, | ||||
| 				"Keyboard rules validation failed, skipping layout reset"); | ||||
| 		rules = xkb_fallback_rules; | ||||
| 	} | ||||
| 
 | ||||
| 	// Create context
 | ||||
| 	struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); | ||||
| 	if (!context) { | ||||
|  | @ -4554,35 +4620,34 @@ void reset_keyboard_layout(void) { | |||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	// Get layout abbreviations
 | ||||
| 	const 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; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Keep the same rules but just reapply them
 | ||||
| 	struct xkb_rule_names rules = xkb_rules; | ||||
| 
 | ||||
| 	// Create new keymap with current rules
 | ||||
| 	// 现在安全地创建真正的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; | ||||
| 		// 理论上这里不应该失败,因为前面已经验证过了
 | ||||
| 		wlr_log(WLR_ERROR, | ||||
| 				"Unexpected failure to create keymap after validation"); | ||||
| 		goto cleanup_context; | ||||
| 	} | ||||
| 
 | ||||
| 	// Apply the same keymap (this will reset the layout state)
 | ||||
| 	// 验证新keymap是否有布局
 | ||||
| 	const int 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
 | ||||
| 	unsigned int depressed = keyboard->modifiers.depressed; | ||||
| 	unsigned int latched = keyboard->modifiers.latched; | ||||
| 	unsigned int locked = keyboard->modifiers.locked; | ||||
|  | @ -4599,9 +4664,6 @@ void reset_keyboard_layout(void) { | |||
| 	// Cleanup
 | ||||
| 	xkb_keymap_unref(new_keymap); | ||||
| 
 | ||||
| cleanup_layouts: | ||||
| 	free(layout_ids); | ||||
| 
 | ||||
| cleanup_context: | ||||
| 	xkb_context_unref(context); | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 DreamMaoMao
						DreamMaoMao