From 2214374bbd7dc356ee46e9adde97bc6f23488f46 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 29 Sep 2019 16:37:18 -0400 Subject: [PATCH] idzio/di.c: Add positional shifter support --- idzio/config.c | 18 ++++ idzio/config.h | 2 + idzio/di.c | 260 +++++++++++++++++++++++++------------------------ 3 files changed, 153 insertions(+), 127 deletions(-) diff --git a/idzio/config.c b/idzio/config.c index 1636048..2b18ede 100644 --- a/idzio/config.c +++ b/idzio/config.c @@ -2,12 +2,17 @@ #include #include +#include #include +#include #include "idzio/config.h" void idz_di_config_load(struct idz_di_config *cfg, const wchar_t *filename) { + wchar_t key[8]; + int i; + assert(cfg != NULL); assert(filename != NULL); @@ -19,6 +24,14 @@ void idz_di_config_load(struct idz_di_config *cfg, const wchar_t *filename) _countof(cfg->device_name), filename); + GetPrivateProfileStringW( + L"dinput", + L"shifterName", + L"", + cfg->shifter_name, + _countof(cfg->shifter_name), + filename); + GetPrivateProfileStringW( L"dinput", L"brakeAxis", @@ -39,6 +52,11 @@ void idz_di_config_load(struct idz_di_config *cfg, const wchar_t *filename) cfg->view_chg = GetPrivateProfileIntW(L"dinput", L"viewChg", 0, filename); cfg->shift_dn = GetPrivateProfileIntW(L"dinput", L"shiftDn", 0, filename); cfg->shift_up = GetPrivateProfileIntW(L"dinput", L"shiftUp", 0, filename); + + for (i = 0 ; i < 6 ; i++) { + swprintf_s(key, _countof(key), L"gear%i", i + 1); + cfg->gear[i] = GetPrivateProfileIntW(L"dinput", key, i + 1, filename); + } } void idz_io_config_load(struct idz_io_config *cfg, const wchar_t *filename) diff --git a/idzio/config.h b/idzio/config.h index 6beed49..d91b5f3 100644 --- a/idzio/config.h +++ b/idzio/config.h @@ -10,12 +10,14 @@ struct idz_shifter_config { struct idz_di_config { wchar_t device_name[64]; + wchar_t shifter_name[64]; wchar_t brake_axis[16]; wchar_t accel_axis[16]; uint8_t start; uint8_t view_chg; uint8_t shift_dn; uint8_t shift_up; + uint8_t gear[6]; }; struct idz_io_config { diff --git a/idzio/di.c b/idzio/di.c index 357ff31..9208716 100644 --- a/idzio/di.c +++ b/idzio/di.c @@ -8,6 +8,7 @@ #include "idzio/backend.h" #include "idzio/config.h" #include "idzio/di.h" +#include "idzio/di-dev.h" #include "idzio/idzio.h" #include "idzio/shifter.h" #include "idzio/wnd.h" @@ -20,19 +21,17 @@ struct idz_di_axis { size_t off; }; -union idz_di_state { - DIJOYSTATE st; - uint8_t bytes[sizeof(DIJOYSTATE)]; -}; - static HRESULT idz_di_config_apply(const struct idz_di_config *cfg); static const struct idz_di_axis *idz_di_get_axis(const wchar_t *name); static BOOL idz_di_enum_callback(const DIDEVICEINSTANCEW *dev, void *ctx); -static void idz_di_try_fx(void); -static HRESULT idz_di_poll(union idz_di_state *state); +static BOOL idz_di_enum_callback_shifter( + const DIDEVICEINSTANCEW *dev, + void *ctx); static void idz_di_jvs_read_buttons(uint8_t *gamebtn_out); static uint8_t idz_di_decode_pov(DWORD pov); static void idz_di_jvs_read_shifter(uint8_t *gear); +static void idz_di_jvs_read_shifter_pos(uint8_t *gear); +static void idz_di_jvs_read_shifter_virt(uint8_t *gear); static void idz_di_jvs_read_analogs(struct idz_io_analog_state *out); static const struct idz_di_axis idz_di_axes[] = { @@ -56,6 +55,7 @@ static const struct idz_io_backend idz_di_backend = { static HWND idz_di_wnd; static IDirectInput8W *idz_di_api; static IDirectInputDevice8W *idz_di_dev; +static IDirectInputDevice8W *idz_di_shifter; static IDirectInputEffect *idz_di_fx; static size_t idz_di_off_brake; static size_t idz_di_off_accel; @@ -63,6 +63,7 @@ static uint8_t idz_di_shift_dn; static uint8_t idz_di_shift_up; static uint8_t idz_di_view_chg; static uint8_t idz_di_start; +static uint8_t idz_di_gear[6]; HRESULT idz_di_init( const struct idz_di_config *cfg, @@ -109,7 +110,7 @@ HRESULT idz_di_init( if (dinput8 == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); - dprintf("Wheel: LoadLibrary failed: %08x\n", (int) hr); + dprintf("DirectInput: LoadLibrary failed: %08x\n", (int) hr); return hr; } @@ -117,7 +118,7 @@ HRESULT idz_di_init( api_entry = (void *) GetProcAddress(dinput8, "DirectInput8Create"); if (api_entry == NULL) { - dprintf("Wheel: GetProcAddress failed\n"); + dprintf("DirectInput: GetProcAddress failed\n"); return E_FAIL; } @@ -130,7 +131,7 @@ HRESULT idz_di_init( NULL); if (FAILED(hr)) { - dprintf("Wheel: API create failed: %08x\n", (int) hr); + dprintf("DirectInput: API create failed: %08x\n", (int) hr); return hr; } @@ -143,7 +144,7 @@ HRESULT idz_di_init( DIEDFL_ATTACHEDONLY); if (FAILED(hr)) { - dprintf("Wheel: EnumDevices failed: %08x\n", (int) hr); + dprintf("DirectInput: EnumDevices failed: %08x\n", (int) hr); return hr; } @@ -154,35 +155,43 @@ HRESULT idz_di_init( return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); } - hr = IDirectInputDevice8_SetCooperativeLevel( - idz_di_dev, - idz_di_wnd, - DISCL_BACKGROUND | DISCL_EXCLUSIVE); + hr = idz_di_dev_start(idz_di_dev, idz_di_wnd); if (FAILED(hr)) { - dprintf("Wheel: SetCooperativeLevel failed: %08x\n", (int) hr); - return hr; } - hr = IDirectInputDevice8_SetDataFormat(idz_di_dev, &c_dfDIJoystick); + idz_di_dev_start_fx(idz_di_dev, &idz_di_fx); - if (FAILED(hr)) { - dprintf("Wheel: SetDataFormat failed: %08x\n", (int) hr); + if (cfg->shifter_name[0] != L'\0') { + hr = IDirectInput8_EnumDevices( + idz_di_api, + DI8DEVCLASS_GAMECTRL, + idz_di_enum_callback_shifter, + (void *) cfg, + DIEDFL_ATTACHEDONLY); - return hr; + if (FAILED(hr)) { + dprintf("DirectInput: EnumDevices failed: %08x\n", (int) hr); + + return hr; + } + + if (idz_di_dev == NULL) { + dprintf("Shifter: Controller not found\n"); + + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + + hr = idz_di_dev_start(idz_di_shifter, idz_di_wnd); + + if (FAILED(hr)) { + return hr; + } } - hr = IDirectInputDevice8_Acquire(idz_di_dev); + dprintf("DirectInput: Controller initialized\n"); - if (FAILED(hr)) { - dprintf("Wheel: Acquire failed: %08x\n", (int) hr); - - return hr; - } - - dprintf("Wheel: DirectInput initialized\n"); - idz_di_try_fx(); *backend = &idz_di_backend; return S_OK; @@ -192,6 +201,7 @@ static HRESULT idz_di_config_apply(const struct idz_di_config *cfg) { const struct idz_di_axis *brake_axis; const struct idz_di_axis *accel_axis; + int i; brake_axis = idz_di_get_axis(cfg->brake_axis); accel_axis = idz_di_get_axis(cfg->accel_axis); @@ -232,6 +242,16 @@ static HRESULT idz_di_config_apply(const struct idz_di_config *cfg) return E_INVALIDARG; } + for (i = 0 ; i < 6 ; i++) { + if (cfg->gear[i] > 32) { + dprintf("Shifter: Invalid gear %i button: %i\n", + i + 1, + cfg->gear[i]); + + return E_INVALIDARG; + } + } + /* Print some debug output to make sure config works... */ dprintf("Wheel: --- Begin configuration ---\n"); @@ -245,6 +265,20 @@ static HRESULT idz_di_config_apply(const struct idz_di_config *cfg) dprintf("Wheel: Shift Up button . . : %i\n", cfg->shift_up); dprintf("Wheel: --- End configuration ---\n"); + if (cfg->shifter_name[0] != L'\0') { + dprintf("Shifter: --- Begin configuration ---\n"); + dprintf("Shifter: Device name . . . : Contains \"%S\"\n", + cfg->shifter_name); + dprintf("Shifter: Gear buttons . . : %i %i %i %i %i %i\n", + cfg->gear[0], + cfg->gear[1], + cfg->gear[2], + cfg->gear[3], + cfg->gear[4], + cfg->gear[5]); + dprintf("Shifter: --- End configuration ---\n"); + } + idz_di_off_brake = accel_axis->off; idz_di_off_accel = brake_axis->off; idz_di_start = cfg->start; @@ -252,6 +286,10 @@ static HRESULT idz_di_config_apply(const struct idz_di_config *cfg) idz_di_shift_dn = cfg->shift_dn; idz_di_shift_up = cfg->shift_up; + for (i = 0 ; i < 6 ; i++) { + idz_di_gear[i] = cfg->gear[i]; + } + return S_OK; } @@ -274,6 +312,7 @@ static const struct idz_di_axis *idz_di_get_axis(const wchar_t *name) static BOOL idz_di_enum_callback(const DIDEVICEINSTANCEW *dev, void *ctx) { const struct idz_di_config *cfg; + HRESULT hr; cfg = ctx; @@ -283,119 +322,45 @@ static BOOL idz_di_enum_callback(const DIDEVICEINSTANCEW *dev, void *ctx) dprintf("Wheel: Using DirectInput device \"%S\"\n", dev->tszProductName); - IDirectInput8_CreateDevice( + hr = IDirectInput8_CreateDevice( idz_di_api, &dev->guidInstance, &idz_di_dev, NULL); + if (FAILED(hr)) { + dprintf("Wheel: CreateDevice failed: %08x\n", (int) hr); + } + return DIENUM_STOP; } -static void idz_di_try_fx(void) +static BOOL idz_di_enum_callback_shifter( + const DIDEVICEINSTANCEW *dev, + void *ctx) { - /* Set up force-feedback on devices that support it. This is just a stub - for the time being, since we don't yet know how the serial port force - feedback protocol works. - - I'm currently developing with an Xbox One Thrustmaster TMX wheel, if - we don't perform at least some perfunctory FFB initialization of this - nature (or indeed if no DirectInput application is running) then the - wheel exhibits considerable resistance, similar to that of a stationary - car. Changing cf.lMagnitude to a nonzero value does cause the wheel to - continuously turn in the given direction with the given force as one - would expect (max magnitude per DirectInput docs is +/- 10000). - - Failure here is non-fatal, we log any errors and move on. - - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee416353(v=vs.85) - */ - - DWORD axis; - LONG direction; - DIEFFECT fx; - DICONSTANTFORCE cf; + const struct idz_di_config *cfg; HRESULT hr; - dprintf("Wheel: Attempting to start force feedback (may take a sec)\n"); + cfg = ctx; - axis = DIJOFS_X; - direction = 0; + if (wcsstr(dev->tszProductName, cfg->shifter_name) == NULL) { + return DIENUM_CONTINUE; + } - memset(&cf, 0, sizeof(cf)); - cf.lMagnitude = 0; + dprintf("Shifter: Using DirectInput device \"%S\"\n", dev->tszProductName); - memset(&fx, 0, sizeof(fx)); - fx.dwSize = sizeof(fx); - fx.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; - fx.dwDuration = INFINITE; - fx.dwGain = DI_FFNOMINALMAX; - fx.dwTriggerButton = DIEB_NOTRIGGER; - fx.dwTriggerRepeatInterval = INFINITE; - fx.cAxes = 1; - fx.rgdwAxes = &axis; - fx.rglDirection = &direction; - fx.cbTypeSpecificParams = sizeof(cf); - fx.lpvTypeSpecificParams = &cf; - - hr = IDirectInputDevice8_CreateEffect( - idz_di_dev, - &GUID_ConstantForce, - &fx, - &idz_di_fx, + hr = IDirectInput8_CreateDevice( + idz_di_api, + &dev->guidInstance, + &idz_di_shifter, NULL); if (FAILED(hr)) { - dprintf("Wheel: DirectInput force feedback unavailable: %08x\n", - (int) hr); - - return; + dprintf("Shifter: CreateDevice failed: %08x\n", (int) hr); } - hr = IDirectInputEffect_Start(idz_di_fx, INFINITE, 0); - - if (FAILED(hr)) { - dprintf("Wheel: DirectInput force feedback start failed: %08x\n", - (int) hr); - - return; - } - - dprintf("Wheel: Force feedback initialized and set to zero\n"); -} - -static HRESULT idz_di_poll(union idz_di_state *out) -{ - HRESULT hr; - MSG msg; - - memset(out, 0, sizeof(*out)); - - if (idz_di_dev == NULL) { - return E_UNEXPECTED; - } - - /* Pump our dummy window's message queue just in case DirectInput or an - IHV DirectInput driver somehow relies on it */ - - while (PeekMessageW(&msg, idz_di_wnd, 0, 0, PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - hr = IDirectInputDevice8_GetDeviceState( - idz_di_dev, - sizeof(out->st), - &out->st); - - if (FAILED(hr)) { - dprintf("Wheel: I/O error: %08x\n", (int) hr); - } - - /* JVS lacks a protocol for reporting hardware errors from poll command - responses, so this ends up returning zeroed input state instead. */ - - return hr; + return DIENUM_STOP; } static void idz_di_jvs_read_buttons(uint8_t *gamebtn_out) @@ -406,7 +371,7 @@ static void idz_di_jvs_read_buttons(uint8_t *gamebtn_out) assert(gamebtn_out != NULL); - hr = idz_di_poll(&state); + hr = idz_di_dev_poll(idz_di_dev, idz_di_wnd, &state); if (FAILED(hr)) { return; @@ -441,6 +406,47 @@ static uint8_t idz_di_decode_pov(DWORD pov) } static void idz_di_jvs_read_shifter(uint8_t *gear) +{ + assert(gear != NULL); + + if (idz_di_shifter != NULL) { + idz_di_jvs_read_shifter_pos(gear); + } else { + idz_di_jvs_read_shifter_virt(gear); + } +} + +static void idz_di_jvs_read_shifter_pos(uint8_t *out) +{ + union idz_di_state state; + uint8_t btn_no; + uint8_t gear; + uint8_t i; + HRESULT hr; + + assert(out != NULL); + assert(idz_di_shifter != NULL); + + hr = idz_di_dev_poll(idz_di_shifter, idz_di_wnd, &state); + + if (FAILED(hr)) { + return; + } + + gear = 0; + + for (i = 0 ; i < 6 ; i++) { + btn_no = idz_di_gear[i]; + + if (btn_no && state.st.rgbButtons[btn_no - 1]) { + gear = i + 1; + } + } + + *out = gear; +} + +static void idz_di_jvs_read_shifter_virt(uint8_t *gear) { union idz_di_state state; bool shift_dn; @@ -449,7 +455,7 @@ static void idz_di_jvs_read_shifter(uint8_t *gear) assert(gear != NULL); - hr = idz_di_poll(&state); + hr = idz_di_dev_poll(idz_di_dev, idz_di_wnd, &state); if (FAILED(hr)) { return; @@ -481,7 +487,7 @@ static void idz_di_jvs_read_analogs(struct idz_io_analog_state *out) assert(out != NULL); - hr = idz_di_poll(&state); + hr = idz_di_dev_poll(idz_di_dev, idz_di_wnd, &state); if (FAILED(hr)) { return;