#include #include #include #include #include #include #include "apm3io/backend.h" #include "apm3io/config.h" #include "apm3io/di.h" #include "apm3io/di-dev.h" #include "apm3io/apm3io.h" #include "apm3io/wnd.h" #include "util/dprintf.h" #include "util/str.h" struct apm3_di_axis { wchar_t name[4]; size_t off; }; static HRESULT apm3_di_config_apply(const struct apm3_di_config *cfg); static BOOL CALLBACK apm3_di_enum_callback( const DIDEVICEINSTANCEW *dev, void *ctx); static BOOL CALLBACK apm3_di_enum_callback_shifter( const DIDEVICEINSTANCEW *dev, void *ctx); static void apm3_di_get_gamebtns(uint16_t *gamebtn_out); static uint8_t apm3_di_decode_pov(DWORD pov); static const struct apm3_io_backend apm3_di_backend = { .get_gamebtns = apm3_di_get_gamebtns, }; static HWND apm3_di_wnd; static IDirectInput8W *apm3_di_api; static IDirectInputDevice8W *apm3_di_dev; static IDirectInputEffect *apm3_di_fx; static uint8_t apm3_di_home; static uint8_t apm3_di_start; static uint8_t apm3_di_button[8]; HRESULT apm3_di_init( const struct apm3_di_config *cfg, HINSTANCE inst, const struct apm3_io_backend **backend) { HRESULT hr; HMODULE dinput8; HRESULT (WINAPI *api_entry)(HINSTANCE,DWORD,REFIID,LPVOID *,LPUNKNOWN); wchar_t dll_path[MAX_PATH]; UINT path_pos; assert(cfg != NULL); assert(backend != NULL); *backend = NULL; hr = apm3_di_config_apply(cfg); if (FAILED(hr)) { return hr; } hr = apm3_io_wnd_create(inst, &apm3_di_wnd); if (FAILED(hr)) { return hr; } hr = IDirectInput8_EnumDevices( apm3_di_api, DI8DEVCLASS_GAMECTRL, apm3_di_enum_callback, (void *) cfg, DIEDFL_ATTACHEDONLY); if (FAILED(hr)) { dprintf("DirectInput: EnumDevices failed: %08x\n", (int) hr); return hr; } if (apm3_di_dev == NULL) { dprintf("Stick: Controller not found\n"); return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); } hr = apm3_di_dev_start(apm3_di_dev, apm3_di_wnd); if (FAILED(hr)) { return hr; } dprintf("DirectInput: Controller initialized\n"); *backend = &apm3_di_backend; return S_OK; } static HRESULT apm3_di_config_apply(const struct apm3_di_config *cfg) { int i; if (cfg->start > 32) { dprintf("Stick: Invalid start button: %i\n", cfg->start); return E_INVALIDARG; } if (cfg->home > 32) { dprintf("Stick: Invalid home button: %i\n", cfg->home); return E_INVALIDARG; } /* Check all 8 defined buttons */ for (i = 0; i < 8; i++) { if (cfg->button[i] > 32) { dprintf("Stick: Invalid button %i: %i\n", i, cfg->button[i]); return E_INVALIDARG; } } /* Print some debug output to make sure config works... */ dprintf("Stick: --- Begin configuration ---\n"); dprintf("Stick: Device name . . . . : Contains \"%S\"\n", cfg->device_name); dprintf("Stick: Home button . . . : %i\n", cfg->home); dprintf("Stick: Start button . . . : %i\n", cfg->start); /* Print the configuration for all 8 buttons */ for (i = 0; i < 8; i++) { dprintf("Stick: Button %i . . . . . : %i\n", i, cfg->button[i]); } dprintf("Stick: --- End configuration ---\n"); apm3_di_start = cfg->start; apm3_di_home = cfg->home; for (i = 0; i < 8; i++) { apm3_di_button[i] = cfg->button[i]; } return S_OK; } static BOOL CALLBACK apm3_di_enum_callback( const DIDEVICEINSTANCEW *dev, void *ctx) { const struct apm3_di_config *cfg; HRESULT hr; cfg = ctx; if (wcsstr(dev->tszProductName, cfg->device_name) == NULL) { return DIENUM_CONTINUE; } dprintf("Stick: Using DirectInput device \"%S\"\n", dev->tszProductName); hr = IDirectInput8_CreateDevice( apm3_di_api, &dev->guidInstance, &apm3_di_dev, NULL); if (FAILED(hr)) { dprintf("Stick: CreateDevice failed: %08x\n", (int) hr); } return DIENUM_STOP; } static void apm3_di_get_gamebtns(uint16_t *gamebtn_out) { union apm3_di_state state; uint16_t gamebtn; HRESULT hr; assert(gamebtn_out != NULL); hr = apm3_di_dev_poll(apm3_di_dev, apm3_di_wnd, &state); if (FAILED(hr)) { return; } gamebtn = apm3_di_decode_pov(state.st.rgdwPOV[0]); if (apm3_di_start && state.st.rgbButtons[apm3_di_start - 1]) { gamebtn |= APM3_IO_GAMEBTN_START; } if (apm3_di_home && state.st.rgbButtons[apm3_di_home - 1]) { gamebtn |= APM3_IO_GAMEBTN_HOME; } if (apm3_di_button[0] && state.st.rgbButtons[apm3_di_button[0] - 1]) { gamebtn |= APM3_IO_GAMEBTN_B1; } if (apm3_di_button[1] && state.st.rgbButtons[apm3_di_button[1] - 1]) { gamebtn |= APM3_IO_GAMEBTN_B2; } if (apm3_di_button[2] && state.st.rgbButtons[apm3_di_button[2] - 1]) { gamebtn |= APM3_IO_GAMEBTN_B3; } if (apm3_di_button[3] && state.st.rgbButtons[apm3_di_button[3] - 1]) { gamebtn |= APM3_IO_GAMEBTN_B4; } if (apm3_di_button[4] && state.st.rgbButtons[apm3_di_button[4] - 1]) { gamebtn |= APM3_IO_GAMEBTN_B5; } if (apm3_di_button[5] && state.st.rgbButtons[apm3_di_button[5] - 1]) { gamebtn |= APM3_IO_GAMEBTN_B6; } if (apm3_di_button[6] && state.st.rgbButtons[apm3_di_button[6] - 1]) { gamebtn |= APM3_IO_GAMEBTN_B7; } if (apm3_di_button[7] && state.st.rgbButtons[apm3_di_button[7] - 1]) { gamebtn |= APM3_IO_GAMEBTN_B8; } *gamebtn_out = gamebtn; } static uint8_t apm3_di_decode_pov(DWORD pov) { switch (pov) { case 0: return APM3_IO_GAMEBTN_UP; case 4500: return APM3_IO_GAMEBTN_UP | APM3_IO_GAMEBTN_RIGHT; case 9000: return APM3_IO_GAMEBTN_RIGHT; case 13500: return APM3_IO_GAMEBTN_RIGHT | APM3_IO_GAMEBTN_DOWN; case 18000: return APM3_IO_GAMEBTN_DOWN; case 22500: return APM3_IO_GAMEBTN_DOWN | APM3_IO_GAMEBTN_LEFT; case 27000: return APM3_IO_GAMEBTN_LEFT; case 31500: return APM3_IO_GAMEBTN_LEFT | APM3_IO_GAMEBTN_UP; default: return 0; } }