Skip to content

Commit

Permalink
Merge remote-tracking branch 'github/pyro-gamepad'
Browse files Browse the repository at this point in the history
  • Loading branch information
Themaister committed Oct 30, 2023
2 parents 5f61d2a + dbadf14 commit 44930e4
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 31 deletions.
9 changes: 9 additions & 0 deletions application/application.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ class Application
return 720;
}

virtual bool enable_joypad_input_manager()
{
// If false, disable polling for gamepads and other devices.
// Keyboard/mouse input is still handled as normal.
// Only relevant to override for special use cases.
// TODO: Figure out something more elegant when more use cases arise.
return true;
}

bool poll();
void run_frame();

Expand Down
1 change: 1 addition & 0 deletions application/input/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ if (${CMAKE_SYSTEM} MATCHES "Linux")
endif()
elseif (WIN32)
target_sources(granite-input PRIVATE xinput_windows.cpp xinput_windows.hpp)
target_link_libraries(granite-input PRIVATE dinput8 dxguid)
endif()

if (HOTPLUG_UDEV_FOUND)
Expand Down
1 change: 1 addition & 0 deletions application/input/input.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ enum class JoypadKey
RightThumb,
Start,
Select,
Mode,
Count,
Unknown
};
Expand Down
28 changes: 20 additions & 8 deletions application/input/input_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ void LinuxInputManager::setup_joypad_remapper(int fd, unsigned index)
remapper.register_button(BTN_THUMBR, JoypadKey::RightThumb, JoypadAxis::Unknown);
remapper.register_button(BTN_TL, JoypadKey::LeftShoulder, JoypadAxis::Unknown);
remapper.register_button(BTN_TR, JoypadKey::RightShoulder, JoypadAxis::Unknown);
remapper.register_button(BTN_MODE, JoypadKey::Mode, JoypadAxis::Unknown);
remapper.register_axis(ABS_X, JoypadAxis::LeftX, 1.0f, JoypadKey::Unknown, JoypadKey::Unknown);
remapper.register_axis(ABS_Y, JoypadAxis::LeftY, 1.0f, JoypadKey::Unknown, JoypadKey::Unknown);
remapper.register_axis(ABS_RX, JoypadAxis::RightX, 1.0f, JoypadKey::Unknown, JoypadKey::Unknown);
Expand Down Expand Up @@ -449,15 +450,26 @@ bool LinuxInputManager::enqueue_open_devices(DeviceType type, InputCallback call
if (!deferred.enumerate)
return false;

// This can take 200ms or so for some reason ...
deferred.task = GRANITE_THREAD_GROUP()->create_task([type, enumerate = deferred.enumerate.get()] {
if (auto *tg = GRANITE_THREAD_GROUP())
{
// This can take 200ms or so for some reason ...
deferred.task = tg->create_task([type, enumerate = deferred.enumerate.get()] {
const char *type_str = get_device_type_string(type);
udev_enumerate_add_match_property(enumerate, type_str, "1");
udev_enumerate_scan_devices(enumerate);
});
deferred.task->set_desc("udev-scan-devices");
deferred.task->flush();
deferred_init.push_back(std::move(deferred));
}
else
{
const char *type_str = get_device_type_string(type);
udev_enumerate_add_match_property(enumerate, type_str, "1");
udev_enumerate_scan_devices(enumerate);
});
deferred.task->set_desc("udev-scan-devices");
deferred.task->flush();
deferred_init.push_back(std::move(deferred));
udev_enumerate_add_match_property(deferred.enumerate.get(), type_str, "1");
udev_enumerate_scan_devices(deferred.enumerate.get());
deferred_init.push_back(std::move(deferred));
}

return true;
}

Expand Down
148 changes: 140 additions & 8 deletions application/input/xinput_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,63 @@ using namespace Util;

