[DINPUT_WINETEST] Sync with Wine Staging 4.18. CORE-16441
[reactos.git] / modules / rostests / winetests / dinput / keyboard.c
index e51e332..0e399f0 100644 (file)
 #include "wingdi.h"
 #include "dinput.h"
 
+/* to make things easier with PSDK without a dinput.lib */
+static HRESULT (WINAPI *pDirectInputCreateA)(HINSTANCE,DWORD,IDirectInputA **,IUnknown *);
+
+static void pump_messages(void)
+{
+    MSG msg;
+
+    while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
+    {
+        TranslateMessage(&msg);
+        DispatchMessageA(&msg);
+    }
+}
+
+static HKL activate_keyboard_layout(LANGID langid, HKL *hkl_orig)
+{
+    HKL hkl, hkl_current;
+    char hkl_name[64];
+
+    sprintf(hkl_name, "%08x", langid);
+    trace("Loading keyboard layout %s\n", hkl_name);
+    hkl = LoadKeyboardLayoutA(hkl_name, 0);
+    if (!hkl)
+    {
+        win_skip("Unable to load keyboard layout %s\n", hkl_name);
+        return 0;
+    }
+    *hkl_orig = ActivateKeyboardLayout(hkl, 0);
+    ok(*hkl_orig != 0, "Unable to activate keyboard layout %s\n", hkl_name);
+    if (!*hkl_orig) return 0;
+
+    hkl_current = GetKeyboardLayout(0);
+    if (LOWORD(hkl_current) != langid)
+    {
+        /* FIXME: Wine can't activate different keyboard layouts.
+         * for testing purposes use this workaround:
+         * setxkbmap us && LANG=en_US.UTF-8 make test
+         * setxkbmap fr && LANG=fr_FR.UTF-8 make test
+         * setxkbmap de && LANG=de_DE.UTF-8 make test
+         */
+        skip("current %08x != langid %08x\n", LOWORD(hkl_current), langid);
+        return 0;
+    }
+
+    return hkl;
+}
+
 static void acquire_tests(IDirectInputA *pDI, HWND hwnd)
 {
     HRESULT hr;
@@ -44,13 +91,18 @@ static void acquire_tests(IDirectInputA *pDI, HWND hwnd)
             { &GUID_Key, sizeof(LONG) * 2, DIDFT_MAKEINSTANCE(DIK_E)|DIDFT_BUTTON, 0 },
             { &GUID_Key, sizeof(LONG) * 4, DIDFT_MAKEINSTANCE(DIK_R)|DIDFT_BUTTON, 0 },
         };
-
     DIDATAFORMAT df;
+    HKL hkl, hkl_orig;
+    UINT prev_raw_devices_count, raw_devices_count;
+
+    hkl = activate_keyboard_layout(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), &hkl_orig);
+    if (!hkl) return;
+
     df.dwSize = sizeof( df );
     df.dwObjSize = sizeof( DIOBJECTDATAFORMAT );
     df.dwFlags = DIDF_RELAXIS;
     df.dwDataSize = sizeof( custom_state );
-    df.dwNumObjs = sizeof( dodf )/sizeof( dodf[0] );
+    df.dwNumObjs = ARRAY_SIZE(dodf);
     df.rgodf = dodf;
 
     hr = IDirectInput_CreateDevice(pDI, &GUID_SysKeyboard, &pKeyboard, NULL);
@@ -88,11 +140,13 @@ static void acquire_tests(IDirectInputA *pDI, HWND hwnd)
 
     memset(custom_state, 0x56, sizeof(custom_state));
     IDirectInputDevice_GetDeviceState(pKeyboard, sizeof(custom_state), custom_state);
