[Bug 59962] New: dinput: buffered keyboard events visible to the game before the corresponding window message is pumped, causing GUI double-activation in Neverwinter Nights (Diamond 1.69)
http://bugs.winehq.org/show_bug.cgi?id=59962 Bug ID: 59962 Summary: dinput: buffered keyboard events visible to the game before the corresponding window message is pumped, causing GUI double-activation in Neverwinter Nights (Diamond 1.69) Product: Wine Version: 11.11 Hardware: x86-64 OS: Linux Status: UNCONFIRMED Severity: normal Priority: P2 Component: -unknown Assignee: wine-bugs@list.winehq.org Reporter: olanordmann@duck.com Distribution: --- Created attachment 81349 --> http://bugs.winehq.org/attachment.cgi?id=81349 WINEDEBUG=+key,+dinput, 1000 lines around five V presses In Neverwinter Nights (2002, GOG Diamond Edition 1.69, 32-bit), pressing a GUI hotkey (V = quickchat menu, Enter = chat bar focus) opens the GUI element and immediately closes it again ("flash"), unless the key is released extremely quickly. Text typing inside the chat is unaffected. The game is unaffected on real Windows 10/11 on the same machine. The game uses DirectInput (dinput.dll, DirectInputCreateA, keyboard device with SetCooperativeLevel DISCL_NONEXCLUSIVE | DISCL_FOREGROUND) for world hotkeys, polling GetDeviceData every frame, and window messages (WM_KEYDOWN/WM_CHAR) for its GUI. It Unacquires the keyboard while a GUI element is open and re-Acquires on close. A WINEDEBUG=+key,+dinput trace shows the failure sequence for one single physical key press (X11 delivers exactly one KeyPress; xev confirms no duplicate events; auto-repeat disabled via `xset r off` does not change the behavior): ``` X11DRV_KeyEvent KeyPress keysym=76 (v) # one physical press X11DRV_send_keyboard_input vkey=0056 scan=002f flags=0000 input_thread_ll_hook_proc calling dinput_keyboard_hook # dinput queue filled immediately dinput_keyboard_hook wparam 0x100 vkCode 0x56 # from Wine's input thread [game polls GetDeviceData, sees V press, OPENS quickchat menu] dinput_device_Unacquire # game switches to GUI input mode NtUserTranslateMessage Translating key VK_V (0056) # the SAME keystroke's message NtUserTranslateMessage -> PostMessage WM_CHAR "v" # is pumped only now [the now-open menu receives WM_KEYDOWN/WM_CHAR for V and CLOSES] dinput_device_Acquire # game back in world mode ``` The dinput buffered event is enqueued by Wine's dedicated input thread (low-level hook path) and becomes visible to the game's GetDeviceData poll *before* the game's main thread has pumped the WM_KEYDOWN for the same keystroke. The game acts on the dinput copy first (opens menu), then acts on the message copy (menu closes). On Windows the game evidently never observes this ordering. Only world→GUI transitions double-fire. Closing a GUI element is always clean, because the keyboard is unacquired while the GUI is open, so only the message path is active — consistent with the analysis. **Workarounds tried, all ineffective or impossible:** - wine 10.20, 11.5, 11.10, 11.11, wine-cachyos (proton-derived): identical behavior - Disabling dinput via WINEDLLOVERRIDES: nwmain.exe statically imports DINPUT.dll → fails to load - Native Win10/11 dinput.dll: crashes (delay-loads ext-ms-win-mininput-inputhost-l1-1-0.dll, not provided by Wine) - X11 and Wayland sessions, virtual desktop mode, autorepeat off, vsync on/off: no difference **Steps to reproduce:** 1. Install NWN Diamond (GOG) in a 32-bit-capable prefix, run nwmain.exe 2. Start any module, press V at a normal speed (hold ~100–300 ms) 3. Quickchat menu opens and instantly closes; releasing the key within a few tens of ms keeps it open **System:** Arch Linux (CachyOS), kernel 7.1.1-2-cachyos, KDE Plasma 6.7.1 (X11 and Wayland), NVIDIA RTX 4090 (proprietary 610.43.02), wine 11.11 (also reproduced 10.20/11.5/11.10) **Attachment:** nwn-dinput-trace-excerpt.log (WINEDEBUG=+key,+dinput, 1000 lines around five V presses; full 4.4 MB log available on request) -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
http://bugs.winehq.org/show_bug.cgi?id=59962 Ola Nordmann <olanordmann@duck.com> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |olanordmann@duck.com -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
http://bugs.winehq.org/show_bug.cgi?id=59962 --- Comment #1 from Ola Nordmann <olanordmann@duck.com> --- I did further debugging with WINEDEBUG=+key,+dinput and found the root cause, plus a workaround that confirms it. Root cause: the game (Neverwinter Nights Diamond 1.69, 32-bit) reads world hotkeys via DirectInput buffered data (dinput.dll, DirectInputCreateA, keyboard with SetCooperativeLevel DISCL_NONEXCLUSIVE|DISCL_FOREGROUND, GetDeviceData polled every frame) and GUI input via window messages. Under Wine, a single physical keystroke becomes visible in the dinput buffer *before* the same keystroke's WM_KEYDOWN is pumped by the game's main thread, because builtin dinput fills its queue from Wine's dedicated input thread (low-level hook path). Sequence from the attached trace (one physical V press; X11 delivers exactly one KeyPress, xev verified, autorepeat disabled): X11DRV_send_keyboard_input vkey=0056 scan=002f flags=0000 input_thread_ll_hook_proc calling dinput_keyboard_hook <- dinput queue filled immediately dinput_keyboard_hook wparam 0x100 vkCode 0x56 [game polls GetDeviceData, sees V, opens quickchat GUI] dinput_device_Unacquire <- game switches to GUI input mode NtUserTranslateMessage Translating key VK_V (0056) <- same keystroke's message pumped now NtUserTranslateMessage -> PostMessageW WM_CHAR "v" [open GUI receives the message, treats it as a new V press, closes -> "flash"] dinput_device_Acquire Only world->GUI transitions double-fire; closing GUI elements is always clean because the keyboard is unacquired while the GUI is open (single input path), which is consistent with the analysis. Reproduced identically on wine 10.20, 11.5, 11.10, 11.11 and wine-cachyos (Proton-derived), X11 and Wayland sessions, KDE Plasma 6.7.1, virtual desktop on/off, keyboard autorepeat on/off. Not a regression — long-standing behavior. Game works correctly on real Windows 10/11 on the same machine. Workaround confirming the diagnosis: overriding with the native Windows XP SP3 dinput.dll (5.03.2600.5512; imports only msvcrt/kernel32/advapi32/user32/winmm, all provided by Wine) via HKCU\Software\Wine\AppDefaults\nwmain.exe\DllOverrides dinput=native fixes the issue completely — with the XP DLL doing its own buffering, the game evidently never observes a dinput event for a keystroke whose window message it has not yet processed. (Native Windows 10/11 dinput.dll cannot be used for comparison: it fails with 0xc06d007e delay-loading ext-ms-win-mininput-inputhost-l1-1-0.dll.) So the defect appears to be event-ordering between builtin dinput's buffered keyboard queue and the thread message queue: buffered dinput data for a keystroke should not be observable by the application before the corresponding keyboard message can be retrieved by the window thread. -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
http://bugs.winehq.org/show_bug.cgi?id=59962 --- Comment #2 from Ola Nordmann <olanordmann@duck.com> --- Created attachment 81350 --> http://bugs.winehq.org/attachment.cgi?id=81350 Proposed one-line fix: use raw input for legacy dinput keyboard devices Attaching a proposed fix. It removes the dwVersion >= 0x0800 gate in keyboard_create_device() so that legacy DirectInput keyboard devices use the raw input path, as DirectInput 8 devices already do. With raw input there is no low-level keyboard hook, so the hardware message is queued together with the raw input instead of being withheld while the hook runs — the buffered event can no longer be observed via GetDeviceData before the corresponding WM_KEYDOWN is retrievable by the application. Tested against Neverwinter Nights Diamond 1.69 on wine 11.11 by replacing the builtin dinput.dll with a patched build: the GUI double-activation described above is gone and keyboard input otherwise behaves normally. Exclusive-mode message suppression is unaffected, as it is handled via RIDEV_NOLEGACY. I'll submit this as a merge request once my GitLab account is verified for forking (user verification issue filed). -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
participants (1)
-
WineHQ Bugzilla