/* * Copyright (c) 2011 Lucas Fialho Zawacki * Copyright (c) 2006 Vitaliy Margolen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #define DIRECTINPUT_VERSION 0x0800 #define COBJMACROS #include #include "wine/test.h" #include "windef.h" #include "dinput.h" struct enum_data { LPDIRECTINPUT8 pDI; LPDIACTIONFORMAT lpdiaf; LPDIRECTINPUTDEVICE8 keyboard; LPDIRECTINPUTDEVICE8 mouse; int ndevices; }; /* Dummy GUID */ static const GUID ACTION_MAPPING_GUID = { 0x1, 0x2, 0x3, { 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb } }; enum { DITEST_AXIS, DITEST_BUTTON, DITEST_KEYBOARDSPACE, DITEST_MOUSEBUTTON0, DITEST_YAXIS }; static DIACTION actionMapping[]= { /* axis */ { 0, 0x01008A01 /* DIAXIS_DRIVINGR_STEER */ , 0, { "Steer" } }, /* button */ { 1, 0x01000C01 /* DIBUTTON_DRIVINGR_SHIFTUP */ , 0, { "Upshift" } }, /* keyboard key */ { 2, DIKEYBOARD_SPACE , 0, { "Missile" } }, /* mouse button */ { 3, DIMOUSE_BUTTON0, 0, { "Select" } }, /* mouse axis */ { 4, DIMOUSE_YAXIS, 0, { "Y Axis" } } }; static void test_device_input( LPDIRECTINPUTDEVICE8 lpdid, DWORD event_type, DWORD event, DWORD expected ) { HRESULT hr; DIDEVICEOBJECTDATA obj_data; DWORD data_size = 1; int i; hr = IDirectInputDevice8_Acquire(lpdid); ok (SUCCEEDED(hr), "Failed to acquire device hr=%08x\n", hr); if (event_type == INPUT_KEYBOARD) keybd_event( event, DIK_SPACE, 0, 0); if (event_type == INPUT_MOUSE) mouse_event( event, 0, 0, 0, 0); IDirectInputDevice8_Poll(lpdid); hr = IDirectInputDevice8_GetDeviceData(lpdid, sizeof(obj_data), &obj_data, &data_size, 0); if (data_size != 1) { win_skip("We're not able to inject input into Windows dinput8 with events\n"); return; } ok (obj_data.uAppData == expected, "Retrieval of action failed uAppData=%lu expected=%d\n", obj_data.uAppData, expected); /* Check for buffer owerflow */ for (i = 0; i < 17; i++) if (event_type == INPUT_KEYBOARD) { keybd_event( VK_SPACE, DIK_SPACE, 0, 0); keybd_event( VK_SPACE, DIK_SPACE, KEYEVENTF_KEYUP, 0); } else if (event_type == INPUT_MOUSE) { mouse_event(MOUSEEVENTF_LEFTDOWN, 1, 1, 0, 0); mouse_event(MOUSEEVENTF_LEFTUP, 1, 1, 0, 0); } IDirectInputDevice8_Poll(lpdid); data_size = 1; hr = IDirectInputDevice8_GetDeviceData(lpdid, sizeof(obj_data), &obj_data, &data_size, 0); ok(hr == DI_BUFFEROVERFLOW, "GetDeviceData() failed: %08x\n", hr); data_size = 1; hr = IDirectInputDevice8_GetDeviceData(lpdid, sizeof(obj_data), &obj_data, &data_size, 0); ok(hr == DI_OK && data_size == 1, "GetDeviceData() failed: %08x cnt:%d\n", hr, data_size); } static void test_build_action_map( LPDIRECTINPUTDEVICE8 lpdid, LPDIACTIONFORMAT lpdiaf, int action_index, DWORD expected_type, DWORD expected_inst ) { HRESULT hr; DIACTION *actions; DWORD instance, type, how; GUID assigned_to; DIDEVICEINSTANCEA ddi; ddi.dwSize = sizeof(ddi); IDirectInputDevice_GetDeviceInfo(lpdid, &ddi); hr = IDirectInputDevice8_BuildActionMap(lpdid, lpdiaf, NULL, DIDBAM_INITIALIZE); ok (SUCCEEDED(hr), "BuildActionMap failed hr=%08x\n", hr); actions = lpdiaf->rgoAction; instance = DIDFT_GETINSTANCE(actions[action_index].dwObjID); type = DIDFT_GETTYPE(actions[action_index].dwObjID); how = actions[action_index].dwHow; assigned_to = actions[action_index].guidInstance; ok (how == DIAH_USERCONFIG || how == DIAH_DEFAULT, "Action was not set dwHow=%08x\n", how); ok (instance == expected_inst, "Action not mapped correctly instance=%08x expected=%08x\n", instance, expected_inst); ok (type == expected_type, "Action type not mapped correctly type=%08x expected=%08x\n", type, expected_type); ok (IsEqualGUID(&assigned_to, &ddi.guidInstance), "Action and device GUID do not match action=%d\n", action_index); } static BOOL CALLBACK enumeration_callback( LPCDIDEVICEINSTANCE lpddi, LPDIRECTINPUTDEVICE8 lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef) { HRESULT hr; DIPROPDWORD dp; DIPROPRANGE dpr; struct enum_data *data = pvRef; DWORD cnt; DIDEVICEOBJECTDATA buffer[5]; if (!data) return DIENUM_CONTINUE; data->ndevices++; /* collect the mouse and keyboard */ if (IsEqualGUID(&lpddi->guidInstance, &GUID_SysKeyboard)) { IDirectInputDevice_AddRef(lpdid); data->keyboard = lpdid; ok (dwFlags & DIEDBS_MAPPEDPRI1, "Keyboard should be mapped as pri1 dwFlags=%08x\n", dwFlags); } if (IsEqualGUID(&lpddi->guidInstance, &GUID_SysMouse)) { IDirectInputDevice_AddRef(lpdid); data->mouse = lpdid; ok (dwFlags & DIEDBS_MAPPEDPRI1, "Mouse should be mapped as pri1 dwFlags=%08x\n", dwFlags); } /* Building and setting an action map */ /* It should not use any pre-stored mappings so we use DIDBAM_INITIALIZE */ hr = IDirectInputDevice8_BuildActionMap(lpdid, data->lpdiaf, NULL, DIDBAM_INITIALIZE); ok (SUCCEEDED(hr), "BuildActionMap failed hr=%08x\n", hr); /* Device has no data format and thus can't be acquired */ hr = IDirectInputDevice8_Acquire(lpdid); ok (hr == DIERR_INVALIDPARAM, "Device was acquired before SetActionMap hr=%08x\n", hr); hr = IDirectInputDevice8_SetActionMap(lpdid, data->lpdiaf, NULL, 0); ok (SUCCEEDED(hr), "SetActionMap failed hr=%08x\n", hr); /* Some joysticks may have no suitable actions and thus should not be tested */ if (hr == DI_NOEFFECT) return DIENUM_CONTINUE; /* Test buffer size */ memset(&dp, 0, sizeof(dp)); dp.diph.dwSize = sizeof(dp); dp.diph.dwHeaderSize = sizeof(DIPROPHEADER); dp.diph.dwHow = DIPH_DEVICE; hr = IDirectInputDevice_GetProperty(lpdid, DIPROP_BUFFERSIZE, &dp.diph); ok (SUCCEEDED(hr), "GetProperty failed hr=%08x\n", hr); ok (dp.dwData == data->lpdiaf->dwBufferSize, "SetActionMap must set the buffer, buffersize=%d\n", dp.dwData); cnt = 1; hr = IDirectInputDevice_GetDeviceData(lpdid, sizeof(buffer[0]), buffer, &cnt, 0); ok(hr == DIERR_NOTACQUIRED, "GetDeviceData() failed hr=%08x\n", hr); /* Test axis range */ memset(&dpr, 0, sizeof(dpr)); dpr.diph.dwSize = sizeof(dpr); dpr.diph.dwHeaderSize = sizeof(DIPROPHEADER); dpr.diph.dwHow = DIPH_DEVICE; hr = IDirectInputDevice_GetProperty(lpdid, DIPROP_RANGE, &dpr.diph); /* Only test if device supports the range property */ if (SUCCEEDED(hr)) { ok (dpr.lMin == data->lpdiaf->lAxisMin, "SetActionMap must set the min axis range expected=%d got=%d\n", data->lpdiaf->lAxisMin, dpr.lMin); ok (dpr.lMax == data->lpdiaf->lAxisMax, "SetActionMap must set the max axis range expected=%d got=%d\n", data->lpdiaf->lAxisMax, dpr.lMax); } /* SetActionMap has set the data format so now it should work */ hr = IDirectInputDevice8_Acquire(lpdid); ok (SUCCEEDED(hr), "Acquire failed hr=%08x\n", hr); cnt = 1; hr = IDirectInputDevice_GetDeviceData(lpdid, sizeof(buffer[0]), buffer, &cnt, 0); ok(hr == DI_OK, "GetDeviceData() failed hr=%08x\n", hr); /* SetActionMap should not work on an acquired device */ hr = IDirectInputDevice8_SetActionMap(lpdid, data->lpdiaf, NULL, 0); ok (hr == DIERR_ACQUIRED, "SetActionMap succeeded with an acquired device hr=%08x\n", hr); return DIENUM_CONTINUE; } /* A simpler callback function used to count and check the enumeration of devices. */ static BOOL CALLBACK counting_callback( LPCDIDEVICEINSTANCE lpddi, LPDIRECTINPUTDEVICE8 lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef) { struct enum_data *data = pvRef; if (!data) return DIENUM_CONTINUE; data->ndevices++; if (IsEqualGUID(&lpddi->guidInstance, &GUID_SysKeyboard)) data->keyboard = lpdid; if (IsEqualGUID(&lpddi->guidInstance, &GUID_SysMouse)) data->mouse = lpdid; return DIENUM_CONTINUE; } static void test_action_mapping(void) { HRESULT hr; HINSTANCE hinst = GetModuleHandle(NULL); LPDIRECTINPUT8 pDI = NULL; DIACTIONFORMAT af; struct enum_data data = {pDI, &af, NULL, NULL, 0}; struct enum_data count = {pDI, &af, NULL, NULL, 0}; hr = CoCreateInstance(&CLSID_DirectInput8, 0, 1, &IID_IDirectInput8A, (LPVOID*)&pDI); if (hr == DIERR_OLDDIRECTINPUTVERSION || hr == DIERR_BETADIRECTINPUTVERSION || hr == REGDB_E_CLASSNOTREG) { win_skip("ActionMapping requires dinput8\n"); return; } ok(SUCCEEDED(hr), "DirectInput8 Create failed: hr=%08x\n", hr); if (FAILED(hr)) return; hr = IDirectInput8_Initialize(pDI,hinst, DIRECTINPUT_VERSION); if (hr == DIERR_OLDDIRECTINPUTVERSION || hr == DIERR_BETADIRECTINPUTVERSION) { win_skip("ActionMapping requires dinput8\n"); return; } ok(SUCCEEDED(hr), "DirectInput8 Initialize failed: hr=%08x\n", hr); if (FAILED(hr)) return; memset (&af, 0, sizeof(af)); af.dwSize = sizeof(af); af.dwActionSize = sizeof(DIACTION); af.dwDataSize = 4 * sizeof(actionMapping) / sizeof(actionMapping[0]); af.dwNumActions = sizeof(actionMapping) / sizeof(actionMapping[0]); af.rgoAction = actionMapping; af.guidActionMap = ACTION_MAPPING_GUID; af.dwGenre = 0x01000000; /* DIVIRTUAL_DRIVING_RACE */ af.dwBufferSize = 32; /* Test enumerating all attached and installed devices */ count.keyboard = NULL; count.mouse = NULL; count.ndevices = 0; hr = IDirectInput8_EnumDevicesBySemantics(pDI, 0, &af, counting_callback, &count, DIEDBSFL_ATTACHEDONLY); ok (count.ndevices > 0, "EnumDevicesBySemantics did not call the callback hr=%08x\n", hr); ok (count.keyboard != NULL, "EnumDevicesBySemantics should enumerate the keyboard\n"); ok (count.mouse != NULL, "EnumDevicesBySemantics should enumerate the mouse\n"); /* Enumerate Force feedback devices. We should get no mouse nor keyboard */ count.keyboard = NULL; count.mouse = NULL; hr = IDirectInput8_EnumDevicesBySemantics(pDI, 0, &af, counting_callback, &count, DIEDBSFL_FORCEFEEDBACK); ok (SUCCEEDED(hr), "EnumDevicesBySemantics failed hr=%08x\n", hr); ok (count.keyboard == NULL, "Keyboard should not be enumerated when asking for forcefeedback\n"); ok (count.mouse == NULL, "Mouse should not be enumerated when asking for forcefeedback\n"); /* Enumerate available devices. That is devices with not owned by any user. Before setting the action map for all devices we still have them available. */ count.ndevices = 0; hr = IDirectInput8_EnumDevicesBySemantics(pDI, 0, &af, counting_callback, &count, DIEDBSFL_AVAILABLEDEVICES); ok (SUCCEEDED(hr), "EnumDevicesBySemantics failed hr=%08x\n", hr); ok (count.ndevices > 0, "There should be devices available before action mapping available=%d\n", count.ndevices); /* This enumeration builds and sets the action map for all devices */ hr = IDirectInput8_EnumDevicesBySemantics(pDI, 0, &af, enumeration_callback, &data, DIEDBSFL_ATTACHEDONLY); ok (SUCCEEDED(hr), "EnumDevicesBySemantics failed: hr=%08x\n", hr); /* After a successful action mapping we should have no devices available */ count.ndevices = 0; hr = IDirectInput8_EnumDevicesBySemantics(pDI, 0, &af, counting_callback, &count, DIEDBSFL_AVAILABLEDEVICES); ok (SUCCEEDED(hr), "EnumDevicesBySemantics failed hr=%08x\n", hr); todo_wine ok (count.ndevices == 0, "No device should be available after action mapping available=%d\n", count.ndevices); /* Use the devices we collect for some tests */ if (data.keyboard != NULL) { /* Test keyboard BuildActionMap */ test_build_action_map(data.keyboard, data.lpdiaf, DITEST_KEYBOARDSPACE, DIDFT_PSHBUTTON, DIK_SPACE); /* Test keyboard input */ test_device_input(data.keyboard, INPUT_KEYBOARD, VK_SPACE, 2); /* Test BuildActionMap with no suitable actions for a device */ IDirectInputDevice_Unacquire(data.keyboard); af.dwDataSize = 4 * DITEST_KEYBOARDSPACE; af.dwNumActions = DITEST_KEYBOARDSPACE; hr = IDirectInputDevice8_BuildActionMap(data.keyboard, data.lpdiaf, NULL, DIDBAM_INITIALIZE); ok (hr == DI_NOEFFECT, "BuildActionMap should have no effect with no actions hr=%08x\n", hr); hr = IDirectInputDevice8_SetActionMap(data.keyboard, data.lpdiaf, NULL, 0); ok (hr == DI_NOEFFECT, "SetActionMap should have no effect with no actions to map hr=%08x\n", hr); af.dwDataSize = 4 * sizeof(actionMapping) / sizeof(actionMapping[0]); af.dwNumActions = sizeof(actionMapping) / sizeof(actionMapping[0]); } if (data.mouse != NULL) { /* Test mouse BuildActionMap */ test_build_action_map(data.mouse, data.lpdiaf, DITEST_MOUSEBUTTON0, DIDFT_PSHBUTTON, 0x03); test_build_action_map(data.mouse, data.lpdiaf, DITEST_YAXIS, DIDFT_RELAXIS, 0x01); test_device_input(data.mouse, INPUT_MOUSE, MOUSEEVENTF_LEFTDOWN, 3); } /* The call fails with a zeroed GUID */ memset(&af.guidActionMap, 0, sizeof(GUID)); hr = IDirectInput8_EnumDevicesBySemantics(pDI, 0, &af, enumeration_callback, 0, 0); todo_wine ok(FAILED(hr), "EnumDevicesBySemantics succeeded with invalid GUID hr=%08x\n", hr); } START_TEST(device) { CoInitialize(NULL); test_action_mapping(); CoUninitialize(); }