From e9f80a5e3297d16c1d0b9b86fea77e74dca04b66 Mon Sep 17 00:00:00 2001 From: maxjivi05 Date: Fri, 6 Nov 2026 10:08:50 -0500 Subject: [PATCH] wine: winex11 reports the focused window's input context to DroidDeck Programs on X11 (gamescope sessions) never told DroidDeck when a text field took focus: winex11 had no EnableIMEContext. The pending patch writes the focused window's state to agent/wine-ime.json under BL_LAUNCH_DIR, the way winewayland already enables text input. Proven on World of Warcraft: Forever, whose chat box and search field associate an input context on focus and remove it on Esc or Enter. --- .github/workflows/build-aarch64.yml | 2 + banner-proton/apply-deferred-game-patches.sh | 1 + .../winex11-report-ime-context-focus.patch | 92 +++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 patches/wine-hotfixes/pending/winex11-report-ime-context-focus.patch diff --git a/.github/workflows/build-aarch64.yml b/.github/workflows/build-aarch64.yml index dc5f6607..189b82ab 100644 --- a/.github/workflows/build-aarch64.yml +++ b/.github/workflows/build-aarch64.yml @@ -271,6 +271,8 @@ jobs: has tools/winebuild/spec32.c "fixup_arm64x_import_thunks( data, st.st_size )" has tools/winebuild/spec32.c "pe_cover_rva( &pe, func, sizeof(hotpatch) + 6" has dlls/ntdll/signal_arm64ec.c "ldr x17, .Lhotpatch_seq" + has dlls/winex11.drv/init.c ".pEnableIMEContext = X11DRV_EnableIMEContext" + has dlls/winex11.drv/xim.c "agent/wine-ime.json" exit $fail verify: diff --git a/banner-proton/apply-deferred-game-patches.sh b/banner-proton/apply-deferred-game-patches.sh index 68d141c2..abca1c74 100755 --- a/banner-proton/apply-deferred-game-patches.sh +++ b/banner-proton/apply-deferred-game-patches.sh @@ -15,6 +15,7 @@ apply "$root/patches/wine-hotfixes/pending/ntdll-android-storage-case-insensitiv apply "$root/patches/game-patches/dirt-telemetry-sendto-closed-socket.patch" apply "$root/patches/wine-hotfixes/pending/ntdll-wow64-exe-preferred-base.patch" apply "$root/patches/wine-hotfixes/pending/winebuild-arm64x-import-thunk-hotpatch.patch" +apply "$root/patches/wine-hotfixes/pending/winex11-report-ime-context-focus.patch" if [ -f "$root/FEX/CMakeLists.txt" ]; then cd "$root/FEX" for p in "$root"/patches/fex/*.patch; do diff --git a/patches/wine-hotfixes/pending/winex11-report-ime-context-focus.patch b/patches/wine-hotfixes/pending/winex11-report-ime-context-focus.patch new file mode 100644 index 00000000..1791f21e --- /dev/null +++ b/patches/wine-hotfixes/pending/winex11-report-ime-context-focus.patch @@ -0,0 +1,92 @@ +From 31a5cb452ac313dcb827760ab44332e9f4de7ac2 Mon Sep 17 00:00:00 2001 +From: maxjivi05 +Date: Fri, 6 Nov 2026 10:08:15 -0500 +Subject: [PATCH] winex11.drv: Report the focused window's input context to + DroidDeck. + +A program that associates an input context with its focused window wants text, as a chat box or search field does, and one that removes it is done typing. winewayland already turns this into text-input enable and disable. winex11 had no EnableIMEContext, so programs on X11 never told DroidDeck when a text field was focused. With BL_LAUNCH_DIR set, the state of the focused window is written to agent/wine-ime.json with the program name. +--- + dlls/winex11.drv/init.c | 1 + + dlls/winex11.drv/x11drv.h | 1 + + dlls/winex11.drv/xim.c | 34 ++++++++++++++++++++++++++++++++++ + 3 files changed, 36 insertions(+) + +diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c +index dca3e27074..91688f65d8 100644 +--- a/dlls/winex11.drv/init.c ++++ b/dlls/winex11.drv/init.c +@@ -1232,6 +1232,7 @@ static const struct user_driver_funcs x11drv_funcs = + .pMapVirtualKeyEx = X11DRV_MapVirtualKeyEx, + .pToUnicodeEx = X11DRV_ToUnicodeEx, + .pVkKeyScanEx = X11DRV_VkKeyScanEx, ++ .pEnableIMEContext = X11DRV_EnableIMEContext, + .pNotifyIMEStatus = X11DRV_NotifyIMEStatus, + .pSetIMECompositionRect = X11DRV_SetIMECompositionRect, + .pDestroyCursorIcon = X11DRV_DestroyCursorIcon, +diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h +index 5fc004bf73..51044393b5 100644 +--- a/dlls/winex11.drv/x11drv.h ++++ b/dlls/winex11.drv/x11drv.h +@@ -211,6 +211,7 @@ extern UINT X11DRV_MapVirtualKeyEx( UINT code, UINT map_type, HKL hkl ); + extern INT X11DRV_ToUnicodeEx( UINT virtKey, UINT scanCode, const BYTE *lpKeyState, + LPWSTR bufW, int bufW_size, UINT flags, HKL hkl ); + extern SHORT X11DRV_VkKeyScanEx( WCHAR wChar, HKL hkl ); ++extern void X11DRV_EnableIMEContext( HWND hwnd, BOOL enabled ); + extern void X11DRV_NotifyIMEStatus( HWND hwnd, UINT status ); + extern BOOL X11DRV_SetIMECompositionRect( HWND hwnd, RECT rect ); + extern void X11DRV_DestroyCursorIcon( HCURSOR handle ); +diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c +index 0cda054f5c..a4ac72ebdf 100644 +--- a/dlls/winex11.drv/xim.c ++++ b/dlls/winex11.drv/xim.c +@@ -26,6 +26,9 @@ + #include + #include + #include ++#include ++#include ++#include + + #include "ntstatus.h" + #define WIN32_NO_STATUS +@@ -333,6 +336,37 @@ void X11DRV_NotifyIMEStatus( HWND hwnd, UINT status ) + XFlush( x11drv_thread_data()->display ); + } + ++static void report_text_field( BOOL focused ) ++{ ++ static LONG seq; ++ const char *launch = getenv( "BL_LAUNCH_DIR" ); ++ char path[4096], temp[4096], text[512]; ++ int fd, len, written; ++ ++ if (!launch || !*launch) return; ++ len = snprintf( path, sizeof(path), "%s/agent/wine-ime.json", launch ); ++ if (len < 0 || len >= (int)sizeof(path)) return; ++ len = snprintf( temp, sizeof(temp), "%s.%d.tmp", path, (int)getpid() ); ++ if (len < 0 || len >= (int)sizeof(temp)) return; ++ len = snprintf( text, sizeof(text), "{\"textField\": %s, \"seq\": %d, \"program\": \"%s\", \"pid\": %d}\n", ++ focused ? "true" : "false", (int)InterlockedIncrement( &seq ), ++ process_name && !strpbrk( process_name, "\"\\" ) ? process_name : "", (int)getpid() ); ++ if (len <= 0 || len >= (int)sizeof(text)) return; ++ if ((fd = open( temp, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0600 )) < 0) return; ++ written = write( fd, text, len ); ++ if (close( fd ) || written != len || rename( temp, path )) unlink( temp ); ++} ++ ++void X11DRV_EnableIMEContext( HWND hwnd, BOOL enabled ) ++{ ++ GUITHREADINFO info = {.cbSize = sizeof(info)}; ++ ++ TRACE( "hwnd %p, enabled %u\n", hwnd, enabled ); ++ ++ if (!NtUserGetGUIThreadInfo( 0, &info ) || info.hwndFocus != hwnd) return; ++ report_text_field( enabled ); ++} ++ + /*********************************************************************** + * xim_init + */ +-- +2.43.0 +