Hi, the documentation for these two functions isn't clear about the contents of the bits of the return value, except the highest and lowest bit. Some programs assume that these are always set to zero, which seems to be the actual behavior in Windows.
Specifically, this leads to stuck keys in "Age of Empires 2 HD" and "Age of Empires 2: Definitive Edition" when tabbing out as reported in https://bugs.winehq.org/show_bug.cgi?id=30814 .
The following patch makes sure that all undefined bits are set to zero.
Markus
Markus Engel (2): user32: Force bits in GetKeyState() and GetKeyboardState() to zero. user32/tests: Add more tests for GetKeyState().
dlls/user32/input.c | 8 +++++++- dlls/user32/tests/input.c | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-)
Only the highest and lowest bits in the return values of these functions have a meaning, the others are undefined. Some programs expect these undefined bits to be zero, though, so make sure they are not set.
This is a slightly adjusted version of the patch by Raditz12 that is attached to the bug report.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=30814 Signed-off-by: Markus Engel markus_wine@familie-engel.online --- dlls/user32/input.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/dlls/user32/input.c b/dlls/user32/input.c index b7cdbd84ef..89ef4924ad 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -555,7 +555,9 @@ SHORT WINAPI DECLSPEC_HOTPATCH GetKeyState(INT vkey) { req->tid = GetCurrentThreadId(); req->key = vkey; - if (!wine_server_call( req )) retval = (signed char)reply->state; + + if (!wine_server_call( req )) + retval = (reply->state & 0x80) << 8 | (reply->state & 0x01); } SERVER_END_REQ; TRACE("key (0x%x) -> %x\n", vkey, retval); @@ -569,6 +571,7 @@ SHORT WINAPI DECLSPEC_HOTPATCH GetKeyState(INT vkey) BOOL WINAPI DECLSPEC_HOTPATCH GetKeyboardState( LPBYTE state ) { BOOL ret; + UINT i;
TRACE("(%p)\n", state);
@@ -579,6 +582,9 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetKeyboardState( LPBYTE state ) req->key = -1; wine_server_set_reply( req, state, 256 ); ret = !wine_server_call_err( req ); + + for (i = 0; i < 256; i++) + state[i] &= 0x81; } SERVER_END_REQ; return ret;
Signed-off-by: Markus Engel markus_wine@familie-engel.online --- dlls/user32/tests/input.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 43fdd34191..3daf445c94 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -2759,6 +2759,8 @@ static DWORD WINAPI get_key_state_thread(void *arg) ok((result & 0x8000) || broken(!(result & 0x8000)), /* > Win 2003 */ "expected that highest bit is set, got %x\n", result);
+ ok((SHORT)(result & 0x8001) == (SHORT)result, "expected that undefined bits are unset, got %x\n", result); + ReleaseSemaphore(semaphores[0], 1, NULL); result = WaitForSingleObject(semaphores[1], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); @@ -2766,6 +2768,8 @@ static DWORD WINAPI get_key_state_thread(void *arg) result = GetKeyState('X'); ok(!(result & 0x8000), "expected that highest bit is unset, got %x\n", result);
+ ok((SHORT)(result & 0x0001) == (SHORT)result, "expected that undefined bits are unset, got %x\n", result); + return 0; }
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=71882
Your paranoid android.
=== wxppro (32 bit report) ===
user32: input.c:2762: Test failed: expected that undefined bits are unset, got ffffff80