2026-11-08

Enter wouldn't send WoW chat under Wine on Wayland. Wine's input method was eating it.

I'd just started playing WoW with a one-handed keypad and remapped half my keys. Then chat broke. Enter opened the chat box, I could type, /p switched it to party, and the final Enter did nothing. The text just sat there. /dance didn't dance.

The obvious suspect was the remap. It wasn't that, or the addons, or IBus. It was Wine's built-in input method claiming every key I typed into a text box, under a one-line TODO in Wine's source that says it shouldn't.

TL;DR

  • Symptom. In any WoW text box, typing works and Enter doesn't submit. Chat and slash commands never send; a macro with /s test does. In a long session it got worse: the first Enter in the world was dropped and Backspace stopped working.
  • Cause. When the window gets focus, Wine's Wayland driver switches it to an IME keyboard layout. From then on, Wine's built-in IME claims every key except Shift, Ctrl, Alt and CapsLock, whether the IME is open or not (ret = TRUE; /* TODO: should be ctx->fOpen */). WoW gets VK_PROCESSKEY first, then the real key re-posted. Letters survive that. Enter doesn't.
  • Not the cause: keybindings, addons, a stuck key, the account, IBus.
  • Fix: one byte in winewayland.so, so the driver never binds the compositor's text-input protocol. My launcher derives a patched copy of the verified Wine build and refuses to start if the byte isn't where it expects.
  • Unchanged on Wine master as of 2026-11-07.

The box

Same workstation as my PRO 6000 and Gemma 4 posts: Fedora 43, GNOME 49 (mutter 49.8), NVIDIA 595, with WoW on the RTX 5070 Ti. The game runs on Wine 11.19-staging (the Kron4ek wow64 build) with DXVK and vkd3d-proton, started by a small launcher script.

It has to use Wine's native Wayland driver. WoW is D3D12-only, and NVIDIA's Vulkan driver won't present to XWayland surfaces on that card, so the X11 path ends in a swapchain failure loop. That matters later: "just use X11" was never an option.

What broken looked like

  • Enter opens the chat box. Letters type. /p switches the box to Party, so the box is getting the keys.
  • The final Enter does nothing. The box stays open with the text in it.
  • Slash commands don't run either (/dance, /sit). So the problem isn't chat itself; it's Enter in a text box.
  • A macro on an action bar containing /s test works. The server and the account are fine.
  • After a few hours in one session it got worse. The first Enter in the world didn't open chat (the second did, every time), and Backspace stopped deleting anywhere. A fresh session showed only the Enter failure.
  • The Battle.net launcher, under the same Wine and prefix, takes Enter and Backspace in its own chat without trouble.

Ruling things out

  • My new keybindings. Nothing in WoW's saved bindings touches Enter, and the keyboard's own Enter failed exactly like the keypad's.
  • Addons. Three are installed, and none hooks chat sending. Disabling all of them changed nothing.
  • A stuck modifier. With Shift held, / types as ?; with Ctrl or Alt held, letters don't type at all. /p worked, so no.
  • A key held at the device level. Under Wayland, a modifier held on one device applies to every other device. I read each input device's key state with python-evdev: nothing was held on the keyboard, the mouse or the keypad.
  • Chat filtering. The chat window shows system and error messages, so a server-side refusal would have printed a line.
  • IBus. GNOME routes text input through IBus, so I stopped it (systemctl --user stop org.freedesktop.IBus.session.GNOME.service) and tried again. Nothing changed.

The forum threads I found with this symptom on Windows ended in an addon (Misspelled) or with no answer at all.

The trace

Wine's debug channels for keys and the input method show the whole thing: WINEDEBUG=fixme-all,+key,+keyboard,+imm. The Enter that opens chat, with no text box focused yet, arrives plain:

trace:key:NtUserTranslateMessage Translating key VK_RETURN (000d), scancode 001c
trace:imm:ImmAssociateContext hwnd 0000000000090166, new_himc 0000000000480188

WoW then attaches an input context to the chat box, and the next Enter goes through the IME:

trace:imm:ImmProcessKey hwnd 0000000000090166, hkl 00000000E0010409, vkey 0xd, lparam 0x1c0001, unknown 0
trace:imm:ImeProcessKey himc 0000000000480188, vkey 0xd, lparam 0x1c0001, state 00007FFFF013EDB0
trace:key:NtUserTranslateMessage Translating key VK_PROCESSKEY (00e5), scancode 001c
trace:imm:ImeToAsciiEx vkey 0xd, vsc 0x1c, state 00007FFFF013EDC0, msgs 00007FFFF013ECC0, flags 0, himc 0000000000480188
trace:imm:ime_driver_call processing vkey 0xd, scan 0x1c -> 3221225474
trace:imm:ImeToAsciiEx Returning 1 messages queued
trace:key:NtUserTranslateMessage Translating key VK_RETURN (000d), scancode 001c
trace:key:NtUserTranslateMessage 1 -> PostMessageW(0x90166,WM_CHAR,<x>,001c0001) for <x> in L"\r"

