[PATCH 0/4] MR10138: Draft: imm32: Only load IME with IME HKLs.
@mzent @bsjeon I'm thinking about doing something like that, bypassing IME entirely as we should if HKL isn't an IME one, instead of checking whether host IME needs to be fed input in ImeProcessKey, it's a bit untested though. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10138
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/winemac.drv/keyboard.c | 54 +++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/dlls/winemac.drv/keyboard.c b/dlls/winemac.drv/keyboard.c index 7c49be7849f..281dd555246 100644 --- a/dlls/winemac.drv/keyboard.c +++ b/dlls/winemac.drv/keyboard.c @@ -313,7 +313,9 @@ static struct list layout_list = LIST_INIT( layout_list ); struct layout { struct list entry; - HKL hkl; + LANGID lang; + /* "Layout Id", used by NtUserGetKeyboardLayoutName / LoadKeyboardLayoutW */ + WORD layout_id; TISInputSourceRef input_source; BOOL enabled; /* is the input source enabled - ie displayed in the input source selector UI */ }; @@ -412,27 +414,10 @@ static DWORD get_lcid(CFStringRef lang) return locale->inotneutral ? entry->id : locale->idefaultlanguage; } -static HKL get_hkl(CFStringRef lang, CFStringRef type) +static HKL get_layout_hkl(struct layout *layout, LCID locale) { - ULONG_PTR lcid = get_lcid(lang); - struct layout *layout; - - /* Look for the last occurrence of this lcid in the list and if - present use that value + 0x10000 */ - LIST_FOR_EACH_ENTRY_REV(layout, &layout_list, struct layout, entry) - { - ULONG_PTR hkl = HandleToUlong(layout->hkl); - - if (LOWORD(hkl) == lcid) - { - lcid = (hkl & ~0xe0000000) + 0x10000; - break; - } - } - - if (!CFEqual(type, kTISTypeKeyboardLayout)) lcid |= 0xe0000000; - - return (HKL)lcid; + if (!layout->layout_id) return UlongToHandle(MAKELONG(locale, layout->lang)); + return UlongToHandle(MAKELONG(locale, layout->layout_id)); } /****************************************************************** @@ -468,6 +453,7 @@ static struct layout *get_layout_from_source(TISInputSourceRef input) */ static void update_layout_list(void) { + LCID locale = LOWORD(NtUserGetKeyboardLayout(0)); CFArrayRef sources; struct layout *layout; int i; @@ -486,18 +472,26 @@ static void update_layout_list(void) layout = get_layout_from_source(input); if (!layout) { + static WORD next_layout_id = 1; + CFStringRef type = CFDictionaryGetValue(dict, macdrv_input_source_type_key); - CFStringRef lang = CFDictionaryGetValue(dict, macdrv_input_source_lang_key); + LANGID lang = get_lcid(CFDictionaryGetValue(dict, macdrv_input_source_lang_key)); + UINT index = 0; + + LIST_FOR_EACH_ENTRY_REV(layout, &layout_list, struct layout, entry) + if (layout->lang == lang) index++; - layout = malloc(sizeof(*layout)); + layout = calloc(1, sizeof(*layout)); layout->input_source = (TISInputSourceRef)CFRetain(input); - layout->hkl = get_hkl(lang, type); + layout->lang = lang; + if (!CFEqual(type, kTISTypeKeyboardLayout)) layout->layout_id = 0xe000 | next_layout_id++; + else if (index) layout->layout_id = 0xf000 | next_layout_id++; list_add_tail(&layout_list, &layout->entry); - TRACE("adding new layout %p\n", layout->hkl); + TRACE("adding new layout %p\n", get_layout_hkl(layout, locale)); } else - TRACE("enabling already existing layout %p\n", layout->hkl); + TRACE("enabling already existing layout %p\n", get_layout_hkl(layout, locale)); layout->enabled = TRUE; } @@ -512,6 +506,7 @@ static void update_layout_list(void) */ HKL macdrv_get_hkl_from_source(TISInputSourceRef input) { + LCID locale = LOWORD(NtUserGetKeyboardLayout(0)); struct layout *layout; HKL ret = 0; @@ -519,7 +514,7 @@ HKL macdrv_get_hkl_from_source(TISInputSourceRef input) update_layout_list(); layout = get_layout_from_source(input); - if (layout) ret = layout->hkl; + if (layout) ret = get_layout_hkl(layout, locale); pthread_mutex_unlock(&layout_list_mutex); @@ -1165,7 +1160,7 @@ BOOL macdrv_ActivateKeyboardLayout(HKL hkl, UINT flags) LIST_FOR_EACH_ENTRY(layout, &layout_list, struct layout, entry) { - if (layout->hkl == hkl) + if (HIWORD(hkl) == layout->layout_id ? layout->layout_id : layout->lang) { if (macdrv_select_input_source(layout->input_source)) { @@ -1302,6 +1297,7 @@ INT macdrv_GetKeyNameText(LONG lparam, LPWSTR buffer, INT size) */ UINT macdrv_GetKeyboardLayoutList(INT size, HKL *list) { + LCID locale = LOWORD(NtUserGetKeyboardLayout(0)); int count = 0; struct layout *layout; @@ -1317,7 +1313,7 @@ UINT macdrv_GetKeyboardLayoutList(INT size, HKL *list) if (list) { if (count >= size) break; - list[count] = layout->hkl; + list[count] = get_layout_hkl(layout, locale); TRACE("\t%d: %p\n", count, list[count]); } count++; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10138
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/winewayland.drv/wayland_keyboard.c | 16 +++++++++++++++- dlls/winewayland.drv/wayland_text_input.c | 9 +++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/dlls/winewayland.drv/wayland_keyboard.c b/dlls/winewayland.drv/wayland_keyboard.c index 648b7f9cc2f..287987780ee 100644 --- a/dlls/winewayland.drv/wayland_keyboard.c +++ b/dlls/winewayland.drv/wayland_keyboard.c @@ -38,6 +38,12 @@ WINE_DEFAULT_DEBUG_CHANNEL(keyboard); WINE_DECLARE_DEBUG_CHANNEL(key); +static HKL get_ime_hkl(void) +{ + LCID locale = LOWORD(NtUserGetKeyboardLayout(0)); + return ULongToHandle(MAKELONG(locale, 0xe001)); +} + struct layout { struct list entry; @@ -573,6 +579,7 @@ static void add_xkb_layout(const char *xkb_layout, struct xkb_keymap *xkb_keymap static void set_current_xkb_group(xkb_layout_index_t xkb_group) { + struct wayland_text_input *text_input = &process_wayland.text_input; struct wayland_keyboard *keyboard = &process_wayland.keyboard; LCID locale = LOWORD(NtUserGetKeyboardLayout(0)); struct layout *layout; @@ -595,9 +602,14 @@ static void set_current_xkb_group(xkb_layout_index_t xkb_group) if (hkl == keyboard_hkl) return; keyboard_hkl = hkl; + pthread_mutex_lock(&text_input->mutex); + if (text_input->focused_hwnd == keyboard->focused_hwnd) + hkl = get_ime_hkl(); + pthread_mutex_unlock(&text_input->mutex); + TRACE("Changing keyboard layout to %p\n", hkl); NtUserPostMessage(keyboard->focused_hwnd, WM_INPUTLANGCHANGEREQUEST, 0 /*FIXME*/, - (LPARAM)keyboard_hkl); + (LPARAM)hkl); } static BOOL find_xkb_layout_variant(const char *name, const char **layout, const char **variant) @@ -962,6 +974,8 @@ const KBDTABLES *WAYLAND_KbdLayerDescriptor(HKL hkl) TRACE("hkl=%p\n", hkl); + if (hkl == get_ime_hkl()) hkl = keyboard_hkl; + pthread_mutex_lock(&xkb_layouts_mutex); LIST_FOR_EACH_ENTRY(layout, &xkb_layouts, struct layout, entry) diff --git a/dlls/winewayland.drv/wayland_text_input.c b/dlls/winewayland.drv/wayland_text_input.c index 52cfef27e50..8b8f7316f9f 100644 --- a/dlls/winewayland.drv/wayland_text_input.c +++ b/dlls/winewayland.drv/wayland_text_input.c @@ -33,6 +33,12 @@ WINE_DEFAULT_DEBUG_CHANNEL(imm); +static HKL get_ime_hkl(void) +{ + LCID locale = LOWORD(NtUserGetKeyboardLayout(0)); + return ULongToHandle(MAKELONG(locale, 0xe001)); +} + static void post_ime_update(HWND hwnd, UINT cursor_pos, WCHAR *comp_str, WCHAR *result_str) { /* Windows uses an empty string to clear the composition string. */ @@ -97,6 +103,9 @@ static void text_input_enter(void *data, struct zwp_text_input_v3 *zwp_text_inpu zwp_text_input_v3_set_cursor_rectangle(text_input->zwp_text_input_v3, 0, 0, 0, 0); zwp_text_input_v3_commit(text_input->zwp_text_input_v3); pthread_mutex_unlock(&text_input->mutex); + + NtUserPostMessage(hwnd, WM_INPUTLANGCHANGEREQUEST, 0 /*FIXME*/, + (LPARAM)get_ime_hkl()); } static void text_input_leave(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10138
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/winex11.drv/keyboard.c | 2 +- dlls/winex11.drv/xim.c | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 25836fe835d..e2281bef017 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1823,7 +1823,7 @@ void X11DRV_InitKeyboard( Display *display ) */ BOOL X11DRV_ActivateKeyboardLayout(HKL hkl, UINT flags) { - FIXME("%p, %04x: semi-stub!\n", hkl, flags); + WARN("%p, %04x: semi-stub!\n", hkl, flags); if (flags & KLF_SETFORPROCESS) { diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 05ef7863a8c..b7321afb4c7 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -70,6 +70,12 @@ static const char *debugstr_xim_style( XIMStyle style ) return wine_dbg_sprintf( "%s", buffer ); } +static HKL get_ime_hkl(void) +{ + LCID locale = LOWORD( NtUserGetKeyboardLayout( 0 ) ); + return ULongToHandle( MAKELONG( locale, 0xe001 ) ); +} + BOOL xim_in_compose_mode(void) { return !!ime_comp_buf; @@ -113,6 +119,7 @@ void xim_set_result_string( HWND hwnd, const char *str, UINT count ) len = ntdll_umbstowcs( str, count, output, count ); output[len] = 0; + NtUserActivateKeyboardLayout( get_ime_hkl(), 0 ); post_ime_update( hwnd, 0, NULL, output ); free( output ); @@ -129,6 +136,7 @@ static BOOL xic_preedit_state_notify( XIC xic, XPointer user, XPointer arg ) switch (state) { case XIMPreeditEnable: + NtUserActivateKeyboardLayout( get_ime_hkl(), 0 ); NtUserPostMessage( hwnd, WM_WINE_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, TRUE ); break; case XIMPreeditDisable: @@ -148,6 +156,7 @@ static int xic_preedit_start( XIC xic, XPointer user, XPointer arg ) if ((ime_comp_buf = realloc( ime_comp_buf, sizeof(WCHAR) ))) *ime_comp_buf = 0; else ERR( "Failed to allocate preedit buffer\n" ); + NtUserActivateKeyboardLayout( get_ime_hkl(), 0 ); NtUserPostMessage( hwnd, WM_WINE_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, TRUE ); post_ime_update( hwnd, 0, ime_comp_buf, NULL ); @@ -163,6 +172,7 @@ static int xic_preedit_done( XIC xic, XPointer user, XPointer arg ) free( ime_comp_buf ); ime_comp_buf = NULL; + NtUserActivateKeyboardLayout( get_ime_hkl(), 0 ); post_ime_update( hwnd, 0, NULL, NULL ); NtUserPostMessage( hwnd, WM_WINE_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, FALSE ); @@ -230,6 +240,7 @@ static int xic_preedit_draw( XIC xic, XPointer user, XPointer arg ) if (text && str != text->string.multi_byte) free( str ); + NtUserActivateKeyboardLayout( get_ime_hkl(), 0 ); ime_comp_cursor_pos = get_comp_cursor_pos( params ); post_ime_update( hwnd, ime_comp_cursor_pos, ime_comp_buf, NULL ); @@ -282,6 +293,7 @@ static int xic_preedit_caret( XIC xic, XPointer user, XPointer arg ) if (LOWORD( ime_comp_cursor_pos ) == HIWORD( ime_comp_cursor_pos ) && LOWORD( ime_comp_cursor_pos ) != pos) { + NtUserActivateKeyboardLayout( get_ime_hkl(), 0 ); ime_comp_cursor_pos = MAKELONG( pos, pos ); post_ime_update( hwnd, ime_comp_cursor_pos, ime_comp_buf, NULL ); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10138
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/imm32/imm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 0b2814da2cb..e2f70b8ed42 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -472,6 +472,8 @@ BOOL WINAPI ImmLoadIME( HKL hkl ) TRACE( "hkl %p\n", hkl ); + if ((HIWORD(hkl) & 0xe000) != 0xe000) return FALSE; + EnterCriticalSection( &ime_cs ); if ((ime = find_ime_from_hkl( hkl )) || !(ime = calloc( 1, sizeof(*ime) ))) { @@ -659,9 +661,7 @@ static struct ime *imc_select_ime( struct imc *imc ) imc_release_ime( imc, ime ); } - if (!(imc->ime = ime_acquire( hkl ))) - WARN( "Failed to acquire IME for HKL %p\n", hkl ); - else + if ((imc->ime = ime_acquire( hkl ))) { INPUTCONTEXT *ctx; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10138
Would it make sense to skip calling ImmProcessKey in win32u's process_keyboard_message? I think this could help reduce some of the UNIX-PE transitions in non-IME hkl. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10138#note_129970
LOWORD(hkl) is currently fixed to the system locale. If the keyboard layout language can be determined, that value should be used to replace LOWORD(hkl). In addition, the part in NtUserActivateKeyboardLayout that compares LOWORD(hkl) with the locale should also be updated. I believe it should at least be allowed when the window procedure is Unicode. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10138#note_129982
On Tue Mar 3 12:12:40 2026 +0000, Byeongsik Jeon wrote:
LOWORD(hkl) is currently fixed to the system locale. If the keyboard layout language can be determined, that value should be used to replace LOWORD(hkl). In addition, the part in NtUserActivateKeyboardLayout that compares LOWORD(hkl) with the locale should also be updated. I believe it should at least be allowed when the window procedure is Unicode. The low word indicates the UI / user locale, and on Windows it can be changed separately from the keyboard layout itself. In Wine we do not support switching user locale, so if we ever want to implement that low word correctly, we would need to add support for switching this? Until then, every keyboard layout needs to use the current user locale.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10138#note_131107
participants (3)
-
Byeongsik Jeon (@bsjeon) -
Rémi Bernon -
Rémi Bernon (@rbernon)