This repository has been archived on 2024-12-27. You can view files and clone it, but cannot push or open issues or pull requests.
Files
segatools/games/apm3io/di.c

258 lines
6.2 KiB
C

#include <windows.h>
#include <assert.h>
#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 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[APM3_BUTTON_COUNT];
HRESULT apm3_di_init(
const struct apm3_di_config *cfg,
HINSTANCE inst,
const struct apm3_io_backend **backend)
{
HRESULT hr;
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 = DirectInput8Create(
inst,
DIRECTINPUT_VERSION,
&IID_IDirectInput8W,
(void**)&apm3_di_api,
NULL);
if (FAILED(hr)) {
dprintf("DirectInput: DirectInput8Create failed: %08x\n", (int)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 < APM3_BUTTON_COUNT; 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 < APM3_BUTTON_COUNT; 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;
}
}