Top to bottom:

  1. The IME claims the key, so WoW gets a WM_KEYDOWN with VK_PROCESSKEY (0xE5), the Windows code for "an input method is handling this key".
  2. Wine asks the Wayland driver to process the key. The driver returns 0xC0000002 (STATUS_NOT_IMPLEMENTED).
  3. Wine re-posts the original key and its WM_CHAR '\r'.

Every key in the chat box takes this path: letters, Backspace, Escape. They all arrive intact as the re-posted key. Only Enter gets lost.

Why the IME, and why Enter

Two pieces of Wine source explain it.

The layout. When the compositor tells a window it has focus (text_input.enter), the Wayland driver switches that window to an IME keyboard layout (dlls/winewayland.drv/wayland_text_input.c):

zwp_text_input_v3_commit(text_input->zwp_text_input_v3);
pthread_mutex_unlock(&text_input->mutex);

activate_keyboard_hkl(hwnd, TRUE);

That's the 0xE0010409 in the trace: US English with an 0xE0xx high word, which Wine's is_ime_hkl counts as an IME.

The claim. Wine's built-in IME then decides whether to take each key (dlls/imm32/ime.c):

if (!is_ime_hkl( GetKeyboardLayout( 0 ) )) return FALSE;

if (!(ctx = ImmLockIMC( himc ))) return FALSE;
ret = TRUE; /* TODO: should be ctx->fOpen */
switch (LOWORD(vkey))
{
    case VK_SHIFT:
    case VK_CONTROL:
    case VK_CAPITAL:
    case VK_MENU:
        ret = FALSE;
}

It takes every key except those four, whether the IME is open or not. The TODO says what it should check.

Why that loses Enter and not letters is the one part I can't observe directly, because the client is closed source. On Windows, an Enter that arrives as VK_PROCESSKEY belongs to the IME: it commits a composition (Japanese, Chinese, Korean input), and an IME-aware app must not treat it as "submit". WoW is IME-aware (the client exposes IsInIMECompositionMode to its UI code), so my reading is that it does exactly that, and the re-posted Enter goes down with the commit. Letters carry no such meaning, so the re-posted ones type normally.

This also explains the IBus result. The layout switch happens when mutter sends text_input.enter, and mutter sends it on focus whether IBus is running or not.

The fix: one byte

There's no setting for this. Wine has no option to skip the text-input protocol, and mutter can't hide a protocol from one client. But the driver only binds the protocol if the compositor's protocol name matches a string (dlls/winewayland.drv/wayland.c):

else if (strcmp(interface, "zwp_text_input_manager_v3") == 0)

Change one byte of that string in winewayland.so and the comparison never matches. Wine then behaves as it would under a compositor without text input:

  • it logs Wayland compositor doesn't support optional zwp_text_input_manager_v3 (host input methods won't work);
  • it never switches the window to the IME layout;
  • keys reach WoW plain.

I don't edit the verified Wine build. The launcher derives a reflinked copy (cheap on btrfs) and patches the byte. It refuses to start if the string isn't there exactly once, so a Wine upgrade that renames or fixes something fails loudly instead of silently. A BNET_HOST_IME=1 switch runs the stock build, so after each upgrade I can check whether upstream has fixed it. The snippet is in the appendix.

Result: the first Enter opens chat, Backspace deletes, Enter sends, and /dance dances.

The cost: no host input-method composition inside Wine apps. If you type Japanese or Chinese into Windows apps through IBus or fcitx, this workaround takes that away. Plain keyboard layouts (I switch between US, Arabic and Dutch) don't go through an IME.

What I got wrong

  • I blamed my keybindings first. I'd just remapped everything for the keypad, and "I changed something and then it broke" is usually right. Not here: the trace shows the IME claiming keys whatever they're bound to.
  • I suspected IBus, because GNOME's input-method stack sits between the keyboard and every text field. Stopping it changed nothing. The decision that matters is made in the compositor and in Wine.
  • I half expected a chat restriction or a server-side refusal. A macro sending the same text worked, which settled it in one test.

What I still don't know

  • Exactly where WoW drops the Enter. The trace shows what it receives, not what it does with it.
  • Why a long session also lost the first Enter in the world and Backspace. A fresh stock session didn't reproduce either of them. I haven't seen them on the patched build, but I haven't run it as long either.

The proper fix

The TODO names it: ImeProcessKey should only claim keys while the IME is open. A narrower alternative is for the Wayland driver to switch a window to an IME layout only once an input method is actually engaged, not on every focus. I'm filing it with Wine's bug tracker.

If you hit this

  • If Enter doesn't submit in a Wine app on Wayland but typing works, take a WINEDEBUG=+key,+imm trace and look for VK_PROCESSKEY on plain keys.
  • grep 'zwp_text_input_manager_v3' wine.log. If the "doesn't support" line is missing, the IME path is live.
  • Don't spend time on IBus. Stopping it doesn't change this.
  • Rule out the server side with a macro before you take the client apart.

Appendix

The launcher snippet that derives and verifies the patched Wine build. Click to expand.

Chasing a bug through someone else's stack?

Most infrastructure work is reading traces until the cause is one line in somebody else's source. If you have one of those, on a desktop, a server or a cluster, I'd like to hear about it.

← Back to Blog