-    for (i = 0; i < sizeof(custom_state) / sizeof(custom_state[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(custom_state); i++)
         ok(custom_state[i] == 0, "Should be zeroed, got 0x%08x\n", custom_state[i]);
 
     /* simulate some keyboard input */
     SetFocus(hwnd);
+    pump_messages();
+
     keybd_event('Q', 0, 0, 0);
     hr = IDirectInputDevice_GetDeviceState(pKeyboard, sizeof(custom_state), custom_state);
     ok(SUCCEEDED(hr), "IDirectInputDevice_GetDeviceState() failed: %08x\n", hr);
@@ -107,12 +161,31 @@ static void acquire_tests(IDirectInputA *pDI, HWND hwnd)
         ok(SUCCEEDED(hr), "IDirectInputDevice_Acquire() failed: %08x\n", hr);
         hr = IDirectInputDevice_GetDeviceState(pKeyboard, sizeof(custom_state), custom_state);
         ok(SUCCEEDED(hr), "IDirectInputDevice_GetDeviceState failed: %08x\n", hr);
-        for (i = 0; i < sizeof(custom_state) / sizeof(custom_state[0]); i++)
+        for (i = 0; i < ARRAY_SIZE(custom_state); i++)
             ok(custom_state[i] == 0, "Should be zeroed, got 0x%08x\n", custom_state[i]);
     }
     keybd_event('Q', 0, KEYEVENTF_KEYUP, 0);
 
+    prev_raw_devices_count = 0;
+    GetRegisteredRawInputDevices(NULL, &prev_raw_devices_count, sizeof(RAWINPUTDEVICE));
+    ok(prev_raw_devices_count == 0 || broken(prev_raw_devices_count == 1) /* wxppro, w2003std */,
+       "Unexpected raw devices registered: %d\n", prev_raw_devices_count);
+
+    hr = IDirectInputDevice_Acquire(pKeyboard);
+    ok(SUCCEEDED(hr), "IDirectInputDevice_Acquire() failed: %08x\n", hr);
+
+    raw_devices_count = 0;
+    GetRegisteredRawInputDevices(NULL, &raw_devices_count, sizeof(RAWINPUTDEVICE));
+    ok(raw_devices_count == prev_raw_devices_count,
+       "Unexpected raw devices registered: %d\n", raw_devices_count);
+
+    hr = IDirectInputDevice_Unacquire(pKeyboard);
+    ok(SUCCEEDED(hr), "IDirectInputDevice_Unacquire() failed: %08x\n", hr);
+
     if (pKeyboard) IUnknown_Release(pKeyboard);
+
+    ActivateKeyboardLayout(hkl_orig, 0);
+    UnloadKeyboardLayout(hkl);
 }
 
 static const HRESULT SetCoop_null_window[16] =  {
@@ -207,6 +280,7 @@ static void test_capabilities(IDirectInputA *pDI, HWND hwnd)
     HRESULT hr;
     IDirectInputDeviceA *pKeyboard = NULL;
     DIDEVCAPS caps;
+    int kbd_type, kbd_subtype, dev_subtype;
 
     hr = IDirectInput_CreateDevice(pDI, &GUID_SysKeyboard, &pKeyboard, NULL);
     ok(SUCCEEDED(hr), "IDirectInput_CreateDevice() failed: %08x\n", hr);
@@ -217,14 +291,214 @@ static void test_capabilities(IDirectInputA *pDI, HWND hwnd)
 
     ok (SUCCEEDED(hr), "GetCapabilities failed: 0x%08x\n", hr);
     ok (caps.dwFlags & DIDC_ATTACHED, "GetCapabilities dwFlags: 0x%08x\n", caps.dwFlags);
-    ok (LOWORD(LOBYTE(caps.dwDevType)) == DIDEVTYPE_KEYBOARD,
+    ok (GET_DIDEVICE_TYPE(caps.dwDevType) == DIDEVTYPE_KEYBOARD,
         "GetCapabilities invalid device type for dwDevType: 0x%08x\n", caps.dwDevType);
-    ok (LOWORD(HIBYTE(caps.dwDevType)) != DIDEVTYPEKEYBOARD_UNKNOWN,
-        "GetCapabilities invalid device subtype for dwDevType: 0x%08x\n", caps.dwDevType);
+    kbd_type = GetKeyboardType(0);
+    kbd_subtype = GetKeyboardType(1);
+    dev_subtype = GET_DIDEVICE_SUBTYPE(caps.dwDevType);
+    if (kbd_type == 4 || (kbd_type == 7 && kbd_subtype == 0))
+        ok (dev_subtype == DIDEVTYPEKEYBOARD_PCENH,
+            "GetCapabilities invalid device subtype for dwDevType: 0x%08x (%04x:%04x)\n",
+            caps.dwDevType, kbd_type, kbd_subtype);
+    else if (kbd_type == 7 && kbd_subtype == 2)
+        ok (dev_subtype == DIDEVTYPEKEYBOARD_JAPAN106,
+            "GetCapabilities invalid device subtype for dwDevType: 0x%08x (%04x:%04x)\n",
+            caps.dwDevType, kbd_type, kbd_subtype);
+    else
+        ok (dev_subtype != DIDEVTYPEKEYBOARD_UNKNOWN,
+            "GetCapabilities invalid device subtype for dwDevType: 0x%08x (%04x:%04x)\n",
+            caps.dwDevType, kbd_type, kbd_subtype);
 
     IUnknown_Release(pKeyboard);
 }
 
+static void test_dik_codes(IDirectInputA *dI, HWND hwnd, LANGID langid)
+{
+    static const struct key2dik
+    {
+        BYTE key, dik, todo;
+    } key2dik_en[] =
+    {
+        {'Q',DIK_Q}, {'W',DIK_W}, {'E',DIK_E}, {'R',DIK_R}, {'T',DIK_T}, {'Y',DIK_Y},
+        {'[',DIK_LBRACKET}, {']',DIK_RBRACKET}, {'.',DIK_PERIOD}
+    },
+    key2dik_fr[] =
+    {
+        {'A',DIK_Q}, {'Z',DIK_W}, {'E',DIK_E}, {'R',DIK_R}, {'T',DIK_T}, {'Y',DIK_Y},
+        {'^',DIK_LBRACKET}, {'$',DIK_RBRACKET}, {':',DIK_PERIOD}
+    },
+    key2dik_de[] =
+    {
+        {'Q',DIK_Q}, {'W',DIK_W}, {'E',DIK_E}, {'R',DIK_R}, {'T',DIK_T}, {'Z',DIK_Y},
+        {'\xfc',DIK_LBRACKET,1}, {'+',DIK_RBRACKET}, {'.',DIK_PERIOD}
+    },
+    key2dik_ja[] =
+    {
+        {'Q',DIK_Q}, {'W',DIK_W}, {'E',DIK_E}, {'R',DIK_R}, {'T',DIK_T}, {'Y',DIK_Y},
+        {'@',DIK_AT}, {']',DIK_RBRACKET}, {'.',DIK_PERIOD}
+    };
+    static const struct
+    {
+        LANGID langid;
+        const struct key2dik *map;
+        DWORD type;
+    } expected[] =
+    {
+        { MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
+          key2dik_en, DIDEVTYPEKEYBOARD_PCENH },
+        { MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH),
+          key2dik_fr, DIDEVTYPEKEYBOARD_PCENH },
+        { MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN),
+          key2dik_de, DIDEVTYPEKEYBOARD_PCENH },
+        { MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN),
+          key2dik_ja, DIDEVTYPEKEYBOARD_JAPAN106 }
+    };
+    const struct key2dik *map = NULL;
+    UINT i;
+    HRESULT hr;
+    IDirectInputDeviceA *device;
+    DIDEVCAPS caps;
+    HKL hkl, hkl_orig;
+    MSG msg;
+
+    for (i = 0; i < ARRAY_SIZE(expected); i++)
+    {
+        if (expected[i].langid == langid)
+        {
+            map = expected[i].map;
+            break;
+        }
+    }
+    ok(map != NULL, "can't find mapping for langid %04x\n", langid);
+    if (!map) return;
+
+    hr = IDirectInput_CreateDevice(dI, &GUID_SysKeyboard, &device, NULL);
+    ok(hr == S_OK, "CreateDevice() failed: %08x\n", hr);
+    hr = IDirectInputDevice_SetDataFormat(device, &c_dfDIKeyboard);
+    ok(hr == S_OK, "SetDataFormat() failed: %08x\n", hr);
+    hr = IDirectInputDevice_Acquire(device);
+    ok(hr == S_OK, "Acquire() failed: %08x\n", hr);
+    caps.dwSize = sizeof( caps );
+    hr = IDirectInputDevice_GetCapabilities(device, &caps);
+    ok(hr == S_OK, "GetDeviceInstance() failed: %08x\n", hr);
+    if (expected[i].type != GET_DIDEVICE_SUBTYPE(caps.dwDevType)) {
+        skip("Keyboard type(%u) doesn't match for lang %04x\n",
+             GET_DIDEVICE_SUBTYPE(caps.dwDevType), langid);
+        goto fail;
+    }
+
+    hkl = activate_keyboard_layout(langid, &hkl_orig);
+    if (!hkl) goto fail;
+
+    SetFocus(hwnd);
+    pump_messages();
+
+    for (i = 0; i < ARRAY_SIZE(key2dik_en); i++)
+    {
+        BYTE kbd_state[256];
+        UINT n;
+        WORD vkey, scan;
+        INPUT in;
+
+        n = VkKeyScanExW(map[i].key, hkl);
+        todo_wine_if(map[i].todo & 1)
+        ok(n != 0xffff, "%u: failed to get virtual key value for %c(%02x)\n", i, map[i].key, map[i].key);
+        vkey = LOBYTE(n);
+        n = MapVirtualKeyExA(vkey, MAPVK_VK_TO_CHAR, hkl) & 0xff;
+        todo_wine_if(map[i].todo & 1)
+        ok(n == map[i].key, "%u: expected %c(%02x), got %c(%02x)\n", i, map[i].key, map[i].key, n, n);
+        scan = MapVirtualKeyExA(vkey, MAPVK_VK_TO_VSC, hkl);
+        /* scan codes match the DIK_ codes on US keyboard.
+           however, it isn't true for symbols and punctuations in other layouts. */
+        if (isalpha(map[i].key) || langid == MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT))
+            ok(scan == map[i].dik, "%u: expected %02x, got %02x\n", i, map[i].dik, n);
+        else
+            todo_wine_if(map[i].todo & 1)
+            ok(scan, "%u: fail to get scan code value, expected %02x (vkey=%02x)\n",
+               i, map[i].dik, vkey);
+
+        in.type = INPUT_KEYBOARD;
+        U(in).ki.wVk = vkey;
+        U(in).ki.wScan = scan;
+        U(in).ki.dwFlags = 0;
+        U(in).ki.dwExtraInfo = 0;
+        U(in).ki.time = 0;
+        n = SendInput(1, &in, sizeof(in));
+        ok(n == 1, "got %u\n", n);
+
+        if (!PeekMessageA(&msg, hwnd, 0, 0, PM_REMOVE))
+        {
+            U(in).ki.dwFlags = KEYEVENTF_KEYUP;
+            SendInput(1, &in, sizeof(in));
+            win_skip("failed to queue keyboard event\n");
+            break;
+        }
+        ok(msg.message == WM_KEYDOWN, "expected WM_KEYDOWN, got %04x\n", msg.message);
+        DispatchMessageA(&msg);
+
+        n = MapVirtualKeyExA(msg.wParam, MAPVK_VK_TO_CHAR, hkl);
+        trace("keydown wParam: %#08lx (%c) lParam: %#08lx, MapVirtualKey(MAPVK_VK_TO_CHAR) = %c\n",
+              msg.wParam, isprint(LOWORD(msg.wParam)) ? LOWORD(msg.wParam) : '?',
+              msg.lParam, isprint(n) ? n : '?');
+
+        pump_messages();
+
+        hr = IDirectInputDevice_GetDeviceState(device, sizeof(kbd_state), kbd_state);
+        ok(hr == S_OK, "GetDeviceState() failed: %08x\n", hr);
+
+        /* this never happens on real hardware but tesbot VMs seem to have timing issues */
+        if (i == 0 && kbd_state[map[0].dik] != 0x80)
+        {
+            win_skip("dinput failed to handle keyboard event\n");
+            break;
+        }
+
+        todo_wine_if(map[i].todo)
+        ok(kbd_state[map[i].dik] == 0x80, "DI key %#x has state %#x\n", map[i].dik, kbd_state[map[i].dik]);
+
+        U(in).ki.dwFlags = KEYEVENTF_KEYUP;
+        n = SendInput(1, &in, sizeof(in));
+        ok(n == 1, "got %u\n", n);
+
+        pump_messages();
+    }
+
+    ActivateKeyboardLayout(hkl_orig, 0);
+    UnloadKeyboardLayout(hkl);
+fail:
+    IDirectInputDevice_Unacquire(device);
+    IUnknown_Release(device);
+}
+
+static void test_GetDeviceInfo(IDirectInputA *pDI)
+{
+    HRESULT hr;
+    IDirectInputDeviceA *pKey = NULL;
+    DIDEVICEINSTANCEA instA;
+    DIDEVICEINSTANCE_DX3A inst3A;
+
+    hr = IDirectInput_CreateDevice(pDI, &GUID_SysKeyboard, &pKey, NULL);
+    ok(SUCCEEDED(hr), "IDirectInput_CreateDevice() failed: %08x\n", hr);
+    if (FAILED(hr)) return;
+
+    instA.dwSize = sizeof(instA);
+    hr = IDirectInputDevice_GetDeviceInfo(pKey, &instA);
+    ok(SUCCEEDED(hr), "got %08x\n", hr);
+
+    inst3A.dwSize = sizeof(inst3A);
+    hr = IDirectInputDevice_GetDeviceInfo(pKey, (DIDEVICEINSTANCEA *)&inst3A);
+    ok(SUCCEEDED(hr), "got %08x\n", hr);
+
+    ok(instA.dwSize != inst3A.dwSize, "got %d, %d \n", instA.dwSize, inst3A.dwSize);
+    ok(IsEqualGUID(&instA.guidInstance, &inst3A.guidInstance), "got %s, %s\n",
+            wine_dbgstr_guid(&instA.guidInstance), wine_dbgstr_guid(&inst3A.guidInstance) );
+    ok(IsEqualGUID(&instA.guidProduct, &inst3A.guidProduct), "got %s, %s\n",
+            wine_dbgstr_guid(&instA.guidProduct), wine_dbgstr_guid(&inst3A.guidProduct) );
+    ok(instA.dwDevType == inst3A.dwDevType, "got %d, %d\n", instA.dwDevType, inst3A.dwDevType);
+
+    IUnknown_Release(pKey);
+}
+
 static void keyboard_tests(DWORD version)
 {
     HRESULT hr;
@@ -233,7 +507,7 @@ static void keyboard_tests(DWORD version)
     HWND hwnd;
     ULONG ref = 0;
 
-    hr = DirectInputCreateA(hInstance, version, &pDI, NULL);
+    hr = pDirectInputCreateA(hInstance, version, &pDI, NULL);
     if (hr == DIERR_OLDDIRECTINPUTVERSION)
     {
         skip("Tests require a newer dinput version\n");
@@ -245,13 +519,22 @@ static void keyboard_tests(DWORD version)
     hwnd = CreateWindowA("static", "Title", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 10, 10, 200, 200,
                          NULL, NULL, NULL, NULL);
     ok(hwnd != NULL, "err: %d\n", GetLastError());
+    SetForegroundWindow( hwnd );
 
     if (hwnd)
     {
+        pump_messages();
+
         acquire_tests(pDI, hwnd);
         test_set_coop(pDI, hwnd);
         test_get_prop(pDI, hwnd);
         test_capabilities(pDI, hwnd);
+        test_GetDeviceInfo(pDI);
+
+        test_dik_codes(pDI, hwnd, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT));
+        test_dik_codes(pDI, hwnd, MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH));
+        test_dik_codes(pDI, hwnd, MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN));
+        test_dik_codes(pDI, hwnd, MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN));
     }
 
     DestroyWindow(hwnd);
@@ -261,6 +544,8 @@ static void keyboard_tests(DWORD version)
 
 START_TEST(keyboard)
 {
+    pDirectInputCreateA = (void *)GetProcAddress(GetModuleHandleA("dinput.dll"), "DirectInputCreateA");
+
     CoInitialize(NULL);
 
     keyboard_tests(0x0700);