forked from TeamTofuShop/segatools
swdc: first segatools added
This commit is contained in:
11
swdcio/backend.h
Normal file
11
swdcio/backend.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "swdcio/swdcio.h"
|
||||
|
||||
struct swdc_io_backend {
|
||||
void (*get_opbtns)(uint8_t *opbtn);
|
||||
void (*get_gamebtns)(uint16_t *gamebtn);
|
||||
void (*get_analogs)(struct swdc_io_analog_state *state);
|
||||
};
|
111
swdcio/config.c
Normal file
111
swdcio/config.c
Normal file
@ -0,0 +1,111 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "swdcio/config.h"
|
||||
|
||||
void swdc_di_config_load(struct swdc_di_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
wchar_t key[8];
|
||||
int i;
|
||||
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"dinput",
|
||||
L"deviceName",
|
||||
L"",
|
||||
cfg->device_name,
|
||||
_countof(cfg->device_name),
|
||||
filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"dinput",
|
||||
L"brakeAxis",
|
||||
L"RZ",
|
||||
cfg->brake_axis,
|
||||
_countof(cfg->brake_axis),
|
||||
filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"dinput",
|
||||
L"accelAxis",
|
||||
L"Y",
|
||||
cfg->accel_axis,
|
||||
_countof(cfg->accel_axis),
|
||||
filename);
|
||||
|
||||
cfg->start = GetPrivateProfileIntW(L"dinput", L"start", 0, 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);
|
||||
cfg->wheel_green = GetPrivateProfileIntW(L"dinput", L"wheelGreen", 0, filename);
|
||||
cfg->wheel_red = GetPrivateProfileIntW(L"dinput", L"wheelRed", 0, filename);
|
||||
cfg->wheel_blue = GetPrivateProfileIntW(L"dinput", L"wheelBlue", 0, filename);
|
||||
cfg->wheel_yellow = GetPrivateProfileIntW(L"dinput", L"wheelYellow", 0, filename);
|
||||
|
||||
cfg->reverse_brake_axis = GetPrivateProfileIntW(
|
||||
L"dinput",
|
||||
L"reverseBrakeAxis",
|
||||
0,
|
||||
filename);
|
||||
cfg->reverse_accel_axis = GetPrivateProfileIntW(
|
||||
L"dinput",
|
||||
L"reverseAccelAxis",
|
||||
0,
|
||||
filename);
|
||||
}
|
||||
|
||||
void swdc_xi_config_load(struct swdc_xi_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->single_stick_steering = GetPrivateProfileIntW(
|
||||
L"io4",
|
||||
L"singleStickSteering",
|
||||
0,
|
||||
filename);
|
||||
}
|
||||
|
||||
void swdc_io_config_load(struct swdc_io_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", '1', filename);
|
||||
cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", '2', filename);
|
||||
cfg->vk_coin = GetPrivateProfileIntW(L"io4", L"coin", '3', filename);
|
||||
cfg->restrict_ = GetPrivateProfileIntW(L"io4", L"restrict", 97, filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"io4",
|
||||
L"mode",
|
||||
L"xinput",
|
||||
cfg->mode,
|
||||
_countof(cfg->mode),
|
||||
filename);
|
||||
|
||||
swdc_shifter_config_load(&cfg->shifter, filename);
|
||||
swdc_di_config_load(&cfg->di, filename);
|
||||
swdc_xi_config_load(&cfg->xi, filename);
|
||||
}
|
||||
|
||||
void swdc_shifter_config_load(
|
||||
struct swdc_shifter_config *cfg,
|
||||
const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->auto_neutral = GetPrivateProfileIntW(
|
||||
L"io4",
|
||||
L"autoNeutral",
|
||||
0,
|
||||
filename);
|
||||
}
|
47
swdcio/config.h
Normal file
47
swdcio/config.h
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct swdc_shifter_config {
|
||||
bool auto_neutral;
|
||||
};
|
||||
|
||||
struct swdc_di_config {
|
||||
wchar_t device_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 wheel_green;
|
||||
uint8_t wheel_red;
|
||||
uint8_t wheel_blue;
|
||||
uint8_t wheel_yellow;
|
||||
bool reverse_brake_axis;
|
||||
bool reverse_accel_axis;
|
||||
};
|
||||
|
||||
struct swdc_xi_config {
|
||||
bool single_stick_steering;
|
||||
};
|
||||
|
||||
struct swdc_io_config {
|
||||
uint8_t vk_test;
|
||||
uint8_t vk_service;
|
||||
uint8_t vk_coin;
|
||||
wchar_t mode[8];
|
||||
int restrict_;
|
||||
struct swdc_shifter_config shifter;
|
||||
struct swdc_di_config di;
|
||||
struct swdc_xi_config xi;
|
||||
};
|
||||
|
||||
void swdc_di_config_load(struct swdc_di_config *cfg, const wchar_t *filename);
|
||||
void swdc_xi_config_load(struct swdc_xi_config *cfg, const wchar_t *filename);
|
||||
void swdc_io_config_load(struct swdc_io_config *cfg, const wchar_t *filename);
|
||||
void swdc_shifter_config_load(
|
||||
struct swdc_shifter_config *cfg,
|
||||
const wchar_t *filename);
|
163
swdcio/di-dev.c
Normal file
163
swdcio/di-dev.c
Normal file
@ -0,0 +1,163 @@
|
||||
#include <windows.h>
|
||||
#include <dinput.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "swdcio/di-dev.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
HRESULT swdc_di_dev_start(IDirectInputDevice8W *dev, HWND wnd)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(dev != NULL);
|
||||
assert(wnd != NULL);
|
||||
|
||||
hr = IDirectInputDevice8_SetCooperativeLevel(
|
||||
dev,
|
||||
wnd,
|
||||
DISCL_BACKGROUND | DISCL_EXCLUSIVE);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("DirectInput: SetCooperativeLevel failed: %08x\n", (int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = IDirectInputDevice8_SetDataFormat(dev, &c_dfDIJoystick);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("DirectInput: SetDataFormat failed: %08x\n", (int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = IDirectInputDevice8_Acquire(dev);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("DirectInput: Acquire failed: %08x\n", (int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
void swdc_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out)
|
||||
{
|
||||
/* 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)
|
||||
*/
|
||||
|
||||
IDirectInputEffect *obj;
|
||||
DWORD axis;
|
||||
LONG direction;
|
||||
DIEFFECT fx;
|
||||
DICONSTANTFORCE cf;
|
||||
HRESULT hr;
|
||||
|
||||
assert(dev != NULL);
|
||||
assert(out != NULL);
|
||||
|
||||
*out = NULL;
|
||||
|
||||
dprintf("DirectInput: Starting force feedback (may take a sec)\n");
|
||||
|
||||
axis = DIJOFS_X;
|
||||
direction = 0;
|
||||
|
||||
memset(&cf, 0, sizeof(cf));
|
||||
cf.lMagnitude = 0;
|
||||
|
||||
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(
|
||||
dev,
|
||||
&GUID_ConstantForce,
|
||||
&fx,
|
||||
&obj,
|
||||
NULL);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("DirectInput: DirectInput force feedback unavailable: %08x\n",
|
||||
(int) hr);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
hr = IDirectInputEffect_Start(obj, INFINITE, 0);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
IDirectInputEffect_Release(obj);
|
||||
dprintf("DirectInput: DirectInput force feedback start failed: %08x\n",
|
||||
(int) hr);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
*out = obj;
|
||||
|
||||
dprintf("DirectInput: Force feedback initialized and set to zero\n");
|
||||
}
|
||||
|
||||
HRESULT swdc_di_dev_poll(
|
||||
IDirectInputDevice8W *dev,
|
||||
HWND wnd,
|
||||
union swdc_di_state *out)
|
||||
{
|
||||
HRESULT hr;
|
||||
MSG msg;
|
||||
|
||||
assert(dev != NULL);
|
||||
assert(wnd != NULL);
|
||||
assert(out != NULL);
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
/* Pump our dummy window's message queue just in case DirectInput or an
|
||||
IHV DirectInput driver somehow relies on it */
|
||||
|
||||
while (PeekMessageW(&msg, wnd, 0, 0, PM_REMOVE)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
hr = IDirectInputDevice8_GetDeviceState(
|
||||
dev,
|
||||
sizeof(out->st),
|
||||
&out->st);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("DirectInput: GetDeviceState 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;
|
||||
}
|
19
swdcio/di-dev.h
Normal file
19
swdcio/di-dev.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <dinput.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
union swdc_di_state {
|
||||
DIJOYSTATE st;
|
||||
uint8_t bytes[sizeof(DIJOYSTATE)];
|
||||
};
|
||||
|
||||
HRESULT swdc_di_dev_start(IDirectInputDevice8W *dev, HWND wnd);
|
||||
void swdc_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out);
|
||||
HRESULT swdc_di_dev_poll(
|
||||
IDirectInputDevice8W *dev,
|
||||
HWND wnd,
|
||||
union swdc_di_state *out);
|
||||
|
420
swdcio/di.c
Normal file
420
swdcio/di.c
Normal file
@ -0,0 +1,420 @@
|
||||
#include <windows.h>
|
||||
#include <dinput.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "swdcio/backend.h"
|
||||
#include "swdcio/config.h"
|
||||
#include "swdcio/di.h"
|
||||
#include "swdcio/di-dev.h"
|
||||
#include "swdcio/swdcio.h"
|
||||
#include "swdcio/wnd.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/str.h"
|
||||
|
||||
struct swdc_di_axis {
|
||||
wchar_t name[4];
|
||||
size_t off;
|
||||
};
|
||||
|
||||
static HRESULT swdc_di_config_apply(const struct swdc_di_config *cfg);
|
||||
static const struct swdc_di_axis *swdc_di_get_axis(const wchar_t *name);
|
||||
static BOOL CALLBACK swdc_di_enum_callback(
|
||||
const DIDEVICEINSTANCEW *dev,
|
||||
void *ctx);
|
||||
static BOOL CALLBACK swdc_di_enum_callback_shifter(
|
||||
const DIDEVICEINSTANCEW *dev,
|
||||
void *ctx);
|
||||
static void swdc_di_get_buttons(uint16_t *gamebtn_out);
|
||||
static uint8_t swdc_di_decode_pov(DWORD pov);
|
||||
static void swdc_di_get_analogs(struct swdc_io_analog_state *out);
|
||||
|
||||
static const struct swdc_di_axis swdc_di_axes[] = {
|
||||
/* Just map DIJOYSTATE for now, we can map DIJOYSTATE2 later if needed */
|
||||
{ .name = L"X", .off = DIJOFS_X },
|
||||
{ .name = L"Y", .off = DIJOFS_Y },
|
||||
{ .name = L"Z", .off = DIJOFS_Z },
|
||||
{ .name = L"RX", .off = DIJOFS_RX },
|
||||
{ .name = L"RY", .off = DIJOFS_RY },
|
||||
{ .name = L"RZ", .off = DIJOFS_RZ },
|
||||
{ .name = L"U", .off = DIJOFS_SLIDER(0) },
|
||||
{ .name = L"V", .off = DIJOFS_SLIDER(1) },
|
||||
};
|
||||
|
||||
static const struct swdc_io_backend swdc_di_backend = {
|
||||
.get_gamebtns = swdc_di_get_buttons,
|
||||
.get_analogs = swdc_di_get_analogs,
|
||||
};
|
||||
|
||||
static HWND swdc_di_wnd;
|
||||
static IDirectInput8W *swdc_di_api;
|
||||
static IDirectInputDevice8W *swdc_di_dev;
|
||||
static IDirectInputEffect *swdc_di_fx;
|
||||
static size_t swdc_di_off_brake;
|
||||
static size_t swdc_di_off_accel;
|
||||
static uint8_t swdc_di_shift_dn;
|
||||
static uint8_t swdc_di_shift_up;
|
||||
static uint8_t swdc_di_view_chg;
|
||||
static uint8_t swdc_di_start;
|
||||
static uint8_t swdc_di_wheel_green;
|
||||
static uint8_t swdc_di_wheel_red;
|
||||
static uint8_t swdc_di_wheel_blue;
|
||||
static uint8_t swdc_di_wheel_yellow;
|
||||
static bool swdc_di_reverse_brake_axis;
|
||||
static bool swdc_di_reverse_accel_axis;
|
||||
|
||||
HRESULT swdc_di_init(
|
||||
const struct swdc_di_config *cfg,
|
||||
HINSTANCE inst,
|
||||
const struct swdc_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 = swdc_di_config_apply(cfg);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = swdc_io_wnd_create(inst, &swdc_di_wnd);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* SWDC has some built-in DirectInput support that is not
|
||||
particularly useful. swdchook shorts this out by redirecting dinput8.dll
|
||||
to a no-op implementation of DirectInput. However, swdcio does need to
|
||||
talk to the real operating system implementation of DirectInput without
|
||||
the stub DLL interfering, so build a path to
|
||||
C:\Windows\System32\dinput.dll here. */
|
||||
|
||||
dll_path[0] = L'\0';
|
||||
path_pos = GetSystemDirectoryW(dll_path, _countof(dll_path));
|
||||
wcscat_s(
|
||||
dll_path + path_pos,
|
||||
_countof(dll_path) - path_pos,
|
||||
L"\\dinput8.dll");
|
||||
|
||||
dinput8 = LoadLibraryW(dll_path);
|
||||
|
||||
if (dinput8 == NULL) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("DirectInput: LoadLibrary failed: %08x\n", (int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
api_entry = (void *) GetProcAddress(dinput8, "DirectInput8Create");
|
||||
|
||||
if (api_entry == NULL) {
|
||||
dprintf("DirectInput: GetProcAddress failed\n");
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
hr = api_entry(
|
||||
inst,
|
||||
DIRECTINPUT_VERSION,
|
||||
&IID_IDirectInput8W,
|
||||
(void **) &swdc_di_api,
|
||||
NULL);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("DirectInput: API create failed: %08x\n", (int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = IDirectInput8_EnumDevices(
|
||||
swdc_di_api,
|
||||
DI8DEVCLASS_GAMECTRL,
|
||||
swdc_di_enum_callback,
|
||||
(void *) cfg,
|
||||
DIEDFL_ATTACHEDONLY);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("DirectInput: EnumDevices failed: %08x\n", (int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (swdc_di_dev == NULL) {
|
||||
dprintf("Wheel: Controller not found\n");
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
hr = swdc_di_dev_start(swdc_di_dev, swdc_di_wnd);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
swdc_di_dev_start_fx(swdc_di_dev, &swdc_di_fx);
|
||||
|
||||
dprintf("DirectInput: Controller initialized\n");
|
||||
|
||||
*backend = &swdc_di_backend;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT swdc_di_config_apply(const struct swdc_di_config *cfg)
|
||||
{
|
||||
const struct swdc_di_axis *brake_axis;
|
||||
const struct swdc_di_axis *accel_axis;
|
||||
int i;
|
||||
|
||||
brake_axis = swdc_di_get_axis(cfg->brake_axis);
|
||||
accel_axis = swdc_di_get_axis(cfg->accel_axis);
|
||||
|
||||
if (brake_axis == NULL) {
|
||||
dprintf("Wheel: Invalid brake axis: %S\n", cfg->brake_axis);
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (accel_axis == NULL) {
|
||||
dprintf("Wheel: Invalid accel axis: %S\n", cfg->accel_axis);
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (cfg->start > 32) {
|
||||
dprintf("Wheel: Invalid start button: %i\n", cfg->start);
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (cfg->view_chg > 32) {
|
||||
dprintf("Wheel: Invalid view change button: %i\n", cfg->view_chg);
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (cfg->shift_dn > 32) {
|
||||
dprintf("Wheel: Invalid shift down button: %i\n", cfg->shift_dn);
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (cfg->shift_up > 32) {
|
||||
dprintf("Wheel: Invalid shift up button: %i\n", cfg->shift_up);
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (cfg->wheel_green > 32) {
|
||||
dprintf("Wheel: Invalid steering wheel green button: %i\n", cfg->wheel_green);
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (cfg->wheel_red > 32) {
|
||||
dprintf("Wheel: Invalid steering wheel red button: %i\n", cfg->wheel_red);
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (cfg->wheel_blue > 32) {
|
||||
dprintf("Wheel: Invalid steering wheel blue button: %i\n", cfg->wheel_blue);
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (cfg->wheel_yellow > 32) {
|
||||
dprintf("Wheel: Invalid steering wheel yellow button: %i\n", cfg->wheel_yellow);
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
/* Print some debug output to make sure config works... */
|
||||
|
||||
dprintf("Wheel: --- Begin configuration ---\n");
|
||||
dprintf("Wheel: Device name . . . . : Contains \"%S\"\n",
|
||||
cfg->device_name);
|
||||
dprintf("Wheel: Brake axis . . . . . . : %S\n", accel_axis->name);
|
||||
dprintf("Wheel: Accelerator axis . . . : %S\n", brake_axis->name);
|
||||
dprintf("Wheel: Start button . . . . . : %i\n", cfg->start);
|
||||
dprintf("Wheel: View Change button . . : %i\n", cfg->view_chg);
|
||||
dprintf("Wheel: Shift Down button . . : %i\n", cfg->shift_dn);
|
||||
dprintf("Wheel: Shift Up button . . . : %i\n", cfg->shift_up);
|
||||
dprintf("Wheel: Steering Green button : %i\n", cfg->wheel_green);
|
||||
dprintf("Wheel: Steering Red button . : %i\n", cfg->wheel_red);
|
||||
dprintf("Wheel: Steering Blue button . : %i\n", cfg->wheel_blue);
|
||||
dprintf("Wheel: Steering Yellow button : %i\n", cfg->wheel_yellow);
|
||||
dprintf("Wheel: Reverse Brake Axis . . : %i\n", cfg->reverse_brake_axis);
|
||||
dprintf("Wheel: Reverse Accel Axis . . : %i\n", cfg->reverse_accel_axis);
|
||||
dprintf("Wheel: --- End configuration ---\n");
|
||||
|
||||
swdc_di_off_brake = accel_axis->off;
|
||||
swdc_di_off_accel = brake_axis->off;
|
||||
swdc_di_start = cfg->start;
|
||||
swdc_di_view_chg = cfg->view_chg;
|
||||
swdc_di_shift_dn = cfg->shift_dn;
|
||||
swdc_di_shift_up = cfg->shift_up;
|
||||
swdc_di_wheel_green = cfg->wheel_green;
|
||||
swdc_di_wheel_red = cfg->wheel_red;
|
||||
swdc_di_wheel_blue = cfg->wheel_blue;
|
||||
swdc_di_wheel_yellow = cfg->wheel_yellow;
|
||||
swdc_di_reverse_brake_axis = cfg->reverse_brake_axis;
|
||||
swdc_di_reverse_accel_axis = cfg->reverse_accel_axis;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static const struct swdc_di_axis *swdc_di_get_axis(const wchar_t *name)
|
||||
{
|
||||
const struct swdc_di_axis *axis;
|
||||
size_t i;
|
||||
|
||||
for (i = 0 ; i < _countof(swdc_di_axes) ; i++) {
|
||||
axis = &swdc_di_axes[i];
|
||||
|
||||
if (wstr_ieq(name, axis->name)) {
|
||||
return axis;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static BOOL CALLBACK swdc_di_enum_callback(
|
||||
const DIDEVICEINSTANCEW *dev,
|
||||
void *ctx)
|
||||
{
|
||||
const struct swdc_di_config *cfg;
|
||||
HRESULT hr;
|
||||
|
||||
cfg = ctx;
|
||||
|
||||
if (wcsstr(dev->tszProductName, cfg->device_name) == NULL) {
|
||||
return DIENUM_CONTINUE;
|
||||
}
|
||||
|
||||
dprintf("Wheel: Using DirectInput device \"%S\"\n", dev->tszProductName);
|
||||
|
||||
hr = IDirectInput8_CreateDevice(
|
||||
swdc_di_api,
|
||||
&dev->guidInstance,
|
||||
&swdc_di_dev,
|
||||
NULL);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("Wheel: CreateDevice failed: %08x\n", (int) hr);
|
||||
}
|
||||
|
||||
return DIENUM_STOP;
|
||||
}
|
||||
|
||||
static void swdc_di_get_buttons(uint16_t *gamebtn_out)
|
||||
{
|
||||
union swdc_di_state state;
|
||||
uint8_t gamebtn;
|
||||
HRESULT hr;
|
||||
|
||||
assert(gamebtn_out != NULL);
|
||||
|
||||
hr = swdc_di_dev_poll(swdc_di_dev, swdc_di_wnd, &state);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
gamebtn = swdc_di_decode_pov(state.st.rgdwPOV[0]);
|
||||
|
||||
if (swdc_di_start && state.st.rgbButtons[swdc_di_start - 1]) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_START;
|
||||
}
|
||||
|
||||
if (swdc_di_view_chg && state.st.rgbButtons[swdc_di_view_chg - 1]) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_VIEW_CHANGE;
|
||||
}
|
||||
|
||||
if (swdc_di_shift_dn && state.st.rgbButtons[swdc_di_shift_dn - 1]) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_STEERING_PADDLE_LEFT;
|
||||
}
|
||||
|
||||
if (swdc_di_shift_up && state.st.rgbButtons[swdc_di_shift_up - 1]) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_STEERING_PADDLE_RIGHT;
|
||||
}
|
||||
|
||||
if (swdc_di_wheel_green && state.st.rgbButtons[swdc_di_wheel_green - 1]) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_STEERING_GREEN;
|
||||
}
|
||||
|
||||
if (swdc_di_wheel_red && state.st.rgbButtons[swdc_di_wheel_red - 1]) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_STEERING_RED;
|
||||
}
|
||||
|
||||
if (swdc_di_wheel_blue && state.st.rgbButtons[swdc_di_wheel_blue - 1]) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_STEERING_BLUE;
|
||||
}
|
||||
|
||||
if (swdc_di_wheel_yellow && state.st.rgbButtons[swdc_di_wheel_yellow - 1]) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_STEERING_YELLOW;
|
||||
}
|
||||
|
||||
*gamebtn_out = gamebtn;
|
||||
}
|
||||
|
||||
static uint8_t swdc_di_decode_pov(DWORD pov)
|
||||
{
|
||||
switch (pov) {
|
||||
case 0: return SWDC_IO_GAMEBTN_UP;
|
||||
case 4500: return SWDC_IO_GAMEBTN_UP | SWDC_IO_GAMEBTN_RIGHT;
|
||||
case 9000: return SWDC_IO_GAMEBTN_RIGHT;
|
||||
case 13500: return SWDC_IO_GAMEBTN_RIGHT | SWDC_IO_GAMEBTN_DOWN;
|
||||
case 18000: return SWDC_IO_GAMEBTN_DOWN;
|
||||
case 22500: return SWDC_IO_GAMEBTN_DOWN | SWDC_IO_GAMEBTN_RIGHT;
|
||||
case 27000: return SWDC_IO_GAMEBTN_LEFT;
|
||||
case 31500: return SWDC_IO_GAMEBTN_LEFT | SWDC_IO_GAMEBTN_UP;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void swdc_di_get_analogs(struct swdc_io_analog_state *out)
|
||||
{
|
||||
union swdc_di_state state;
|
||||
const LONG *brake;
|
||||
const LONG *accel;
|
||||
HRESULT hr;
|
||||
|
||||
assert(out != NULL);
|
||||
|
||||
hr = swdc_di_dev_poll(swdc_di_dev, swdc_di_wnd, &state);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
brake = (LONG *) &state.bytes[swdc_di_off_brake];
|
||||
accel = (LONG *) &state.bytes[swdc_di_off_accel];
|
||||
|
||||
out->wheel = state.st.lX - 32768;
|
||||
|
||||
if (swdc_di_reverse_brake_axis) {
|
||||
out->brake = *brake;
|
||||
} else {
|
||||
out->brake = 65535 - *brake;
|
||||
}
|
||||
|
||||
if (swdc_di_reverse_accel_axis) {
|
||||
out->accel = *accel;
|
||||
} else {
|
||||
out->accel = 65535 - *accel;
|
||||
}
|
||||
}
|
9
swdcio/di.h
Normal file
9
swdcio/di.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "swdcio/backend.h"
|
||||
#include "swdcio/config.h"
|
||||
|
||||
HRESULT swdc_di_init(
|
||||
const struct swdc_di_config *cfg,
|
||||
HINSTANCE inst,
|
||||
const struct swdc_io_backend **backend);
|
112
swdcio/dllmain.c
Normal file
112
swdcio/dllmain.c
Normal file
@ -0,0 +1,112 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "swdcio/backend.h"
|
||||
#include "swdcio/config.h"
|
||||
#include "swdcio/di.h"
|
||||
#include "swdcio/swdcio.h"
|
||||
#include "swdcio/xi.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static struct swdc_io_config swdc_io_cfg;
|
||||
static const struct swdc_io_backend *swdc_io_backend;
|
||||
static bool swdc_io_coin;
|
||||
|
||||
uint16_t swdc_io_get_api_version(void)
|
||||
{
|
||||
return 0x0100;
|
||||
}
|
||||
|
||||
HRESULT swdc_io_init(void)
|
||||
{
|
||||
HINSTANCE inst;
|
||||
HRESULT hr;
|
||||
|
||||
assert(swdc_io_backend == NULL);
|
||||
|
||||
inst = GetModuleHandleW(NULL);
|
||||
|
||||
if (inst == NULL) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("GetModuleHandleW failed: %lx\n", hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
swdc_io_config_load(&swdc_io_cfg, L".\\segatools.ini");
|
||||
|
||||
if (wstr_ieq(swdc_io_cfg.mode, L"dinput")) {
|
||||
hr = swdc_di_init(&swdc_io_cfg.di, inst, &swdc_io_backend);
|
||||
} else if (wstr_ieq(swdc_io_cfg.mode, L"xinput")) {
|
||||
hr = swdc_xi_init(&swdc_io_cfg.xi, &swdc_io_backend);
|
||||
} else {
|
||||
hr = E_INVALIDARG;
|
||||
dprintf("swdc IO: Invalid IO mode \"%S\", use dinput or xinput\n",
|
||||
swdc_io_cfg.mode);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
void swdc_io_get_opbtns(uint8_t *opbtn_out)
|
||||
{
|
||||
uint8_t opbtn;
|
||||
|
||||
assert(swdc_io_backend != NULL);
|
||||
assert(opbtn_out != NULL);
|
||||
|
||||
opbtn = 0;
|
||||
|
||||
if (GetAsyncKeyState(swdc_io_cfg.vk_test) & 0x8000) {
|
||||
opbtn |= SWDC_IO_OPBTN_TEST;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(swdc_io_cfg.vk_service) & 0x8000) {
|
||||
opbtn |= SWDC_IO_OPBTN_SERVICE;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(swdc_io_cfg.vk_coin) & 0x8000) {
|
||||
if (!swdc_io_coin) {
|
||||
swdc_io_coin = true;
|
||||
opbtn |= SWDC_IO_OPBTN_COIN;
|
||||
}
|
||||
} else {
|
||||
swdc_io_coin = false;
|
||||
}
|
||||
|
||||
*opbtn_out = opbtn;
|
||||
}
|
||||
|
||||
|
||||
void swdc_io_get_gamebtns(uint16_t *gamebtn_out)
|
||||
{
|
||||
assert(swdc_io_backend != NULL);
|
||||
assert(gamebtn_out != NULL);
|
||||
|
||||
swdc_io_backend->get_gamebtns(gamebtn_out);
|
||||
}
|
||||
|
||||
void swdc_io_get_analogs(struct swdc_io_analog_state *out)
|
||||
{
|
||||
struct swdc_io_analog_state tmp;
|
||||
|
||||
assert(out != NULL);
|
||||
assert(swdc_io_backend != NULL);
|
||||
|
||||
swdc_io_backend->get_analogs(&tmp);
|
||||
|
||||
/* Apply steering wheel restriction. Real cabs only report about 77% of
|
||||
the IO-3's max ADC output value when the wheel is turned to either of
|
||||
its maximum positions. To match this behavior we set the default value
|
||||
for the wheel restriction config parameter to 97 (out of 128). This
|
||||
scaling factor is applied using fixed-point arithmetic below. */
|
||||
|
||||
out->wheel = (tmp.wheel * swdc_io_cfg.restrict_) / 128;
|
||||
out->accel = tmp.accel;
|
||||
out->brake = tmp.brake;
|
||||
}
|
30
swdcio/meson.build
Normal file
30
swdcio/meson.build
Normal file
@ -0,0 +1,30 @@
|
||||
swdcio_lib = static_library(
|
||||
'swdccio',
|
||||
name_prefix : '',
|
||||
include_directories : inc,
|
||||
implicit_include_directories : false,
|
||||
c_pch : '../precompiled.h',
|
||||
dependencies : [
|
||||
dinput8_lib,
|
||||
dxguid_lib,
|
||||
xinput_lib,
|
||||
],
|
||||
link_with : [
|
||||
util_lib,
|
||||
],
|
||||
sources : [
|
||||
'backend.h',
|
||||
'config.c',
|
||||
'config.h',
|
||||
'di.c',
|
||||
'di.h',
|
||||
'di-dev.c',
|
||||
'di-dev.h',
|
||||
'dllmain.c',
|
||||
'swdcio.h',
|
||||
'wnd.c',
|
||||
'wnd.h',
|
||||
'xi.c',
|
||||
'xi.h',
|
||||
],
|
||||
)
|
8
swdcio/swdcio.def
Normal file
8
swdcio/swdcio.def
Normal file
@ -0,0 +1,8 @@
|
||||
LIBRARY swdcio
|
||||
|
||||
EXPORTS
|
||||
swdc_io_init
|
||||
swdc_io_poll
|
||||
swdc_io_get_opbtns
|
||||
swdc_io_get_gamebtns
|
||||
swdc_io_get_analogs
|
98
swdcio/swdcio.h
Normal file
98
swdcio/swdcio.h
Normal file
@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum {
|
||||
SWDC_IO_OPBTN_TEST = 0x01,
|
||||
SWDC_IO_OPBTN_SERVICE = 0x02,
|
||||
SWDC_IO_OPBTN_COIN = 0x04,
|
||||
};
|
||||
|
||||
enum {
|
||||
SWDC_IO_GAMEBTN_UP = 0x01,
|
||||
SWDC_IO_GAMEBTN_DOWN = 0x02,
|
||||
SWDC_IO_GAMEBTN_LEFT = 0x04,
|
||||
SWDC_IO_GAMEBTN_RIGHT = 0x08,
|
||||
SWDC_IO_GAMEBTN_START = 0x10,
|
||||
SWDC_IO_GAMEBTN_VIEW_CHANGE = 0x20,
|
||||
|
||||
SWDC_IO_GAMEBTN_STEERING_BLUE = 0x40,
|
||||
SWDC_IO_GAMEBTN_STEERING_GREEN = 0x80,
|
||||
SWDC_IO_GAMEBTN_STEERING_RED = 0x100,
|
||||
SWDC_IO_GAMEBTN_STEERING_YELLOW = 0x200,
|
||||
SWDC_IO_GAMEBTN_STEERING_PADDLE_LEFT = 0x400,
|
||||
SWDC_IO_GAMEBTN_STEERING_PADDLE_RIGHT = 0x800,
|
||||
};
|
||||
|
||||
struct swdc_io_analog_state {
|
||||
/* Current steering wheel position, where zero is the centered position.
|
||||
|
||||
The game will accept any signed 16-bit position value, however a real
|
||||
cabinet will report a value of approximately +/- 25230 when the wheel
|
||||
is at full lock. Steering wheel positions of a magnitude greater than
|
||||
this value are not possible on a real cabinet. */
|
||||
|
||||
int16_t wheel;
|
||||
|
||||
/* Current position of the accelerator pedal, where 0 is released. */
|
||||
|
||||
uint16_t accel;
|
||||
|
||||
/* Current position of the brake pedal, where 0 is released. */
|
||||
|
||||
uint16_t brake;
|
||||
};
|
||||
|
||||
/* Get the version of the IDAC IO API that this DLL supports. This
|
||||
function should return a positive 16-bit integer, where the high byte is
|
||||
the major version and the low byte is the minor version (as defined by the
|
||||
Semantic Versioning standard).
|
||||
|
||||
The latest API version as of this writing is 0x0100. */
|
||||
|
||||
uint16_t swdc_io_get_api_version(void);
|
||||
|
||||
/* Initialize the IO DLL. This is the second function that will be called on
|
||||
your DLL, after mu3_io_get_api_version.
|
||||
|
||||
All subsequent calls to this API may originate from arbitrary threads.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
HRESULT swdc_io_init(void);
|
||||
|
||||
/* Send any queued outputs (of which there are currently none, though this may
|
||||
change in subsequent API versions) and retrieve any new inputs.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
HRESULT swdc_io_poll(void);
|
||||
|
||||
/* Get the state of the cabinet's operator buttons as of the last poll. See
|
||||
MU3_IO_OPBTN enum above: this contains bit mask definitions for button
|
||||
states returned in *opbtn. All buttons are active-high.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
void swdc_io_get_opbtns(uint8_t *opbtn);
|
||||
|
||||
/* Get the state of the cabinet's gameplay buttons as of the last poll. See
|
||||
MU3_IO_GAMEBTN enum above for bit mask definitions. Inputs are split into
|
||||
a left hand side set of inputs and a right hand side set of inputs: the bit
|
||||
mappings are the same in both cases.
|
||||
|
||||
All buttons are active-high, even though some buttons' electrical signals
|
||||
on a real cabinet are active-low.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
void swdc_io_get_gamebtns(uint16_t *gamebtn);
|
||||
|
||||
/* Poll the current state of the cabinet's JVS analog inputs. See structure
|
||||
definition above for details.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
void swdc_io_get_analogs(struct swdc_io_analog_state *out);
|
86
swdcio/wnd.c
Normal file
86
swdcio/wnd.c
Normal file
@ -0,0 +1,86 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
/* DirectInput requires a window for correct initialization (and also force
|
||||
feedback), so this source file provides some utilities for creating a
|
||||
generic message-only window. */
|
||||
|
||||
static LRESULT WINAPI swdc_io_wnd_proc(
|
||||
HWND hwnd,
|
||||
UINT msg,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam);
|
||||
|
||||
HRESULT swdc_io_wnd_create(HINSTANCE inst, HWND *out)
|
||||
{
|
||||
HRESULT hr;
|
||||
WNDCLASSEXW wcx;
|
||||
ATOM atom;
|
||||
HWND hwnd;
|
||||
|
||||
assert(inst != NULL); /* We are not an EXE */
|
||||
assert(out != NULL);
|
||||
|
||||
*out = NULL;
|
||||
|
||||
memset(&wcx, 0, sizeof(wcx));
|
||||
wcx.cbSize = sizeof(wcx);
|
||||
wcx.lpfnWndProc = swdc_io_wnd_proc;
|
||||
wcx.hInstance = inst;
|
||||
wcx.lpszClassName = L"SWDCIO";
|
||||
|
||||
atom = RegisterClassExW(&wcx);
|
||||
|
||||
if (atom == 0) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("SWDCIO: RegisterClassExW failed: %08x\n", (int) hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hwnd = CreateWindowExW(
|
||||
0,
|
||||
(wchar_t *) (intptr_t) atom,
|
||||
L"",
|
||||
0,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
HWND_MESSAGE,
|
||||
NULL,
|
||||
inst,
|
||||
NULL);
|
||||
|
||||
if (hwnd == NULL) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("SWDCIO: CreateWindowExW failed: %08x\n", (int) hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*out = hwnd;
|
||||
|
||||
return S_OK;
|
||||
|
||||
fail:
|
||||
UnregisterClassW((wchar_t *) (intptr_t) atom, inst);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static LRESULT WINAPI swdc_io_wnd_proc(
|
||||
HWND hwnd,
|
||||
UINT msg,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam)
|
||||
{
|
||||
switch (msg) {
|
||||
default:
|
||||
return DefWindowProcW(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
}
|
5
swdcio/wnd.h
Normal file
5
swdcio/wnd.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
HRESULT swdc_io_wnd_create(HINSTANCE inst, HWND *out);
|
165
swdcio/xi.c
Normal file
165
swdcio/xi.c
Normal file
@ -0,0 +1,165 @@
|
||||
#include <windows.h>
|
||||
#include <xinput.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "swdcio/backend.h"
|
||||
#include "swdcio/config.h"
|
||||
#include "swdcio/swdcio.h"
|
||||
#include "swdcio/xi.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static void swdc_xi_get_gamebtns(uint16_t *gamebtn_out);
|
||||
static void swdc_xi_get_analogs(struct swdc_io_analog_state *out);
|
||||
|
||||
static HRESULT swdc_xi_config_apply(const struct swdc_xi_config *cfg);
|
||||
|
||||
static const struct swdc_io_backend swdc_xi_backend = {
|
||||
.get_gamebtns = swdc_xi_get_gamebtns,
|
||||
.get_analogs = swdc_xi_get_analogs,
|
||||
};
|
||||
|
||||
static bool swdc_xi_single_stick_steering;
|
||||
|
||||
HRESULT swdc_xi_init(const struct swdc_xi_config *cfg, const struct swdc_io_backend **backend)
|
||||
{
|
||||
HRESULT hr;
|
||||
assert(cfg != NULL);
|
||||
assert(backend != NULL);
|
||||
|
||||
hr = swdc_xi_config_apply(cfg);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
dprintf("XInput: Using XInput controller\n");
|
||||
*backend = &swdc_xi_backend;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT swdc_io_poll(void)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT swdc_xi_config_apply(const struct swdc_xi_config *cfg)
|
||||
{
|
||||
dprintf("XInput: --- Begin configuration ---\n");
|
||||
dprintf("XInput: Single Stick Steering : %i\n", cfg->single_stick_steering);
|
||||
dprintf("XInput: --- End configuration ---\n");
|
||||
|
||||
swdc_xi_single_stick_steering = cfg->single_stick_steering;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static void swdc_xi_get_gamebtns(uint16_t *gamebtn_out)
|
||||
{
|
||||
uint16_t gamebtn;
|
||||
XINPUT_STATE xi;
|
||||
WORD xb;
|
||||
|
||||
assert(gamebtn_out != NULL);
|
||||
|
||||
gamebtn = 0;
|
||||
|
||||
memset(&xi, 0, sizeof(xi));
|
||||
XInputGetState(0, &xi);
|
||||
xb = xi.Gamepad.wButtons;
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_DPAD_UP) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_UP;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_DPAD_DOWN) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_DOWN;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_DPAD_LEFT) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_LEFT;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_DPAD_RIGHT) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_RIGHT;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_START) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_START;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_BACK) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_VIEW_CHANGE;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_A) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_STEERING_GREEN;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_B) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_STEERING_RED;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_X) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_STEERING_BLUE;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_Y) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_STEERING_YELLOW;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_LEFT_SHOULDER) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_STEERING_PADDLE_LEFT;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_RIGHT_SHOULDER) {
|
||||
gamebtn |= SWDC_IO_GAMEBTN_STEERING_PADDLE_RIGHT;
|
||||
}
|
||||
|
||||
*gamebtn_out = gamebtn;
|
||||
}
|
||||
|
||||
static void swdc_xi_get_analogs(struct swdc_io_analog_state *out)
|
||||
{
|
||||
XINPUT_STATE xi;
|
||||
int left;
|
||||
int right;
|
||||
|
||||
assert(out != NULL);
|
||||
|
||||
memset(&xi, 0, sizeof(xi));
|
||||
XInputGetState(0, &xi);
|
||||
|
||||
left = xi.Gamepad.sThumbLX;
|
||||
|
||||
if (left < -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) {
|
||||
left += XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE;
|
||||
} else if (left > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) {
|
||||
left -= XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE;
|
||||
} else {
|
||||
left = 0;
|
||||
}
|
||||
|
||||
right = xi.Gamepad.sThumbRX;
|
||||
|
||||
if (right < -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) {
|
||||
right += XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE;
|
||||
} else if (right > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) {
|
||||
right -= XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE;
|
||||
} else {
|
||||
right = 0;
|
||||
}
|
||||
|
||||
if (swdc_xi_single_stick_steering) {
|
||||
out->wheel = left;
|
||||
} else {
|
||||
out->wheel = (left + right) / 2;
|
||||
}
|
||||
|
||||
out->accel = xi.Gamepad.bRightTrigger << 8;
|
||||
out->brake = xi.Gamepad.bLeftTrigger << 8;
|
||||
}
|
10
swdcio/xi.h
Normal file
10
swdcio/xi.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
/* Can't call this xinput.h or it will conflict with <xinput.h> */
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "swdcio/backend.h"
|
||||
#include "swdcio/config.h"
|
||||
|
||||
HRESULT swdc_xi_init(const struct swdc_xi_config *cfg, const struct swdc_io_backend **backend);
|
Reference in New Issue
Block a user