[PATCH v4 2/2] dinput: Fix DInput8 keyboard behavior for injected events with scancode=0.

Brendan Shanks bshanks at codeweavers.com
Fri Apr 17 14:14:43 CDT 2020


> On Apr 17, 2020, at 11:47 AM, Rémi Bernon <rbernon at codeweavers.com> wrote:
> 
> On 4/17/20 8:35 PM, Brendan Shanks wrote:
>> Grand Theft Auto IV injects VK_F8 and scancode=0, and expects DirectInput not
>> to report that F8 is pressed.
>> Signed-off-by: Brendan Shanks <bshanks at codeweavers.com>
>> ---
>> v4: Only make this behavior change for DirectInput 8.
>> It's difficult to show because of the separation of 8 and pre-8 tests,
>> but really does seem to depend only on the DI version used.
>>  dlls/dinput/keyboard.c      | 8 ++++----
>>  dlls/dinput8/tests/device.c | 2 --
>>  2 files changed, 4 insertions(+), 6 deletions(-)
>> diff --git a/dlls/dinput/keyboard.c b/dlls/dinput/keyboard.c
>> index 47f28cac52..f842f1ca42 100644
>> --- a/dlls/dinput/keyboard.c
>> +++ b/dlls/dinput/keyboard.c
>> @@ -68,9 +68,9 @@ static inline IDirectInputDevice8W *IDirectInputDevice8W_from_impl(SysKeyboardIm
>>      return &This->base.IDirectInputDevice8W_iface;
>>  }
>>  -static BYTE map_dik_code(DWORD scanCode, DWORD vkCode, DWORD subType)
>> +static BYTE map_dik_code(DWORD scanCode, DWORD vkCode, DWORD subType, DWORD version)
>>  {
>> -    if (!scanCode)
>> +    if (!scanCode && version < 0x0800)
>>          scanCode = MapVirtualKeyW(vkCode, MAPVK_VK_TO_VSC);
>>        if (subType == DIDEVTYPEKEYBOARD_JAPAN106)
>> @@ -125,7 +125,7 @@ static int KeyboardCallback( LPDIRECTINPUTDEVICE8A iface, WPARAM wparam, LPARAM
>>          case VK_NUMLOCK : dik_code = DIK_NUMLOCK; break;
>>          case VK_SUBTRACT: dik_code = DIK_SUBTRACT; break;
>>          default:
>> -            dik_code = map_dik_code(hook->scanCode & 0xff, hook->vkCode, This->subtype);
>> +            dik_code = map_dik_code(hook->scanCode & 0xff, hook->vkCode, This->subtype, This->base.dinput->dwVersion);
>>              if (hook->flags & LLKHF_EXTENDED) dik_code |= 0x80;
>>      }
>>      new_diks = hook->flags & LLKHF_UP ? 0 : 0x80;
>> @@ -282,7 +282,7 @@ static SysKeyboardImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput)
>>          if (!GetKeyNameTextA(((i & 0x7f) << 16) | ((i & 0x80) << 17), buf, sizeof(buf)))
>>              continue;
>>  -        dik_code = map_dik_code(i, 0, newDevice->subtype);
>> +        dik_code = map_dik_code(i, 0, newDevice->subtype, dinput->dwVersion);
>>          memcpy(&df->rgodf[idx], &c_dfDIKeyboard.rgodf[dik_code], df->dwObjSize);
>>          df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(dik_code) | DIDFT_PSHBUTTON;
>>      }
>> diff --git a/dlls/dinput8/tests/device.c b/dlls/dinput8/tests/device.c
>> index bb82855d40..39c635f2fb 100644
>> --- a/dlls/dinput8/tests/device.c
>> +++ b/dlls/dinput8/tests/device.c
>> @@ -820,7 +820,6 @@ static void test_keyboard_events(void)
>>      data_size = ARRAY_SIZE(obj_data);
>>      hr = IDirectInputDevice8_GetDeviceData(di_keyboard, sizeof(DIDEVICEOBJECTDATA), obj_data, &data_size, 0);
>>      ok(SUCCEEDED(hr), "Failed to get data hr=%08x\n", hr);
>> -    todo_wine
>>      ok(data_size == 0, "Expected 0 elements, received %d\n", data_size);
>>        hr = IDirectInputDevice8_GetDeviceState(di_keyboard, sizeof(kbdata), kbdata);
>> @@ -834,7 +833,6 @@ static void test_keyboard_events(void)
>>      data_size = ARRAY_SIZE(obj_data);
>>      hr = IDirectInputDevice8_GetDeviceData(di_keyboard, sizeof(DIDEVICEOBJECTDATA), obj_data, &data_size, 0);
>>      ok(SUCCEEDED(hr), "Failed to get data hr=%08x\n", hr);
>> -    todo_wine
>>      ok(data_size == 0, "Expected 0 elements, received %d\n", data_size);
>>        hr = IDirectInputDevice8_Unacquire(di_keyboard);
> 
> Ha that's why it installs a LL keyboard hook, I was wondering why it was doing that.
> 
> Long story short, DInput8 uses RawInput API to get input events, so I believe it should not install any LL hook in that case.
> 
> In addition, in exclusive cooperative mode it should also set the RIDEV_NOLEGACY flag which I believe prevents any input messages to be sent -even to LL hooks.
> 
> The rawinput patches that are in Staging should implement that logic, but I kept the hooks installed, specifically because of GTAV.
> 
> When no DInput hooks are there, wineserver sends the messages to GTAV and they are neve handled for some reason, which causes a wait and timeout on every keypress.
> 
> When the hooks are kept, and because the esync staging patch series also includes a hook removal on timeout -as Win >=7 is supposed to do [*]-, the first input makes GTAV hook time out, and it's then removed.
> 
> [*] I did some investigation and it looks like Windows >=7 removes hooks that time out for 32bit applications, not 64bit apps for whatever reason.

Hmm interesting, this may be a different issue though. I’ve only seen this on GTA 4, not sure if there’s something similar in GTA 5.
GTAIV does have code to install a LL keyboard hook, but on Windows 10 and Wine it doesn’t seem to actually run.

A minute or two after the game launches, it starts calling SendInput() constantly with VK_F8, scan=0 and other args=0. 
It’s bizarre behavior, my best theory is that F8 was a common key used by cheats to bring up their menu, so constantly sending it would make cheats go crazy and the game unplayable. But by exploiting this corner case of DirectInput8 ignoring the VK (certainly when scancode=0), the game itself isn’t affected.

Brendan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.winehq.org/pipermail/wine-devel/attachments/20200417/59a476a8/attachment-0001.htm>


More information about the wine-devel mailing list