mirror of
https://github.com/hedge-dev/UnleashedRecomp.git
synced 2025-04-28 13:27:58 +03:00

* Initial Linux attempt. * Add clang toolchain & make tools compile. * vcpkg as submodule. * First implementation of IO rewrite. (#31) * Fix directory iteration resolving symlinks. * Refactor kernel objects to be lock-free. * Implement guest critical sections using std::atomic. * Make D3D12 support optional. (#33) * Make D3D12 support optional. * Update ShaderRecomp, fix macros. * Replace QueryPerformanceCounter. (#35) * Add Linux home path for GetUserPath(). (#36) * Cross-platform Sleep. (#37) * Add mmap implementations for virtual allocation. (#38) * Cross-platform TLS. (#34) * Cross-platform TLS. * Fix front() to back(), use Mutex. * Fix global variable namings. --------- Co-authored-by: Skyth <19259897+blueskythlikesclouds@users.noreply.github.com> * Unicode support. (#39) * Replace CreateDirectoryA with Unicode version. * Cross platform thread implementation. (#41) * Cross-platform thread implementation. * Put set thread name calls behind a Win32 macro. * Cross-platform semaphore implementation. (#43) * xam: use SDL for keyboard input * Cross-platform atomic operations. (#44) * Cross-platform spin lock implementation. * Cross-platform reference counting. * Cross-platform event implementation. (#47) * Compiling and running on Linux. (#49) * Current work trying to get it to compile. * Update vcpkg.json baseline. * vcpkg, memory mapped file. * Bitscan forward. * Fix localtime_s. * FPS patches high res clock. * Rename Window to GameWindow. Fix guest pointers. * GetCurrentThreadID gone. * Code cache pointers, RenderWindow type. * Add Linux stubs. * Refactor Config. * Fix paths. * Add linux-release config. * FS fixes. * Fix Windows compilation errors & unicode converter crash. * Rename physical memory allocation functions to not clash with X11. * Fix NULL character being added on RtlMultiByteToUnicodeN. * Use std::exit. * Add protection to memory on Linux. * Convert majority of dependencies to submodules. (#48) * Convert majority of dependencies to submodules. * Don't compile header-only libraries. * Fix a few incorrect data types. * Fix config directory. * Unicode fixes & sizeof asserts. * Change the exit function to not call static destructors. * Fix files picker. * Add RelWithDebInfo preset for Linux. * Implement OS Restart on Linux. (#50) --------- Co-authored-by: Dario <dariosamo@gmail.com> * Update PowerRecomp. * Add Env Var detection for VCPKG_ROOT, add DLC detection. * Use error code version on DLC directory iterator. * Set D3D12MA::ALLOCATOR_FLAG_DONT_PREFER_SMALL_BUFFERS_COMMITTED flag. * Linux flatpak. (#51) * Add flatpak support. * Add game install directory override for flatpak. * Flatpak'ing. * Flatpak it some more. * We flat it, we pak it. * Flatpak'd. * The Marvelous Misadventures of Flatpak. * Attempt to change logic of NFD and show error. * Flattenpakken. * Use game install directory instead of current path. * Attempt to fix line endings. * Update io.github.hedge_dev.unleashedrecomp.json * Fix system time query implementation. * Add Present Wait to Vulkan to improve frame pacing and reduce latency. (#53) * Add present wait support to Vulkan. * Default to triple buffering if presentWait is supported. * Bracey fellas. * Update paths.h * SDL2 audio (again). (#52) * Implement SDL2 audio (again). * Call timeBeginPeriod/timeEndPeriod. * Replace miniaudio with SDL mixer. * Queue audio samples in a separate thread. * Enable CMake option override policy & fix compilation error. * Fix compilation error on Linux. * Fix but also trim shared strings. * Wayland support. (#55) * Make channel index a global variable in embedded player. * Fix SDL Audio selection for OGG on Flatpak. * Minor installer wizard fixes. * Fix compilation error. * Yield in model consumer and pipeline compiler threads. * Special case Sleep(0) to yield on Linux. * Add App Id hint. * Correct implementation for auto reset events. (#57) --------- Co-authored-by: Dario <dariosamo@gmail.com> Co-authored-by: Hyper <34012267+hyperbx@users.noreply.github.com>
311 lines
8.5 KiB
C++
311 lines
8.5 KiB
C++
#include <stdafx.h>
|
|
#include <SDL.h>
|
|
#include <user/config.h>
|
|
#include <hid/hid_detail.h>
|
|
#include <ui/game_window.h>
|
|
#include <kernel/xdm.h>
|
|
|
|
#define TRANSLATE_INPUT(S, X) SDL_GameControllerGetButton(controller, S) << FirstBitLow(X)
|
|
#define VIBRATION_TIMEOUT_MS 5000
|
|
|
|
class Controller
|
|
{
|
|
public:
|
|
SDL_GameController* controller{};
|
|
SDL_Joystick* joystick{};
|
|
SDL_JoystickID id{ -1 };
|
|
XAMINPUT_GAMEPAD state{};
|
|
XAMINPUT_VIBRATION vibration{ 0, 0 };
|
|
int index{};
|
|
|
|
Controller() = default;
|
|
|
|
explicit Controller(int index) : Controller(SDL_GameControllerOpen(index))
|
|
{
|
|
this->index = index;
|
|
}
|
|
|
|
Controller(SDL_GameController* controller) : controller(controller)
|
|
{
|
|
if (!controller)
|
|
return;
|
|
|
|
joystick = SDL_GameControllerGetJoystick(controller);
|
|
id = SDL_JoystickInstanceID(joystick);
|
|
}
|
|
|
|
SDL_GameControllerType GetControllerType() const
|
|
{
|
|
return SDL_GameControllerTypeForIndex(index);
|
|
}
|
|
|
|
hid::detail::EInputDevice GetInputDevice() const
|
|
{
|
|
switch (GetControllerType())
|
|
{
|
|
case SDL_CONTROLLER_TYPE_PS3:
|
|
case SDL_CONTROLLER_TYPE_PS4:
|
|
case SDL_CONTROLLER_TYPE_PS5:
|
|
return hid::detail::EInputDevice::PlayStation;
|
|
}
|
|
|
|
return hid::detail::EInputDevice::Xbox;
|
|
}
|
|
|
|
void Close()
|
|
{
|
|
if (!controller)
|
|
return;
|
|
|
|
SDL_GameControllerClose(controller);
|
|
|
|
controller = nullptr;
|
|
joystick = nullptr;
|
|
id = -1;
|
|
}
|
|
|
|
bool CanPoll()
|
|
{
|
|
return controller && (GameWindow::s_isFocused || Config::AllowBackgroundInput);
|
|
}
|
|
|
|
void PollAxis()
|
|
{
|
|
if (!CanPoll())
|
|
return;
|
|
|
|
auto& pad = state;
|
|
|
|
pad.sThumbLX = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
|
|
pad.sThumbLY = ~SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
|
|
|
|
pad.sThumbRX = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
|
|
pad.sThumbRY = ~SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
|
|
|
|
pad.bLeftTrigger = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) >> 7;
|
|
pad.bRightTrigger = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) >> 7;
|
|
}
|
|
|
|
void Poll()
|
|
{
|
|
if (!CanPoll())
|
|
return;
|
|
|
|
auto& pad = state;
|
|
|
|
pad.wButtons = 0;
|
|
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_DPAD_UP, XAMINPUT_GAMEPAD_DPAD_UP);
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_DPAD_DOWN, XAMINPUT_GAMEPAD_DPAD_DOWN);
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_DPAD_LEFT, XAMINPUT_GAMEPAD_DPAD_LEFT);
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_DPAD_RIGHT, XAMINPUT_GAMEPAD_DPAD_RIGHT);
|
|
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_START, XAMINPUT_GAMEPAD_START);
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_BACK, XAMINPUT_GAMEPAD_BACK);
|
|
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_LEFTSTICK, XAMINPUT_GAMEPAD_LEFT_THUMB);
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_RIGHTSTICK, XAMINPUT_GAMEPAD_RIGHT_THUMB);
|
|
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_LEFTSHOULDER, XAMINPUT_GAMEPAD_LEFT_SHOULDER);
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, XAMINPUT_GAMEPAD_RIGHT_SHOULDER);
|
|
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_A, XAMINPUT_GAMEPAD_A);
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_B, XAMINPUT_GAMEPAD_B);
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_X, XAMINPUT_GAMEPAD_X);
|
|
pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_Y, XAMINPUT_GAMEPAD_Y);
|
|
}
|
|
|
|
void SetVibration(const XAMINPUT_VIBRATION& vibration)
|
|
{
|
|
if (!CanPoll())
|
|
return;
|
|
|
|
this->vibration = vibration;
|
|
|
|
SDL_GameControllerRumble(controller, vibration.wLeftMotorSpeed * 256, vibration.wRightMotorSpeed * 256, VIBRATION_TIMEOUT_MS);
|
|
}
|
|
};
|
|
|
|
std::array<Controller, 4> g_controllers;
|
|
Controller* g_activeController;
|
|
|
|
inline Controller* EnsureController(uint32_t dwUserIndex)
|
|
{
|
|
if (!g_controllers[dwUserIndex].controller)
|
|
return nullptr;
|
|
|
|
return &g_controllers[dwUserIndex];
|
|
}
|
|
|
|
inline size_t FindFreeController()
|
|
{
|
|
for (size_t i = 0; i < g_controllers.size(); i++)
|
|
{
|
|
if (!g_controllers[i].controller)
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
inline Controller* FindController(int which)
|
|
{
|
|
for (auto& controller : g_controllers)
|
|
{
|
|
if (controller.id == which)
|
|
return &controller;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static void SetControllerInputDevice(Controller* controller)
|
|
{
|
|
g_activeController = controller;
|
|
hid::detail::g_inputDevice = controller->GetInputDevice();
|
|
hid::detail::g_inputDeviceController = hid::detail::g_inputDevice;
|
|
}
|
|
|
|
int HID_OnSDLEvent(void*, SDL_Event* event)
|
|
{
|
|
switch (event->type)
|
|
{
|
|
case SDL_CONTROLLERDEVICEADDED:
|
|
{
|
|
const auto freeIndex = FindFreeController();
|
|
|
|
if (freeIndex != -1)
|
|
g_controllers[freeIndex] = Controller(event->cdevice.which);
|
|
|
|
break;
|
|
}
|
|
|
|
case SDL_CONTROLLERDEVICEREMOVED:
|
|
{
|
|
auto* controller = FindController(event->cdevice.which);
|
|
|
|
if (controller)
|
|
controller->Close();
|
|
|
|
break;
|
|
}
|
|
|
|
case SDL_CONTROLLERBUTTONDOWN:
|
|
case SDL_CONTROLLERBUTTONUP:
|
|
case SDL_CONTROLLERAXISMOTION:
|
|
{
|
|
auto* controller = FindController(event->cdevice.which);
|
|
|
|
if (controller)
|
|
{
|
|
if (event->type == SDL_CONTROLLERAXISMOTION)
|
|
{
|
|
if (abs(event->caxis.value) > 8000)
|
|
SetControllerInputDevice(controller);
|
|
|
|
controller->PollAxis();
|
|
}
|
|
else
|
|
{
|
|
SetControllerInputDevice(controller);
|
|
|
|
controller->Poll();
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SDL_KEYDOWN:
|
|
case SDL_KEYUP:
|
|
hid::detail::g_inputDevice = hid::detail::EInputDevice::Keyboard;
|
|
break;
|
|
|
|
case SDL_MOUSEMOTION:
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
case SDL_MOUSEBUTTONUP:
|
|
hid::detail::g_inputDevice = hid::detail::EInputDevice::Mouse;
|
|
break;
|
|
|
|
case SDL_WINDOWEVENT:
|
|
{
|
|
if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST)
|
|
{
|
|
// Stop vibrating controllers on focus lost.
|
|
for (auto& controller : g_controllers)
|
|
controller.SetVibration({ 0, 0 });
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void hid::detail::Init()
|
|
{
|
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS3, "1");
|
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4, "1");
|
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5, "1");
|
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, "1");
|
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_WII, "1");
|
|
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
|
|
SDL_SetHint(SDL_HINT_XINPUT_ENABLED, "1");
|
|
|
|
SDL_InitSubSystem(SDL_INIT_EVENTS);
|
|
SDL_AddEventWatch(HID_OnSDLEvent, nullptr);
|
|
|
|
SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
|
|
}
|
|
|
|
uint32_t hid::detail::GetState(uint32_t dwUserIndex, XAMINPUT_STATE* pState)
|
|
{
|
|
static uint32_t packet;
|
|
|
|
if (!pState)
|
|
return ERROR_BAD_ARGUMENTS;
|
|
|
|
memset(pState, 0, sizeof(*pState));
|
|
|
|
pState->dwPacketNumber = packet++;
|
|
|
|
if (!g_activeController)
|
|
return ERROR_DEVICE_NOT_CONNECTED;
|
|
|
|
pState->Gamepad = g_activeController->state;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
uint32_t hid::detail::SetState(uint32_t dwUserIndex, XAMINPUT_VIBRATION* pVibration)
|
|
{
|
|
if (!pVibration)
|
|
return ERROR_BAD_ARGUMENTS;
|
|
|
|
if (!g_activeController)
|
|
return ERROR_DEVICE_NOT_CONNECTED;
|
|
|
|
g_activeController->SetVibration(*pVibration);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
uint32_t hid::detail::GetCapabilities(uint32_t dwUserIndex, XAMINPUT_CAPABILITIES* pCaps)
|
|
{
|
|
if (!pCaps)
|
|
return ERROR_BAD_ARGUMENTS;
|
|
|
|
if (!g_activeController)
|
|
return ERROR_DEVICE_NOT_CONNECTED;
|
|
|
|
memset(pCaps, 0, sizeof(*pCaps));
|
|
|
|
pCaps->Type = XAMINPUT_DEVTYPE_GAMEPAD;
|
|
pCaps->SubType = XAMINPUT_DEVSUBTYPE_GAMEPAD; // TODO: other types?
|
|
pCaps->Flags = 0;
|
|
pCaps->Gamepad = g_activeController->state;
|
|
pCaps->Vibration = g_activeController->vibration;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|