Skip to content

Commit

Permalink
#353 XInput support
Browse files Browse the repository at this point in the history
  • Loading branch information
XProger committed Jun 20, 2022
1 parent 18566e1 commit daa0e35
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 7 deletions.
6 changes: 4 additions & 2 deletions src/fixed/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -639,8 +639,10 @@ enum InputKey {
IK_Z = (1 << 9),
IK_L = (1 << 10),
IK_R = (1 << 11),
IK_START = (1 << 12),
IK_SELECT = (1 << 13)
IK_LT = (1 << 12),
IK_RT = (1 << 13),
IK_START = (1 << 14),
IK_SELECT = (1 << 15),
};

// action keys (ItemObj::input)
Expand Down
9 changes: 8 additions & 1 deletion src/fixed/lara.h
Original file line number Diff line number Diff line change
Expand Up @@ -2745,7 +2745,7 @@ struct Lara : ItemObj
if (keys & IK_X) input |= IN_WALK;
if (keys & IK_Y) input |= IN_UP | IN_DOWN;
if (keys & IK_Z) input |= IN_LOOK;
#elif defined(__GBA__) || defined(__GBA_WIN__) || defined(__WIN32__)
#elif defined(__GBA__) || defined(__GBA_WIN__)
int32 ikA, ikB;

if (gSettings.controls_swap) {
Expand Down Expand Up @@ -2793,6 +2793,13 @@ struct Lara : ItemObj
if (keys & IK_Y) input |= IN_JUMP;
if (keys & IK_L) input |= IN_LOOK;
if (keys & IK_R) input |= IN_WALK;
#elif defined(__WIN32__)
if (keys & IK_A) input |= IN_ACTION;
if (keys & IK_B) input |= IN_UP | IN_DOWN;
if (keys & IK_Y) input |= IN_WEAPON;
if (keys & IK_X) input |= IN_JUMP;
if (keys & IK_L) input |= IN_LOOK;
if (keys & IK_R) input |= IN_WALK;
#endif

if (keys & IK_LEFT) input |= IN_LEFT;
Expand Down
211 changes: 207 additions & 4 deletions src/platform/win_fixed/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,19 @@ HDC hDC;
LARGE_INTEGER g_timer;
LARGE_INTEGER g_current;

LARGE_INTEGER gTimerFreq;
LARGE_INTEGER gTimerStart;

void osSetPalette(const uint16* palette)
{
//
}

int32 osGetSystemTimeMS()
{
return GetTickCount();
LARGE_INTEGER count;
QueryPerformanceCounter(&count);
return int32((count.QuadPart - gTimerStart.QuadPart) * 1000L / gTimerFreq.QuadPart);
}

bool osSaveSettings()
Expand Down Expand Up @@ -90,8 +95,188 @@ bool osLoadGame()
return true;
}


#define INPUT_JOY_COUNT 4

#define USE_GAMEPAD_XINPUT

// gamepad
#ifdef USE_GAMEPAD_XINPUT
typedef struct _XINPUT_GAMEPAD {
WORD wButtons;
BYTE bLeftTrigger;
BYTE bRightTrigger;
SHORT sThumbLX;
SHORT sThumbLY;
SHORT sThumbRX;
SHORT sThumbRY;
} XINPUT_GAMEPAD, * PXINPUT_GAMEPAD;

typedef struct _XINPUT_STATE {
DWORD dwPacketNumber;
XINPUT_GAMEPAD Gamepad;
} XINPUT_STATE, * PXINPUT_STATE;

typedef struct _XINPUT_VIBRATION {
WORD wLeftMotorSpeed;
WORD wRightMotorSpeed;
} XINPUT_VIBRATION, * PXINPUT_VIBRATION;

#define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE 7849
#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689
#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD 30

DWORD(WINAPI* _XInputGetState) (DWORD dwUserIndex, XINPUT_STATE* pState);
DWORD(WINAPI* _XInputSetState) (DWORD dwUserIndex, XINPUT_VIBRATION* pVibration);
void (WINAPI* _XInputEnable) (BOOL enable);

#define JOY_DEAD_ZONE_TRIGGER 0.01f
#define JOY_MIN_UPDATE_FX_TIME 50

struct JoyDevice {
int32 vL, vR; // current value for left/right motor vibration
int32 oL, oR; // last applied value
int32 time; // time when we can send vibration update
int32 mask; // buttons mask
bool ready;
} joyDevice[INPUT_JOY_COUNT];

bool osJoyReady(int index) {
return joyDevice[index].ready;
}

void osJoyVibrate(int32 index, int32 L, int32 R)
{
joyDevice[index].vL = L;
joyDevice[index].vR = R;
}

void joyRumble(int index)
{
if (!_XInputSetState)
return;

JoyDevice& joy = joyDevice[index];

if (!joy.ready)
return;

if ((joy.vL == joy.oL) && (joy.vR == joy.oR))
return;

if (osGetSystemTimeMS() < joy.time)
return;

XINPUT_VIBRATION vibration;
vibration.wLeftMotorSpeed = WORD(joy.vL << 8);
vibration.wRightMotorSpeed = WORD(joy.vR << 8);
_XInputSetState(index, &vibration);
joy.oL = joy.vL;
joy.oR = joy.vR;
joy.time = osGetSystemTimeMS() + JOY_MIN_UPDATE_FX_TIME;
}

