Permalink
Browse files

Joystick support using mmsystem library on windows (#2355)

* Joystick support using mmsystem library on windows

* fixed signed/unsigned issues with axes

* joystick buttons are now configurable
1 parent a1189fa commit 6023794081e6cdea7a8584234e2c1faecac3f091 @georgemoralis georgemoralis committed with Nekotekina Feb 10, 2017
Showing with 251 additions and 0 deletions.
  1. +167 −0 rpcs3/MMJoystickHandler.cpp
  2. +71 −0 rpcs3/MMJoystickHandler.h
  3. +2 −0 rpcs3/rpcs3.cpp
  4. +2 −0 rpcs3/rpcs3.vcxproj
  5. +9 −0 rpcs3/rpcs3.vcxproj.filters
@@ -0,0 +1,167 @@
+#include "stdafx.h"
+#include "stdafx_gui.h"
+#ifdef _MSC_VER
+#include "MMJoystickHandler.h"
+
+MMJoystickConfig g_mmjoystick_config;
+
+namespace {
+ const DWORD THREAD_SLEEP = 10;
+ const DWORD THREAD_SLEEP_INACTIVE = 100;
+ const DWORD THREAD_TIMEOUT = 1000;
+
+ inline u16 ConvertAxis(DWORD value)
+ {
+ return static_cast<u16>((value) >> 8);
+ }
+}
+
+
+MMJoystickHandler::MMJoystickHandler() : active(false), thread(nullptr)
+{
+}
+
+MMJoystickHandler::~MMJoystickHandler()
+{
+ Close();
+}
+
+void MMJoystickHandler::Init(const u32 max_connect)
+{
+ supportedJoysticks = joyGetNumDevs();
+ if (supportedJoysticks > 0)
+ {
+ LOG_ERROR(HLE, "Driver supports %u joysticks", supportedJoysticks);
+ }
+ else
+ {
+ LOG_ERROR(HLE, "Driver doesn't support Joysticks");
+ }
+ js_info.dwSize = sizeof(js_info);
+ js_info.dwFlags = JOY_RETURNALL;
+ joyGetDevCaps(JOYSTICKID1, &js_caps, sizeof(js_caps));
+ bool JoyPresent = (joyGetPosEx(JOYSTICKID1, &js_info) == JOYERR_NOERROR);
+ if (JoyPresent)
+ {
+ LOG_ERROR(HLE, "Found connected joystick with %u buttons", js_caps.wNumButtons);
+
+ std::memset(&m_info, 0, sizeof m_info);
+ m_info.max_connect = max_connect;
+
+ for (u32 i = 0, max = std::min(max_connect, u32(1)); i != max; ++i)
+ {
+ g_mmjoystick_config.load();
+ m_pads.emplace_back(
+ CELL_PAD_STATUS_ASSIGN_CHANGES,
+ CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF,
+ CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE,
+ CELL_PAD_DEV_TYPE_STANDARD
+ );
+ auto & pad = m_pads.back();
+
+ pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.triangle, CELL_PAD_CTRL_TRIANGLE);
+ pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.circle, CELL_PAD_CTRL_CIRCLE);
+ pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.cross, CELL_PAD_CTRL_CROSS);
+ pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.square, CELL_PAD_CTRL_SQUARE);
+ pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.l2, CELL_PAD_CTRL_L2);
+ pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.r2, CELL_PAD_CTRL_R2);
+ pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.l1, CELL_PAD_CTRL_L1);
+ pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.r1, CELL_PAD_CTRL_R1);
+ pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_mmjoystick_config.start, CELL_PAD_CTRL_START);
+ pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_mmjoystick_config.select, CELL_PAD_CTRL_SELECT);
+ pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_mmjoystick_config.l3, CELL_PAD_CTRL_L3);
+ pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_mmjoystick_config.r3, CELL_PAD_CTRL_R3);
+ pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, 0x100/*CELL_PAD_CTRL_PS*/);// TODO: PS button support
+
+ pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, JOY_POVFORWARD, CELL_PAD_CTRL_UP);
+ pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, JOY_POVBACKWARD, CELL_PAD_CTRL_DOWN);
+ pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, JOY_POVLEFT, CELL_PAD_CTRL_LEFT);
+ pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, JOY_POVRIGHT, CELL_PAD_CTRL_RIGHT);
+
+ pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, 0, 0);
+ pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, 0, 0);
+ pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, 0, 0);
+ pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, 0, 0);
+
+ active = true;
+ thread = CreateThread(NULL, 0, &MMJoystickHandler::ThreadProcProxy, this, 0, NULL);
+
+ }
+ }
+ else
+ {
+ LOG_ERROR(HLE, "Joystick not found");
+ }
+}
+
+void MMJoystickHandler::Close()
+{
+ if (active)
+ {
+ if (thread)
+ {
+ active = false;
+ if (WaitForSingleObject(thread, THREAD_TIMEOUT) != WAIT_OBJECT_0)
+ LOG_ERROR(HLE, "MMJoystick thread could not stop within %d milliseconds", (u32)THREAD_TIMEOUT);
+ thread = nullptr;
+ }
+ }
+
+ m_pads.clear();
+}
+
+DWORD MMJoystickHandler::ThreadProcedure()
+{
+ while (active)
+ {
+ MMRESULT status;
+ DWORD online = 0;
+
+ for (DWORD i = 0; i != m_pads.size(); ++i)
+ {
+
+ auto & pad = m_pads[i];
+ status =joyGetPosEx(JOYSTICKID1, &js_info);
+
+ switch (status)
+ {
+ case JOYERR_UNPLUGGED:
+ pad.m_port_status &= ~CELL_PAD_STATUS_CONNECTED;
+ break;
+
+ case JOYERR_NOERROR:
+ ++online;
+ pad.m_port_status |= CELL_PAD_STATUS_CONNECTED;
+ for (DWORD j = 0; j <= 12; j++)
+ {
+ bool pressed = js_info.dwButtons & pad.m_buttons[j].m_keyCode;
+ pad.m_buttons[j].m_pressed = pressed;
+ pad.m_buttons[j].m_value = pressed ? 255 : 0;
+ }
+ for (DWORD j = 13; j <= 16; j++)//POV aka digital pad
+ {
+ bool pressed = js_info.dwPOV == pad.m_buttons[j].m_keyCode;
+ pad.m_buttons[j].m_pressed = pressed;
+ pad.m_buttons[j].m_value = pressed ? 255 : 0;
+ }
+ pad.m_sticks[0].m_value = ConvertAxis(js_info.dwXpos);
+ pad.m_sticks[1].m_value = ConvertAxis(js_info.dwYpos);
+ pad.m_sticks[2].m_value = ConvertAxis(js_info.dwZpos);
+ pad.m_sticks[3].m_value = ConvertAxis(js_info.dwRpos);
+ break;
+ }
+ }
+
+ Sleep((online > 0) ? THREAD_SLEEP : THREAD_SLEEP_INACTIVE);
+ m_info.now_connect = online;
+ }
+
+ return 0;
+}
+
+DWORD WINAPI MMJoystickHandler::ThreadProcProxy(LPVOID parameter)
+{
+ return reinterpret_cast<MMJoystickHandler *>(parameter)->ThreadProcedure();
+}
+
+#endif
@@ -0,0 +1,71 @@
+#pragma once
+
+#include "Emu/Io/PadHandler.h"
+#include <mmsystem.h>
+#include "Utilities/Config.h"
+
+struct MMJoystickConfig final : cfg::node
+{
+ const std::string cfg_name = fs::get_config_dir() + "/config_mmjoystick.yml";
+
+ //cfg::int32_entry left_stick_left{ *this, "Left Analog Stick Left", static_cast<int>('A') };
+ //cfg::int32_entry left_stick_down{ *this, "Left Analog Stick Down", static_cast<int>('S') };
+ //cfg::int32_entry left_stick_right{ *this, "Left Analog Stick Right", static_cast<int>('D') };
+ //cfg::int32_entry left_stick_up{ *this, "Left Analog Stick Up", static_cast<int>('W') };
+ //cfg::int32_entry right_stick_left{ *this, "Right Analog Stick Left", 313 };
+ //cfg::int32_entry right_stick_down{ *this, "Right Analog Stick Down", 367 };
+ //cfg::int32_entry right_stick_right{ *this, "Right Analog Stick Right", 312 };
+ //cfg::int32_entry right_stick_up{ *this, "Right Analog Stick Up", 366 };
+ cfg::int32_entry start{ *this, "Start", JOY_BUTTON9 };
+ cfg::int32_entry select{ *this, "Select", JOY_BUTTON10 };
+ cfg::int32_entry square{ *this, "Square", JOY_BUTTON4 };
+ cfg::int32_entry cross{ *this, "Cross", JOY_BUTTON3 };
+ cfg::int32_entry circle{ *this, "Circle", JOY_BUTTON2 };
+ cfg::int32_entry triangle{ *this, "Triangle", JOY_BUTTON1 };
+ //cfg::int32_entry left{ *this, "Left", 314 };
+ //cfg::int32_entry down{ *this, "Down", 317 };
+ //cfg::int32_entry right{ *this, "Right", 316 };
+ //cfg::int32_entry up{ *this, "Up", 315 };
+ cfg::int32_entry r1{ *this, "R1", JOY_BUTTON8 };
+ cfg::int32_entry r2{ *this, "R2", JOY_BUTTON6 };
+ cfg::int32_entry r3{ *this, "R3", JOY_BUTTON12 };
+ cfg::int32_entry l1{ *this, "L1", JOY_BUTTON7 };
+ cfg::int32_entry l2{ *this, "L2", JOY_BUTTON5 };
+ cfg::int32_entry l3{ *this, "L3", JOY_BUTTON11 };
+
+ bool load()
+ {
+ if (fs::file cfg_file{ cfg_name, fs::read })
+ {
+ return from_string(cfg_file.to_string());
+ }
+
+ return false;
+ }
+
+ void save()
+ {
+ fs::file(cfg_name, fs::rewrite).write(to_string());
+ }
+};
+
+class MMJoystickHandler final : public PadHandlerBase
+{
+public:
+ MMJoystickHandler();
+ ~MMJoystickHandler();
+
+ void Init(const u32 max_connect) override;
+ void Close();
+
+private:
+ DWORD ThreadProcedure();
+ static DWORD WINAPI ThreadProcProxy(LPVOID parameter);
+
+private:
+ u32 supportedJoysticks;
+ mutable bool active;
+ HANDLE thread;
+ JOYINFOEX js_info;
+ JOYCAPS js_caps;
+};
View
@@ -19,6 +19,7 @@
#include "KeyboardPadHandler.h"
#ifdef _MSC_VER
#include "XInputPadHandler.h"
+#include "MMJoystickHandler.h"
#endif
#include "Emu/RSX/Null/NullGSRender.h"
@@ -87,6 +88,7 @@ cfg::map_entry<std::function<std::shared_ptr<PadHandlerBase>()>> g_cfg_pad_handl
{ "Keyboard", &std::make_shared<KeyboardPadHandler> },
#ifdef _MSC_VER
{ "XInput", &std::make_shared<XInputPadHandler> },
+ { "MMJoystick", &std::make_shared<MMJoystickHandler>},
#endif
});
View
@@ -143,6 +143,7 @@
<ClCompile Include="Gui\SignInDialog.cpp" />
<ClCompile Include="Gui\TextInputDialog.cpp" />
<ClCompile Include="KeyboardPadHandler.cpp" />
+ <ClCompile Include="MMJoystickHandler.cpp" />
<ClCompile Include="rpcs3.cpp" />
<ClCompile Include="XInputPadHandler.cpp" />
</ItemGroup>
@@ -176,6 +177,7 @@
<ClInclude Include="Gui\SignInDialog.h" />
<ClInclude Include="Gui\TextInputDialog.h" />
<ClInclude Include="KeyboardPadHandler.h" />
+ <ClInclude Include="MMJoystickHandler.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="rpcs3.h" />
<ClInclude Include="stdafx_gui.h" />
@@ -19,6 +19,9 @@
<Filter Include="Io\Basic">
<UniqueIdentifier>{ce9ce942-07dd-4cbc-9b76-0f754c363eda}</UniqueIdentifier>
</Filter>
+ <Filter Include="Io\MMJoystick">
+ <UniqueIdentifier>{92e92511-88b9-4187-9c94-80790248d337}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="rpcs3.cpp">
@@ -108,6 +111,9 @@
<ClCompile Include="Gui\OskDialog.cpp">
<Filter>Gui</Filter>
</ClCompile>
+ <ClCompile Include="MMJoystickHandler.cpp">
+ <Filter>Io\MMJoystick</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\Utilities\MTProgressDialog.h">
@@ -204,6 +210,9 @@
<ClInclude Include="BasicMouseHandler.h">
<Filter>Io\Basic</Filter>
</ClInclude>
+ <ClInclude Include="MMJoystickHandler.h">
+ <Filter>Io\MMJoystick</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="rpcs3.ico" />

0 comments on commit 6023794

Please sign in to comment.