namespace Granite
{
bool XInputManager::init(Granite::InputTracker *tracker_)
XInputManager::~XInputManager()
{
for (auto *dev : pDevice)
if (dev)
dev->Release();
if (pDI)
pDI->Release();
}

// Really should just move to SDL ... But need quick hack to make PS4 controllers work :')

static BOOL CALLBACK enum_callback(const DIDEVICEINSTANCEA *inst, void *p)
{
auto *manager = static_cast<XInputManager *>(p);
return manager->di_enum_callback(inst);
}

BOOL XInputManager::di_enum_callback(const DIDEVICEINSTANCEA *inst)
{
// Different PIDs for wireless dongle and cabled connection.
if (inst->guidProduct.Data1 != 195036492 && inst->guidProduct.Data1 != 164365644)
{
LOGI("Enumerated DI input device that is not PS4 controller. Bailing ...\n");
return DIENUM_CONTINUE;
}

LOGI("Enumerated PS4 controller.\n");

unsigned index = trailing_ones(active_pads);
if (index >= 4)
return DIENUM_STOP;

if (FAILED(pDI->CreateDevice(inst->guidInstance, &pDevice[index], nullptr)))
return DIENUM_CONTINUE;

if (FAILED(pDevice[index]->SetDataFormat(&c_dfDIJoystick2)))
{
pDevice[index]->Release();
pDevice[index] = nullptr;
return DIENUM_CONTINUE;
}

if (FAILED(pDevice[index]->SetCooperativeLevel(hwnd, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE)))
{
pDevice[index]->Release();
pDevice[index] = nullptr;
return DIENUM_CONTINUE;
}

active_pads |= 1u << index;
tracker->enable_joypad(index);
return DIENUM_CONTINUE;
}

bool XInputManager::init(Granite::InputTracker *tracker_, HWND hwnd_)
{
hwnd = hwnd_;
if (!lib)
lib = DynamicLibrary("xinput1_4");
if (!lib)
Expand All @@ -47,6 +102,13 @@ bool XInputManager::init(Granite::InputTracker *tracker_)
for (unsigned i = 0; i < 4; i++)
try_polling_device(i);

HRESULT hr;
if (FAILED(hr = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8A, (void**)&pDI, nullptr)))
return false;

if (FAILED(pDI->EnumDevices(DI8DEVCLASS_GAMECTRL, enum_callback, this, DIEDFL_ATTACHEDONLY)))
return false;

return true;
}

Expand Down Expand Up @@ -77,15 +139,85 @@ bool XInputManager::poll()
if ((active_pads & (1u << i)) == 0)
continue;

XINPUT_STATE new_state;
memset(&new_state, 0, sizeof(new_state));
if (pXInputGetState(i, &new_state) != ERROR_DEVICE_NOT_CONNECTED)
create_events(i, new_state);
if (pDevice[i])
{
if (FAILED(pDevice[i]->Poll()) && FAILED(pDevice[i]->Acquire()) && FAILED(pDevice[i]->Poll()))
{
tracker->disable_joypad(i);
active_pads &= ~(1u << i);
pDevice[i]->Release();
pDevice[i] = nullptr;
}
else
{
DIJOYSTATE2 joy_state = {};
if (SUCCEEDED(pDevice[i]->GetDeviceState(sizeof(DIJOYSTATE2), &joy_state)))
{
// Hardcoded for PS4 dinput, yaaaay <_<
static const JoypadKey joykey_mapping[] =
{
JoypadKey::West, JoypadKey::South, JoypadKey::East, JoypadKey::North,
JoypadKey::LeftShoulder, JoypadKey::RightShoulder,
JoypadKey::Unknown, JoypadKey::Unknown,
JoypadKey::Select, JoypadKey::Start,
JoypadKey::LeftThumb, JoypadKey::RightThumb,
JoypadKey::Mode
};

for (unsigned j = 0; j < sizeof(joykey_mapping) / sizeof(joykey_mapping[0]); j++)
{
if (joykey_mapping[j] == JoypadKey::Unknown)
continue;

tracker->joypad_key_state(
i, joykey_mapping[j], joy_state.rgbButtons[j] ? JoypadKeyState::Pressed : JoypadKeyState::Released);
}

float lx = 2.0f * joy_state.lX / float(0xffff) - 1.0f;
float ly = 2.0f * joy_state.lY / float(0xffff) - 1.0f;
float rx = 2.0f * joy_state.lZ / float(0xffff) - 1.0f;
float ry = 2.0f * joy_state.lRz / float(0xffff) - 1.0f;
float lt = joy_state.lRx / float(0xffff);
float rt = joy_state.lRy / float(0xffff);

tracker->joyaxis_state(i, JoypadAxis::LeftX, lx);
tracker->joyaxis_state(i, JoypadAxis::LeftY, ly);
tracker->joyaxis_state(i, JoypadAxis::RightX, rx);
tracker->joyaxis_state(i, JoypadAxis::RightY, ry);
tracker->joyaxis_state(i, JoypadAxis::LeftTrigger, lt);
tracker->joyaxis_state(i, JoypadAxis::RightTrigger, rt);

int pov = joy_state.rgdwPOV[0];

bool left = false, right = false, up = false, down = false;
if (pov >= 0)
{
pov /= 100;
up = pov > 270 || pov < 90;
right = pov > 0 && pov < 180;
down = pov > 90 && pov < 270;
left = pov > 180;
}

tracker->joypad_key_state(i, JoypadKey::Right, right ? JoypadKeyState::Pressed : JoypadKeyState::Released);
tracker->joypad_key_state(i, JoypadKey::Down, down ? JoypadKeyState::Pressed : JoypadKeyState::Released);
tracker->joypad_key_state(i, JoypadKey::Up, up ? JoypadKeyState::Pressed : JoypadKeyState::Released);
tracker->joypad_key_state(i, JoypadKey::Left, left ? JoypadKeyState::Pressed : JoypadKeyState::Released);
}
}
}
else
{
tracker->disable_joypad(i);
memset(&pads[i], 0, sizeof(pads[i]));
active_pads &= ~(1u << i);
XINPUT_STATE new_state;
memset(&new_state, 0, sizeof(new_state));
if (pXInputGetState(i, &new_state) != ERROR_DEVICE_NOT_CONNECTED)
create_events(i, new_state);
else
{
tracker->disable_joypad(i);
memset(&pads[i], 0, sizeof(pads[i]));
active_pads &= ~(1u << i);
}
}
}