void inputInit() {
memset(joyDevice, 0, sizeof(joyDevice));

HMODULE h = LoadLibrary("xinput1_3.dll");
if (h == NULL) {
h = LoadLibrary("xinput9_1_0.dll");
}

if (!h)
return;

#define GetProcAddr(lib, x) (x = (decltype(x))GetProcAddress(lib, #x + 1))

GetProcAddr(h, _XInputGetState);
GetProcAddr(h, _XInputSetState);
GetProcAddr(h, _XInputEnable);

for (int i = 0; i < INPUT_JOY_COUNT; i++)
{
XINPUT_STATE state;
int res = _XInputGetState(i, &state);
joyDevice[i].ready = (_XInputGetState(i, &state) == ERROR_SUCCESS);

if (joyDevice[i].ready)
LOG("Gamepad %d is ready\n", i + 1);
}
}

void inputFree()
{
memset(joyDevice, 0, sizeof(joyDevice));
}

void inputUpdate()
{
if (!_XInputGetState)
return;

for (int i = 0; i < INPUT_JOY_COUNT; i++)
{
if (!joyDevice[i].ready)
continue;

joyRumble(i);

XINPUT_STATE state;
if (_XInputGetState(i, &state) != ERROR_SUCCESS)
{
inputFree();
inputInit();
break;
}

static const InputKey buttons[] = { IK_UP, IK_DOWN, IK_LEFT, IK_RIGHT, IK_START, IK_SELECT, IK_NONE, IK_NONE, IK_L, IK_R, IK_NONE, IK_NONE, IK_A, IK_B, IK_X, IK_Y };

int32 curMask = state.Gamepad.wButtons;
int32 oldMask = joyDevice[i].mask;

for (int i = 0; i < 16; i++)
{
bool wasDown = (oldMask & (1 << i)) != 0;
bool isDown = (curMask & (1 << i)) != 0;

if (isDown == wasDown)
continue;

if (isDown && !wasDown) {
keys |= buttons[i];
} else {
keys &= ~buttons[i];
}
}

joyDevice[i].mask = curMask;


//osJoyVibrate(j, state.Gamepad.bLeftTrigger / 255.0f, state.Gamepad.bRightTrigger / 255.0f); // vibration test

/*
Input::setJoyPos(j, jkL, joyDir(joyAxis(state.Gamepad.sThumbLX, -32768, 32767),
joyAxis(-state.Gamepad.sThumbLY, -32768, 32767)));
Input::setJoyPos(j, jkR, joyDir(joyAxis(state.Gamepad.sThumbRX, -32768, 32767),
joyAxis(-state.Gamepad.sThumbRY, -32768, 32767)));
Input::setJoyPos(j, jkLT, vec2(state.Gamepad.bLeftTrigger / 255.0f, 0.0f));
Input::setJoyPos(j, jkRT, vec2(state.Gamepad.bRightTrigger / 255.0f, 0.0f));
*/




}
}
#elif defined(USE_GAMEPAD_WINMM)

#else

void osJoyVibrate(int32 index, int32 L, int32 R) {}

#endif


uint8 soundBuffer[2 * SND_SAMPLES + 32]; // 32 bytes of silence for DMA overrun while interrupt

HWAVEOUT waveOut;
Expand Down Expand Up @@ -197,14 +382,19 @@ LRESULT CALLBACK wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
return 0;
}

void* osLoadLevel(const char* name)
const void* osLoadScreen(LevelID id)
{
return (const void*)1; // TODO
}

const void* osLoadLevel(LevelID id)
{
// level1
char buf[32];

delete[] levelData;

sprintf(buf, "data/%s.PHD", name);
sprintf(buf, "data/%s.PHD", (char*)gLevelInfo[id].data);

FILE *f = fopen(buf, "rb");

Expand All @@ -225,8 +415,17 @@ void* osLoadLevel(const char* name)
return (void*)levelData;
}

// hint to the driver to use discrete GPU
extern "C" {
__declspec(dllexport) int NvOptimusEnablement = 1; // NVIDIA
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; // AMD
}

int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
QueryPerformanceFrequency(&gTimerFreq);
QueryPerformanceCounter(&gTimerStart);

// int argc = (lpCmdLine && strlen(lpCmdLine)) ? 2 : 1;
// const char* argv[] = { "", lpCmdLine };

Expand All @@ -250,8 +449,9 @@ int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi
ShowWindow(hWnd, SW_SHOWDEFAULT);

soundInit();
inputInit();

gameInit(gLevelInfo[gLevelID].name);
gameInit();

MSG msg;

Expand All @@ -266,6 +466,8 @@ int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi
#ifdef _DEBUG
Sleep(4);
#endif
inputUpdate();

int32 frame = (GetTickCount() - startTime) / 33;
if (GetAsyncKeyState('R')) frame /= 10;

Expand All @@ -280,6 +482,7 @@ int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi
}
} while (msg.message != WM_QUIT);

inputFree();
gameFree();

return 0;
Expand Down

0 comments on commit daa0e35

Please sign in to comment.