http://bugs.winehq.org/show_bug.cgi?id=35907
Bug ID: 35907 Summary: Caps Lock state gets confused with multiple processes/threads Product: Wine Version: 1.7.15 Hardware: x86 OS: Mac OS X Status: NEW Severity: normal Priority: P2 Component: wineserver Assignee: wine-bugs@winehq.org Reporter: ken@codeweavers.com
Open two instances of Notepad in the same prefix:
wine notepad & wine notepad &
With one Notepad window active, turn Caps Lock on. Switch to the other Notepad window. Type some text. Notice it's not capitalized, although it should be. Turn Caps Lock off. Type some text. Notice that it is capitalized, although it shouldn't be. Switch back to the first Notepad window. Type some text. Notice that it is capitalized, although it shouldn't be.
http://bugs.winehq.org/show_bug.cgi?id=35907
--- Comment #1 from Ken Thomases ken@codeweavers.com --- The problem is that the X11 driver uses the global async key state to test if the wineserver thinks the key is currently locked on or off, but that's not what TranslateMessage() uses for its call to ToUnicode(). TranslateMessage() uses the synchronous and thread-specific state. The thread-specific state is only updated when that thread gets a WM_KEYDOWN for VK_CAPITAL.
The thread-specific state has to be in sync with the messages, but I expect that if you lock Caps Lock on when one thread's window has focus, then other threads should eventually show that state when they've processed all pending messages. I don't know if each thread should see its own WM_KEYDOWN for VK_CAPITAL or its state should just silently change at the point in the message stream where such a message would have been.
If the X11 driver tries to compensate by generating VK_CAPITAL press-release pairs for every thread, then the global (desktop) key state will be wrong (depending on if there are an odd or even number of threads). So, I think this has to be solved in the wineserver.
This will require tests to determine the correct behavior.
http://bugs.winehq.org/show_bug.cgi?id=35907
Ken Thomases ken@codeweavers.com changed:
What |Removed |Added ---------------------------------------------------------------------------- See Also| |http://bugs.winehq.org/show | |_bug.cgi?id=26489
https://bugs.winehq.org/show_bug.cgi?id=35907
Austin English austinenglish@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- Keywords| |download CC| |austinenglish@gmail.com
http://bugs.winehq.org/show_bug.cgi?id=35907
--- Comment #2 from Dmitry Timoshkov dmitry@baikal.ru --- (In reply to Ken Thomases from comment #1)
The thread-specific state is only updated when that thread gets a WM_KEYDOWN for VK_CAPITAL.
If that would be true then turning Caps Lock on before running notepad would lead to wrong results as well, but that's not the case.
http://bugs.winehq.org/show_bug.cgi?id=35907
--- Comment #3 from Ken Thomases ken@codeweavers.com --- (In reply to Dmitry Timoshkov from comment #2)
(In reply to Ken Thomases from comment #1)
The thread-specific state is only updated when that thread gets a WM_KEYDOWN for VK_CAPITAL.
If that would be true then turning Caps Lock on before running notepad would lead to wrong results as well, but that's not the case.
No, because then the thread owning the Notepad window does receive the VK_CAPITAL key-down. The wineserver's notion of the async key state starts out cleared. When X11 delivers a KeyPress, the X11 driver examines the async key state and compares it to the event state to decide whether it should synthesize a VK_CAPITAL key down. The event state has LockMask but the Wine key state does not indicate that VK_CAPITAL is toggled on, so the X11 driver synthesizes a key-down, key-up pair. The thread receives the WM_KEYDOWN.
The WM_KEYDOWN is not associated with the window/thread that was current when the actual Caps Lock key was pressed or occur at the time it was pressed. It occurs at the time that the X11 driver notices the mismatch between the X11 state and the Win32 async key state, and is delivered to the target window's thread (and only that thread).
http://bugs.winehq.org/show_bug.cgi?id=35907
--- Comment #4 from Ken Thomases ken@codeweavers.com --- Created attachment 47963 --> http://bugs.winehq.org/attachment.cgi?id=47963 Test program for behavior on Windows
The attached program (with source) helps demonstrate the behavior on Windows. When you run it, it presents a window. It logs when it gets a WM_KEYDOWN or WM_SYSKEYDOWN for VK_CAPITAL. It also checks the status of VK_CAPITAL with GetAsyncKeyState(), GetKeyState(), and GetKeyboardState(). It does that same check every 3 seconds, even with it doesn't have focus or receive WM_KEYDOWN.
On Windows, when the window has focus, turning Caps Lock on and waiting a few seconds results in:
11:12:44: WM_KEYDOWN VK_CAPITAL 11:12:44: GetKeyState(VK_CAPITAL) 0x00 -> 0x81 GetKeyboardState() VK_CAPITAL 0x00 -> 0x81 11:12:44: GetKeyState(VK_CAPITAL) 0x81 -> 0x01 GetKeyboardState() VK_CAPITAL 0x81 -> 0x01
Turning it back off:
11:12:52: WM_KEYDOWN VK_CAPITAL 11:12:52: GetKeyState(VK_CAPITAL) 0x01 -> 0x80 GetKeyboardState() VK_CAPITAL 0x01 -> 0x80 11:12:53: GetKeyState(VK_CAPITAL) 0x80 -> 0x00 GetKeyboardState() VK_CAPITAL 0x80 -> 0x00
Then, if you switch to another window and turn Caps Lock on and wait:
11:13:02: GetKeyState(VK_CAPITAL) 0x00 -> 0x01 GetKeyboardState() VK_CAPITAL 0x00 -> 0x01
And turn it off again and wait:
11:13:08: GetKeyState(VK_CAPITAL) 0x01 -> 0x00 GetKeyboardState() VK_CAPITAL 0x01 -> 0x00
So, the thread without focus does not get a WM_KEYDOWN, but its thread-specific key state changes "spontaneously".
Curiously, GetAsyncKeyState(VK_CAPITAL) never showed the key being down (having the high bit set), even when I was holding it down for long periods. It did show that the key had been pressed since the last call (low bit set), but I've masked that out as noise.
http://bugs.winehq.org/show_bug.cgi?id=35907
--- Comment #5 from Ken Thomases ken@codeweavers.com --- I suspect, but haven't yet confirmed with tests, that the change of thread keystate happens in sync with the message stream. That is, the keystate doesn't change until the thread pops all messages that were queued for it prior to the time when the Caps Lock key-press was sent.
So, this may require that, when the hardware message is queued for the destination thread, the wineserver also queue a fake version of the message into every other thread's queue, too. The fake message would never be returned from Get/PeekMessage(), but when it reaches the front of the queue, it would alter the keystate just like the real message does for the destination thread.
https://bugs.winehq.org/show_bug.cgi?id=35907
Sebastian Lackner sebastian@fds-team.de changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |sebastian@fds-team.de
--- Comment #6 from Sebastian Lackner sebastian@fds-team.de --- (In reply to Ken Thomases from comment #5)
So, this may require that, when the hardware message is queued for the destination thread, the wineserver also queue a fake version of the message into every other thread's queue, too. The fake message would never be returned from Get/PeekMessage(), but when it reaches the front of the queue, it would alter the keystate just like the real message does for the destination thread.
My analysis of bug 27238 and 31899 shows that the key state is also updated even though there is no main message loop (and I have also tested and confirmed that manually with a small test code). I have more the impression that as soon as there are no further queued key/input events, then the key state gets "magically" synced again with the async key state.
https://bugs.winehq.org/show_bug.cgi?id=35907
--- Comment #7 from Sebastian Lackner sebastian@fds-team.de --- A fix was added to Wine-Staging: https://github.com/wine-compholio/wine-staging/tree/master/patches/server-Ke...
I ran a lot of manual tests with various self-written test applications and games, and came to the conclusion that my theory is right. Windows internally uses a locking mechanism of the thread input keystate, and under specific circumstances synchronizes it with the global keystate. Some things in the patch might look weird (for example that GetKeyboardState() does _NOT_ synchronize the keystate), but this matches very exactly what Windows does. Will try to clean up the tests during the next time, and integrate them somehow in the wine test suite.
https://bugs.winehq.org/show_bug.cgi?id=35907
Michael Müller michael@fds-team.de changed:
What |Removed |Added ---------------------------------------------------------------------------- Status|NEW |STAGED CC| |michael@fds-team.de Staged patchset| |https://github.com/wine-com | |pholio/wine-staging/tree/ma | |ster/patches/server-Key_Sta | |te
https://bugs.winehq.org/show_bug.cgi?id=35907
Austin English austinenglish@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- Keywords| |patch
https://bugs.winehq.org/show_bug.cgi?id=35907
Ken Thomases ken@codeweavers.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |4ijgr8+eaj5wf4kx1e2g@sharkl | |asers.com
--- Comment #8 from Ken Thomases ken@codeweavers.com --- *** Bug 41461 has been marked as a duplicate of this bug. ***
https://bugs.winehq.org/show_bug.cgi?id=35907
André H. nerv@dawncrow.de changed:
What |Removed |Added ---------------------------------------------------------------------------- Staged patchset|https://github.com/wine-com |https://github.com/wine-sta |pholio/wine-staging/tree/ma |ging/wine-staging/tree/mast |ster/patches/server-Key_Sta |er/patches/server-Key_State |te | CC| |nerv@dawncrow.de
https://bugs.winehq.org/show_bug.cgi?id=35907
Julian Rüger jr98@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |jr98@gmx.net