Expand Down
16 changes: 15 additions & 1 deletion application/input/xinput_windows.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,35 @@
#include <windows.h>
#include <xinput.h>

#define DIRECTINPUT_VERSION 0x800
#include <dinput.h>

#include "dynamic_library.hpp"

namespace Granite
{
class XInputManager
{
public:
bool init(InputTracker *tracker);
~XInputManager();
bool init(InputTracker *tracker, HWND hwnd);
bool poll();

void operator=(const XInputManager &) = delete;
XInputManager(const XInputManager &) = delete;
XInputManager() = default;

BOOL di_enum_callback(const DIDEVICEINSTANCEA *inst);

private:
HWND hwnd = nullptr;
InputTracker *tracker = nullptr;
uint8_t active_pads = 0;
XINPUT_STATE pads[4] = {};

IDirectInput8A *pDI = nullptr;
IDirectInputDevice8A *pDevice[4] = {};

void create_events(unsigned index, const XINPUT_STATE &state);
void try_polling_device(unsigned index);
unsigned poll_count = 0;
Expand Down
28 changes: 18 additions & 10 deletions application/platforms/application_glfw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,17 +171,21 @@ struct WSIPlatformGLFW : GraniteWSIPlatform
bool alive(Vulkan::WSI &) override
{
process_events_async_thread();

if (enabled_input_manager)
{
#if defined(HAVE_LINUX_INPUT) || defined(HAVE_XINPUT_WINDOWS)
input_manager.poll();
input_manager.poll();
#endif

// Convenient equivalent to pressing escape on the keyboard or something.
if (get_input_tracker().joykey_pressed(0, JoypadKey::Start) &&
get_input_tracker().joykey_pressed(0, JoypadKey::Select) &&
get_input_tracker().joykey_pressed(0, JoypadKey::LeftShoulder) &&
get_input_tracker().joykey_pressed(0, JoypadKey::RightShoulder))
{
return false;
// Convenient equivalent to pressing escape on the keyboard or something.
if (get_input_tracker().joykey_pressed(0, JoypadKey::Start) &&
get_input_tracker().joykey_pressed(0, JoypadKey::Select) &&
get_input_tracker().joykey_pressed(0, JoypadKey::LeftShoulder) &&
get_input_tracker().joykey_pressed(0, JoypadKey::RightShoulder))
{
return false;
}
}

return !request_tear_down.load();
Expand All @@ -191,7 +195,8 @@ struct WSIPlatformGLFW : GraniteWSIPlatform
{
process_events_async_thread();
#if defined(HAVE_LINUX_INPUT) || defined(HAVE_XINPUT_WINDOWS)
input_manager.poll();
if (enabled_input_manager)
input_manager.poll();
#endif
get_input_tracker().dispatch_current_state(get_frame_timer().get_frame_time());
}
Expand Down Expand Up @@ -350,7 +355,7 @@ struct WSIPlatformGLFW : GraniteWSIPlatform
if (!input_manager.init(LINUX_INPUT_MANAGER_JOYPAD_BIT, &get_input_tracker()))
LOGE("Failed to initialize input manager.\n");
#elif defined(HAVE_XINPUT_WINDOWS)
if (!input_manager.init(&get_input_tracker()))
if (!input_manager.init(&get_input_tracker(), glfwGetWin32Window(window)))
LOGE("Failed to initialize input manager.\n");
#endif
}
Expand All @@ -368,9 +373,11 @@ struct WSIPlatformGLFW : GraniteWSIPlatform
dispatch_running_events();
}

if (app->enable_joypad_input_manager())
{
GRANITE_SCOPED_TIMELINE_EVENT("glfw-init-input-managers");
init_input_managers();
enabled_input_manager = true;
}

{
Expand Down Expand Up @@ -482,6 +489,7 @@ struct WSIPlatformGLFW : GraniteWSIPlatform

std::atomic_bool request_tear_down;
bool async_loop_alive = false;
bool enabled_input_manager = false;

#ifdef HAVE_LINUX_INPUT
LinuxInputManager input_manager;
Expand Down
6 changes: 3 additions & 3 deletions application/platforms/application_khr_display.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ static bool vulkan_update_display_mode(unsigned *width, unsigned *height, const
struct WSIPlatformDisplay : Granite::GraniteWSIPlatform
{
public:
bool init(unsigned width_, unsigned height_)
bool init(unsigned width_, unsigned height_, bool enable_joypad)
{
width = width_;
height = height_;
Expand Down Expand Up @@ -125,7 +125,7 @@ struct WSIPlatformDisplay : Granite::GraniteWSIPlatform

#ifdef HAVE_LINUX_INPUT
if (!input_manager.init(
LINUX_INPUT_MANAGER_JOYPAD_BIT |
(enable_joypad ? LINUX_INPUT_MANAGER_JOYPAD_BIT : 0) |
LINUX_INPUT_MANAGER_KEYBOARD_BIT |
LINUX_INPUT_MANAGER_MOUSE_BIT |
LINUX_INPUT_MANAGER_TOUCHPAD_BIT,
Expand Down Expand Up @@ -370,7 +370,7 @@ int application_main(
if (app)
{
auto platform = std::make_unique<Granite::WSIPlatformDisplay>();
if (!platform->init(1280, 720))
if (!platform->init(1280, 720, app->enable_joypad_input_manager()))
return 1;
if (!app->init_platform(std::move(platform)) || !app->init_wsi())
return 1;
Expand Down
2 changes: 1 addition & 1 deletion util/timeline_trace_file.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class TimelineTraceFile
#define GRANITE_MACRO_CONCAT_IMPL(a, b) a##b
#define GRANITE_MACRO_CONCAT(a, b) GRANITE_MACRO_CONCAT_IMPL(a, b)
#define GRANITE_SCOPED_TIMELINE_EVENT(str) \
::Util::TimelineTraceFile::ScopedEvent GRANITE_MACRO_CONCAT(_timeline_scoped_count_, __COUNTER__){GRANITE_THREAD_GROUP()->get_timeline_trace_file(), str}
::Util::TimelineTraceFile::ScopedEvent GRANITE_MACRO_CONCAT(_timeline_scoped_count_, __COUNTER__){GRANITE_THREAD_GROUP() ? GRANITE_THREAD_GROUP()->get_timeline_trace_file() : nullptr, str}
#define GRANITE_SCOPED_TIMELINE_EVENT_FILE(file, str) \
::Util::TimelineTraceFile::ScopedEvent GRANITE_MACRO_CONCAT(_timeline_scoped_count_, __COUNTER__){file, str}
#else
Expand Down
Loading

0 comments on commit 44930e4

Please sign in to comment.