diff --git a/include/menu/menu.h b/include/menu/menu.h index 736e32b1..e0e39cf5 100644 --- a/include/menu/menu.h +++ b/include/menu/menu.h @@ -23,7 +23,7 @@ struct menuitem { char *text; char *icon_name; const char *arrow; - char accelerator; + uint32_t accelerator; struct menu *parent; struct menu *submenu; bool selectable; @@ -78,7 +78,7 @@ void menu_item_select_previous(void); * Return: a boolean value that represents whether the newly selected item * needs to be executed. */ -bool menu_item_select_by_accelerator(char accelerator); +bool menu_item_select_by_accelerator(uint32_t accelerator); void menu_submenu_enter(void); void menu_submenu_leave(void); diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 0c5bd802..16b51947 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -419,20 +419,6 @@ handle_change_vt_key(struct keyboard *keyboard, return false; } -static char -keysym_to_char(uint32_t keysym) -{ - if (keysym >= 0x0020 && keysym <= 0x00FF) { - return (char)keysym; - } - - if (keysym >= XKB_KEY_KP_0 && keysym <= XKB_KEY_KP_9) { - return (char)('0' + (keysym - XKB_KEY_KP_0)); - } - - return '\0'; -} - static void handle_menu_keys(struct keysyms *syms) { @@ -454,15 +440,17 @@ handle_menu_keys(struct keysyms *syms) break; case XKB_KEY_Return: case XKB_KEY_KP_Enter: - menu_call_selected_actions(); + if (!menu_call_selected_actions()) { + menu_submenu_enter(); + }; break; case XKB_KEY_Escape: menu_close_root(); cursor_update_focus(); break; default: { - char accelerator = keysym_to_char(syms->syms[i]); - if (accelerator == '\0') { + uint32_t accelerator = xkb_keysym_to_utf32(syms->syms[i]); + if (accelerator == 0) { continue; } if (menu_item_select_by_accelerator(accelerator)) { diff --git a/src/menu/menu.c b/src/menu/menu.c index 013ee7af..f56b208f 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -8,7 +8,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -131,7 +133,7 @@ validate(void) } static struct menuitem * -item_create(struct menu *menu, const char *text, const char *icon_name, bool show_arrow) +item_create(struct menu *menu, char *text, const char *icon_name, bool show_arrow) { assert(menu); assert(text); @@ -145,26 +147,34 @@ item_create(struct menu *menu, const char *text, const char *icon_name, bool sho menuitem->arrow = show_arrow ? "›" : NULL; const char *it = text; - /* Skip emojis and whitespace */ - while (*it != '\0') { - unsigned char c = (unsigned char)*it; - - if (isspace(c) || c > 127) { - it++; - } else { - break; - } - } - - menuitem->accelerator = tolower(*it); + uint32_t accelerator = 0; while (*it != '\0') { if (*it == '_') { - menuitem->accelerator = tolower(*(it + 1)); + char32_t codepoint = 0; + mbstate_t state = {0}; + size_t bytes = mbrtoc32(&codepoint, it + 1, MB_CUR_MAX, &state); + if (bytes > 0 && bytes <= 4) { + accelerator = (uint32_t)towlower((wint_t)codepoint); + } + break; } it++; } + /* Fallback to the first character of the label */ + if (accelerator == 0 && text[0] != '\0') { + char32_t codepoint = 0; + mbstate_t state = {0}; + + size_t bytes = mbrtoc32(&codepoint, text, MB_CUR_MAX, &state); + if (bytes > 0 && bytes <= 4) { + accelerator = (uint32_t)towlower((wint_t)codepoint); + } + } + + menuitem->accelerator = accelerator; + #if HAVE_LIBSFDO if (rc.menu_show_icons && !string_null_or_empty(icon_name)) { menuitem->icon_name = xstrdup(icon_name); @@ -1483,7 +1493,7 @@ menu_item_select_previous(void) } bool -menu_item_select_by_accelerator(char accelerator) +menu_item_select_by_accelerator(uint32_t accelerator) { struct menu *menu = get_selection_leaf(); if (!menu || wl_list_empty(&menu->menuitems)) {