From c07cc8be5ae7838d4861948c711407b9083abea6 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Tue, 21 Oct 2025 18:04:10 +0800 Subject: [PATCH 1/6] fix: only set maxmize when ismaxmizescreen is true --- src/mango.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mango.c b/src/mango.c index 346cb0a..edf6137 100644 --- a/src/mango.c +++ b/src/mango.c @@ -4389,7 +4389,7 @@ void setmaxmizescreen(Client *c, int maxmizescreen) { if (!c->force_maximize && !c->ismaxmizescreen) { client_set_maximized(c, false); - } else { + } else if (!c->force_maximize && c->ismaxmizescreen) { client_set_maximized(c, true); } From 8499ea209ef7e51ba52e4cb1074cfb559c9337b8 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Tue, 21 Oct 2025 22:59:14 +0800 Subject: [PATCH 2/6] bump version to 0.10.3 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index c5e58e8..43c337e 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('mango', ['c', 'cpp'], - version : '0.10.2', + version : '0.10.3', ) subdir('protocols') From b08939bb69b9db1a40853af305b3af3b879431f2 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 22 Oct 2025 09:17:52 +0800 Subject: [PATCH 3/6] feat: auto convert keysym to keycode bind --- src/config/parse_config.h | 133 ++++++++++++++++++++++++++++++++++--- src/config/preset.h | 9 +++ src/dispatch/bind_define.h | 7 ++ src/mango.c | 124 +++++++++++++++++++++++++--------- 4 files changed, 232 insertions(+), 41 deletions(-) diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 945fc16..9fec19f 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -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; @@ -308,6 +314,8 @@ typedef struct { char keymode[28]; + struct xkb_context *ctx; + struct xkb_keymap *keymap; } Config; typedef int (*FuncType)(const Arg *); @@ -596,23 +604,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); - kc.type = KEY_TYPE_SYM; - kc.keycode = 0; - kc.keysym = sym; + + if (isbindsym) { + kc.type = KEY_TYPE_SYM; + 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; } @@ -1584,7 +1689,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, ","); @@ -1721,7 +1827,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)); @@ -1768,7 +1875,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; @@ -2366,6 +2474,9 @@ void free_config(void) { // 释放动画资源 free_baked_points(); + + // 清理解析按键用的keymap + cleanup_config_keymap(); } void override_config(void) { @@ -2711,6 +2822,8 @@ void parse_config(void) { config.cursor_theme = NULL; strcpy(config.keymode, "default"); + create_config_keymap(); + // 获取 MANGOCONFIG 环境变量 const char *mangoconfig = getenv("MANGOCONFIG"); diff --git a/src/config/preset.h b/src/config/preset.h index d546820..77d86ce 100644 --- a/src/config/preset.h +++ b/src/config/preset.h @@ -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: diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 4f93670..3a91ab9 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -864,6 +864,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; // 假设这是可修改的 // 清空原有内容(安全方式) diff --git a/src/mango.c b/src/mango.c index edf6137..a31dba3 100644 --- a/src/mango.c +++ b/src/mango.c @@ -695,6 +695,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" @@ -2317,9 +2318,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"); @@ -3171,7 +3181,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); @@ -3203,14 +3215,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) { @@ -4464,6 +4480,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"); @@ -4485,6 +4541,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) { @@ -4492,35 +4558,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; @@ -4537,9 +4602,6 @@ void reset_keyboard_layout(void) { // Cleanup xkb_keymap_unref(new_keymap); -cleanup_layouts: - free(layout_ids); - cleanup_context: xkb_context_unref(context); } From 86cedaadca18636c992120aa64399c04aefe0cf8 Mon Sep 17 00:00:00 2001 From: werapi Date: Tue, 21 Oct 2025 13:36:34 +0200 Subject: [PATCH 4/6] feat: add support for DRM lease protocol This implementation is based on a patch authored by Micah Gorrell (minego) for dwl. Original repository: https://github.com/minego/dwl Original commit: 94c448a842fc3a9fae454646e149ac63b69e7ef8 --- src/mango.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/mango.c b/src/mango.c index a31dba3..feadef1 100644 --- a/src/mango.c +++ b/src/mango.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -577,6 +578,7 @@ static void quitsignal(int signo); static void powermgrsetmode(struct wl_listener *listener, void *data); static void rendermon(struct wl_listener *listener, void *data); static void requestdecorationmode(struct wl_listener *listener, void *data); +static void requestdrmlease(struct wl_listener *listener, void *data); static void requeststartdrag(struct wl_listener *listener, void *data); static void resize(Client *c, struct wlr_box geo, int interact); static void run(char *startup_cmd); @@ -733,6 +735,7 @@ static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; static struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr; static struct wlr_output_power_manager_v1 *power_mgr; static struct wlr_pointer_gestures_v1 *pointer_gestures; +static struct wlr_drm_lease_v1_manager *drm_lease_manager; static struct wlr_cursor *cursor; static struct wlr_xcursor_manager *cursor_mgr; @@ -841,6 +844,7 @@ static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape}; static struct wl_listener request_start_drag = {.notify = requeststartdrag}; static struct wl_listener start_drag = {.notify = startdrag}; static struct wl_listener new_session_lock = {.notify = locksession}; +static struct wl_listener drm_lease_request = {.notify = requestdrmlease}; #ifdef XWAYLAND static void activatex11(struct wl_listener *listener, void *data); @@ -1945,6 +1949,7 @@ void cleanuplisteners(void) { wl_list_remove(&start_drag.link); wl_list_remove(&new_session_lock.link); wl_list_remove(&new_foreign_toplevel_capture_request.link); + wl_list_remove(&drm_lease_request.link); #ifdef XWAYLAND wl_list_remove(&new_xwayland_surface.link); wl_list_remove(&xwayland_ready.link); @@ -2480,6 +2485,14 @@ void createmon(struct wl_listener *listener, void *data) { if (!wlr_output_init_render(wlr_output, alloc, drw)) return; + if (wlr_output->non_desktop) { + if (drm_lease_manager) { + wlr_drm_lease_v1_manager_offer_output(drm_lease_manager, + wlr_output); + } + return; + } + m = wlr_output->data = ecalloc(1, sizeof(*m)); m->wlr_output = wlr_output; @@ -4063,6 +4076,16 @@ void requestdecorationmode(struct wl_listener *listener, void *data) { } } +static void requestdrmlease(struct wl_listener *listener, void *data) { + struct wlr_drm_lease_request_v1 *req = data; + struct wlr_drm_lease_v1 *lease = wlr_drm_lease_request_v1_grant(req); + + if (!lease) { + wlr_log(WLR_ERROR, "Failed to grant lease request"); + wlr_drm_lease_request_v1_reject(req); + } +} + void requeststartdrag(struct wl_listener *listener, void *data) { struct wlr_seat_request_start_drag_event *event = data; @@ -4969,6 +4992,14 @@ void setup(void) { dwl_input_method_relay = dwl_im_relay_create(); + drm_lease_manager = wlr_drm_lease_v1_manager_create(dpy, backend); + if (drm_lease_manager) { + wl_signal_add(&drm_lease_manager->events.request, &drm_lease_request); + } else { + wlr_log(WLR_ERROR, "Failed to create wlr_drm_lease_device_v1; VR will " + "not be available"); + } + wl_global_create(dpy, &zdwl_ipc_manager_v2_interface, 2, NULL, dwl_ipc_manager_bind); From c69b736d0e568ff5a9f84b499410edacdfdf1737 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 22 Oct 2025 13:24:36 +0800 Subject: [PATCH 5/6] opt: optimize keymap set --- src/config/preset.h | 4 ++++ src/dispatch/bind_define.h | 2 +- src/mango.c | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/config/preset.h b/src/config/preset.h index 77d86ce..a6d263f 100644 --- a/src/config/preset.h +++ b/src/config/preset.h @@ -123,6 +123,10 @@ static const struct xkb_rule_names xkb_fallback_rules = { .options = NULL, }; +static const struct xkb_rule_names xkb_default_rules = { + .options = NULL, +}; + struct xkb_rule_names xkb_rules = { /* can specify fields: rules, model, layout, variant, options */ /* example: diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 3a91ab9..a39dd3b 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -868,7 +868,7 @@ int switch_keyboard_layout(const Arg *arg) { if (!check_keyboard_rules_validate(&rules)) { wlr_log(WLR_ERROR, "Keyboard rules validation failed, skipping layout reset"); - rules = xkb_fallback_rules; + rules = xkb_default_rules; } char *layout_buf = (char *)rules.layout; // 假设这是可修改的 diff --git a/src/mango.c b/src/mango.c index feadef1..031da32 100644 --- a/src/mango.c +++ b/src/mango.c @@ -2329,7 +2329,7 @@ KeyboardGroup *createkeyboardgroup(void) { if (!check_keyboard_rules_validate(&rules)) { wlr_log(WLR_ERROR, "Keyboard rules validation failed, skipping layout reset"); - rules = xkb_fallback_rules; + rules = xkb_default_rules; } /* Prepare an XKB keymap and assign it to the keyboard group. */ @@ -4571,7 +4571,7 @@ void reset_keyboard_layout(void) { if (!check_keyboard_rules_validate(&rules)) { wlr_log(WLR_ERROR, "Keyboard rules validation failed, skipping layout reset"); - rules = xkb_fallback_rules; + rules = xkb_default_rules; } // Create context From 8708cb347f42a6a13074d0a11d7aa27640fc27ce Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 22 Oct 2025 14:04:39 +0800 Subject: [PATCH 6/6] opt: default numlock to 0 --- config.conf | 2 +- src/config/preset.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config.conf b/config.conf index 1a58617..577942a 100644 --- a/config.conf +++ b/config.conf @@ -65,7 +65,7 @@ drag_tile_to_tile=1 # keyboard repeat_rate=25 repeat_delay=600 -numlockon=1 +numlockon=0 xkb_rules_layout=us # Trackpad diff --git a/src/config/preset.h b/src/config/preset.h index a6d263f..ab62477 100644 --- a/src/config/preset.h +++ b/src/config/preset.h @@ -40,7 +40,7 @@ int center_master_overspread = 0; // 中心master时是否铺满 int center_when_single_stack = 1; // 单个stack时是否居中 /* logging */ int log_level = WLR_ERROR; -unsigned int numlockon = 1; // 是否打开右边小键盘 +unsigned int numlockon = 0; // 是否打开右边小键盘 unsigned int capslock = 0; // 是否启用快捷键 unsigned int ov_tab_mode = 0; // alt tab切换模式