2018-05-24 02:14:17 +00:00
|
|
|
#pragma once
|
Update to v093r12 release.
byuu says:
I've completely redone the ethos InputManager and ruby to work on
HID::Device objects instead of one giant scancode pool.
Currently only the udev driver supports the changes to ruby, so only
Linux users will be able to compile and run this WIP build.
The nice thing about the new system is that it's now possible to
uniquely identify controllers, so if you swap out gamepads, you won't
end up with it working but with all the mappings all screwed up. Since
higan lets you map multiple physical inputs to one emulated input, you
can now configure your keyboard and multiple gamepads to the same
emulated input, and then just use whatever controller you want.
Because USB gamepad makers failed to provide unique serial#s with each
controller, we have to limit the mapping to specific USB ports.
Otherwise, we couldn't distinguish two otherwise identical gamepads. So
basically your computer USB ports act like real game console input port
numbers. Which is kind of neat, I guess.
And the really nice thing about the new system is that we now have the
capability to support hotplugging input devices. I haven't yet added
this to any drivers, but I'm definitely going to add it to udev for v094
official.
Finally, with the device ID (vendor ID + product ID) exposed, we gain
one last really cool feature that we may be able to develop more in the
future. Say we created a joypad.bml file to include with higan. In it,
we'd store the Xbox 360 controller, and pre-defined button mappings for
each emulated system. So if higan detects you have an Xbox 360
controller, you can just plug it in and use it. Even better, we can
clearly specify the difference between triggers and analog axes, and
name each individual input. So you'd see "Xbox 360 Gamepad #1: Left
Trigger" instead of higan v093's "JP0::Axis2.Hi"
Note: for right now, ethos' input manager isn't filtering the device IDs
to look pretty. So you're going to see a 64-bit hex value for a device
ID right now instead of something like Joypad#N for now.
2013-12-23 11:43:51 +00:00
|
|
|
|
2014-01-05 09:59:17 +00:00
|
|
|
//documented functionality
|
|
|
|
#define oXInputGetState "XInputGetState"
|
|
|
|
#define oXInputSetState "XInputSetState"
|
|
|
|
typedef DWORD WINAPI (*pXInputGetState)(DWORD dwUserIndex, XINPUT_STATE* pState);
|
|
|
|
typedef DWORD WINAPI (*pXInputSetState)(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration);
|
|
|
|
|
|
|
|
//undocumented functionality
|
|
|
|
#define oXInputGetStateEx (LPCSTR)100
|
|
|
|
#define oXInputWaitForGuideButton (LPCSTR)101
|
|
|
|
#define oXInputCancelGuideButtonWait (LPCSTR)102
|
|
|
|
#define oXInputPowerOffController (LPCSTR)103
|
|
|
|
typedef DWORD WINAPI (*pXInputGetStateEx)(DWORD dwUserIndex, XINPUT_STATE* pState);
|
|
|
|
typedef DWORD WINAPI (*pXInputWaitForGuideButton)(DWORD dwUserIndex, DWORD dwFlag, void* pUnknown);
|
|
|
|
typedef DWORD WINAPI (*pXInputCancelGuideButtonWait)(DWORD dwUserIndex);
|
|
|
|
typedef DWORD WINAPI (*pXInputPowerOffController)(DWORD dwUserIndex);
|
|
|
|
|
|
|
|
#define XINPUT_GAMEPAD_GUIDE 0x0400
|
2013-12-21 10:45:58 +00:00
|
|
|
|
|
|
|
struct InputJoypadXInput {
|
2015-06-20 05:44:05 +00:00
|
|
|
Input& input;
|
|
|
|
InputJoypadXInput(Input& input) : input(input) {}
|
|
|
|
|
2013-12-21 10:45:58 +00:00
|
|
|
HMODULE libxinput = nullptr;
|
2014-01-05 09:59:17 +00:00
|
|
|
pXInputGetStateEx XInputGetStateEx = nullptr;
|
|
|
|
pXInputSetState XInputSetState = nullptr;
|
2013-12-21 10:45:58 +00:00
|
|
|
|
2014-01-05 09:59:17 +00:00
|
|
|
struct Joypad {
|
2015-06-12 13:14:38 +00:00
|
|
|
shared_pointer<HID::Joypad> hid{new HID::Joypad};
|
2018-05-24 02:14:17 +00:00
|
|
|
uint id = 0;
|
2013-12-21 10:45:58 +00:00
|
|
|
};
|
2014-01-05 09:59:17 +00:00
|
|
|
vector<Joypad> joypads;
|
2013-12-21 10:45:58 +00:00
|
|
|
|
2018-05-24 02:14:17 +00:00
|
|
|
auto assign(shared_pointer<HID::Joypad> hid, uint groupID, uint inputID, int16_t value) -> void {
|
2015-06-12 13:14:38 +00:00
|
|
|
auto& group = hid->group(groupID);
|
|
|
|
if(group.input(inputID).value() == value) return;
|
2015-06-20 05:44:05 +00:00
|
|
|
input.doChange(hid, groupID, inputID, group.input(inputID).value(), value);
|
2015-06-12 13:14:38 +00:00
|
|
|
group.input(inputID).setValue(value);
|
2014-01-05 09:59:17 +00:00
|
|
|
}
|
2013-12-21 10:45:58 +00:00
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto poll(vector<shared_pointer<HID::Device>>& devices) -> void {
|
2014-01-05 09:59:17 +00:00
|
|
|
for(auto& jp : joypads) {
|
2013-12-21 10:45:58 +00:00
|
|
|
XINPUT_STATE state;
|
2014-01-05 09:59:17 +00:00
|
|
|
if(XInputGetStateEx(jp.id, &state) != ERROR_SUCCESS) continue;
|
|
|
|
|
|
|
|
//flip vertical axes so that -32768 = up, +32767 = down
|
|
|
|
uint16_t axisLY = 32768 + state.Gamepad.sThumbLY;
|
|
|
|
uint16_t axisRY = 32768 + state.Gamepad.sThumbRY;
|
|
|
|
assign(jp.hid, HID::Joypad::GroupID::Axis, 0, (int16_t)state.Gamepad.sThumbLX);
|
|
|
|
assign(jp.hid, HID::Joypad::GroupID::Axis, 1, (int16_t)(~axisLY - 32768));
|
|
|
|
assign(jp.hid, HID::Joypad::GroupID::Axis, 2, (int16_t)state.Gamepad.sThumbRX);
|
|
|
|
assign(jp.hid, HID::Joypad::GroupID::Axis, 3, (int16_t)(~axisRY - 32768));
|
|
|
|
|
|
|
|
int16_t hatX = 0;
|
|
|
|
int16_t hatY = 0;
|
2017-05-30 07:48:41 +00:00
|
|
|
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ) hatY -= 32767;
|
|
|
|
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ) hatY += 32767;
|
|
|
|
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ) hatX -= 32767;
|
|
|
|
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) hatX += 32767;
|
2014-01-05 09:59:17 +00:00
|
|
|
|
|
|
|
assign(jp.hid, HID::Joypad::GroupID::Hat, 0, hatX);
|
|
|
|
assign(jp.hid, HID::Joypad::GroupID::Hat, 1, hatY);
|
|
|
|
|
|
|
|
//scale trigger ranges for up to down from (0 to 255) to (-32768 to +32767)
|
2013-12-21 10:45:58 +00:00
|
|
|
uint16_t triggerL = state.Gamepad.bLeftTrigger;
|
|
|
|
uint16_t triggerR = state.Gamepad.bRightTrigger;
|
|
|
|
triggerL = triggerL << 8 | triggerL << 0;
|
|
|
|
triggerR = triggerR << 8 | triggerR << 0;
|
|
|
|
|
2014-01-05 09:59:17 +00:00
|
|
|
assign(jp.hid, HID::Joypad::GroupID::Trigger, 0, (int16_t)(triggerL - 32768));
|
|
|
|
assign(jp.hid, HID::Joypad::GroupID::Trigger, 1, (int16_t)(triggerR - 32768));
|
|
|
|
|
|
|
|
assign(jp.hid, HID::Joypad::GroupID::Button, 0, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_A));
|
|
|
|
assign(jp.hid, HID::Joypad::GroupID::Button, 1, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_B));
|
|
|
|
assign(jp.hid, HID::Joypad::GroupID::Button, 2, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_X));
|
|
|
|
assign(jp.hid, HID::Joypad::GroupID::Button, 3, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_Y));
|
|
|
|
assign(jp.hid, HID::Joypad::GroupID::Button, 4, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK));
|
|
|
|
assign(jp.hid, HID::Joypad::GroupID::Button, 5, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_START));
|
|
|
|
assign(jp.hid, HID::Joypad::GroupID::Button, 6, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER));
|
|
|
|
assign(jp.hid, HID::Joypad::GroupID::Button, 7, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER));
|
|
|
|
assign(jp.hid, HID::Joypad::GroupID::Button, 8, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB));
|
|
|
|
assign(jp.hid, HID::Joypad::GroupID::Button, 9, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB));
|
|
|
|
assign(jp.hid, HID::Joypad::GroupID::Button, 10, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE));
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
devices.append(jp.hid);
|
2013-12-21 10:45:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto rumble(uint64_t id, bool enable) -> bool {
|
2014-01-05 09:59:17 +00:00
|
|
|
for(auto& jp : joypads) {
|
2015-06-12 13:14:38 +00:00
|
|
|
if(jp.hid->id() != id) continue;
|
2013-12-21 10:45:58 +00:00
|
|
|
|
2014-01-05 09:59:17 +00:00
|
|
|
XINPUT_VIBRATION vibration;
|
|
|
|
memset(&vibration, 0, sizeof(XINPUT_VIBRATION));
|
|
|
|
vibration.wLeftMotorSpeed = enable ? 65535 : 0; //low-frequency motor (0 = off, 65535 = max)
|
|
|
|
vibration.wRightMotorSpeed = enable ? 65535 : 0; //high-frequency motor (0 = off, 65535 = max)
|
|
|
|
XInputSetState(jp.id, &vibration);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2013-12-21 10:45:58 +00:00
|
|
|
}
|
|
|
|
|
Update to v103r15 release.
byuu says:
Changelog:
- ruby: rewrote the API interfaces for Video, Audio, Input
- ruby/audio: can now select the number of output channels (not useful
to higan, sorry)
- ruby/asio: various improvements
- tomoko: audio settings panel can now select separate audio devices
(for ASIO, OSS so far)
- tomoko: audio settings panel frequency and latency lists are
dynamically populated now
Note: due to the ruby API rewrite, most drivers will not compile. Right
now, the following work:
- video: Direct3D, XShm
- audio: ASIO, OSS
- input: Windows, SDL, Xlib
It takes a really long time to rewrite these (six hours to do the
above), so it's going to be a while before we're back at 100%
functionality again.
Errata:
- ASIO needs device(), setDevice()
- need to call setDevice() at program startup to populate
frequency/latency settings properly
- changing the device and/or frequency needs to update the emulator
resampler rates
The really hard part is going to be the last one: the only way to change
the emulator frequency is to flush all the audio streams and then
recompute all the coefficients for the resamplers. If this is called
during emulation, all audio streams will be erased and thus no sound
will be output. I'll most likely be forced to simply ignore
device/frequency changes until the user loads another game. It is at
least possible to toggle the latency dynamically.
2017-07-17 05:11:18 +00:00
|
|
|
auto initialize() -> bool {
|
2013-12-21 10:45:58 +00:00
|
|
|
if(!libxinput) libxinput = LoadLibraryA("xinput1_3.dll");
|
Update to bsnes v107.1 release.
byuu says:
Don't let the point release fool you, there are many significant changes in this
release. I will be keeping bsnes releases using a point system until the new
higan release is ready.
Changelog:
- GUI: added high DPI support
- GUI: fixed the state manager image preview
- Windows: added a new waveOut driver with support for dynamic rate control
- Windows: corrected the XAudio 2.1 dynamic rate control support [BearOso]
- Windows: corrected the Direct3D 9.0 fullscreen exclusive window centering
- Windows: fixed XInput controller support on Windows 10
- SFC: added high-level emulation for the DSP1, DSP2, DSP4, ST010, and Cx4
coprocessors
- SFC: fixed a slight rendering glitch in the intro to Megalomania
If the coprocessor firmware is missing, bsnes will fallback on HLE where it is
supported, which is everything other than SD Gundam GX and the two Hayazashi
Nidan Morita Shougi games.
The Windows dynamic rate control works best with Direct3D in fullscreen
exclusive mode. I recommend the waveOut driver over the XAudio 2.1 driver, as it
is not possible to target a single XAudio2 version on all Windows OS releases.
The waveOut driver should work everywhere out of the box.
Note that with DRC, the synchronization source is your monitor, so you will
want to be running at 60hz (NTSC) or 50hz (PAL). If you have an adaptive sync
monitor, you should instead use the WASAPI (exclusive) or ASIO audio driver.
2019-04-09 01:16:30 +00:00
|
|
|
if(!libxinput) libxinput = LoadLibraryA("xinput1_4.dll");
|
2013-12-21 10:45:58 +00:00
|
|
|
if(!libxinput) return false;
|
|
|
|
|
2014-01-05 09:59:17 +00:00
|
|
|
//XInputGetStateEx is an undocumented function; but is required to get the state of the guide button
|
|
|
|
//if for some reason it is not available, fall back on XInputGetState, which takes the same parameters
|
|
|
|
XInputGetStateEx = (pXInputGetStateEx)GetProcAddress(libxinput, oXInputGetStateEx);
|
|
|
|
XInputSetState = (pXInputSetState)GetProcAddress(libxinput, oXInputSetState);
|
|
|
|
if(!XInputGetStateEx) XInputGetStateEx = (pXInputGetStateEx)GetProcAddress(libxinput, oXInputGetState);
|
Update to v103r15 release.
byuu says:
Changelog:
- ruby: rewrote the API interfaces for Video, Audio, Input
- ruby/audio: can now select the number of output channels (not useful
to higan, sorry)
- ruby/asio: various improvements
- tomoko: audio settings panel can now select separate audio devices
(for ASIO, OSS so far)
- tomoko: audio settings panel frequency and latency lists are
dynamically populated now
Note: due to the ruby API rewrite, most drivers will not compile. Right
now, the following work:
- video: Direct3D, XShm
- audio: ASIO, OSS
- input: Windows, SDL, Xlib
It takes a really long time to rewrite these (six hours to do the
above), so it's going to be a while before we're back at 100%
functionality again.
Errata:
- ASIO needs device(), setDevice()
- need to call setDevice() at program startup to populate
frequency/latency settings properly
- changing the device and/or frequency needs to update the emulator
resampler rates
The really hard part is going to be the last one: the only way to change
the emulator frequency is to flush all the audio streams and then
recompute all the coefficients for the resamplers. If this is called
during emulation, all audio streams will be erased and thus no sound
will be output. I'll most likely be forced to simply ignore
device/frequency changes until the user loads another game. It is at
least possible to toggle the latency dynamically.
2017-07-17 05:11:18 +00:00
|
|
|
if(!XInputGetStateEx || !XInputSetState) return terminate(), false;
|
2013-12-21 10:45:58 +00:00
|
|
|
|
|
|
|
//XInput supports a maximum of four controllers
|
2014-01-05 09:59:17 +00:00
|
|
|
//add all four to devices list now. If they are not connected, they will not show up in poll() results
|
2018-05-24 02:14:17 +00:00
|
|
|
for(auto id : range(4)) {
|
2014-01-05 09:59:17 +00:00
|
|
|
Joypad jp;
|
|
|
|
jp.id = id;
|
2018-05-24 02:14:17 +00:00
|
|
|
jp.hid->setVendorID(0x045e);
|
|
|
|
jp.hid->setProductID(0x028e);
|
|
|
|
jp.hid->setPathID(id);
|
2015-06-12 13:14:38 +00:00
|
|
|
jp.hid->setRumble(true);
|
|
|
|
|
|
|
|
jp.hid->axes().append("LeftThumbX");
|
|
|
|
jp.hid->axes().append("LeftThumbY");
|
|
|
|
jp.hid->axes().append("RightThumbX");
|
|
|
|
jp.hid->axes().append("RightThumbY");
|
|
|
|
|
|
|
|
jp.hid->hats().append("HatX");
|
|
|
|
jp.hid->hats().append("HatY");
|
|
|
|
|
|
|
|
jp.hid->triggers().append("LeftTrigger");
|
|
|
|
jp.hid->triggers().append("RightTrigger");
|
|
|
|
|
|
|
|
jp.hid->buttons().append("A");
|
|
|
|
jp.hid->buttons().append("B");
|
|
|
|
jp.hid->buttons().append("X");
|
|
|
|
jp.hid->buttons().append("Y");
|
|
|
|
jp.hid->buttons().append("Back");
|
|
|
|
jp.hid->buttons().append("Start");
|
|
|
|
jp.hid->buttons().append("LeftShoulder");
|
|
|
|
jp.hid->buttons().append("RightShoulder");
|
|
|
|
jp.hid->buttons().append("LeftThumb");
|
|
|
|
jp.hid->buttons().append("RightThumb");
|
|
|
|
jp.hid->buttons().append("Guide");
|
2014-01-05 09:59:17 +00:00
|
|
|
|
|
|
|
joypads.append(jp);
|
2013-12-21 10:45:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
Update to v103r15 release.
byuu says:
Changelog:
- ruby: rewrote the API interfaces for Video, Audio, Input
- ruby/audio: can now select the number of output channels (not useful
to higan, sorry)
- ruby/asio: various improvements
- tomoko: audio settings panel can now select separate audio devices
(for ASIO, OSS so far)
- tomoko: audio settings panel frequency and latency lists are
dynamically populated now
Note: due to the ruby API rewrite, most drivers will not compile. Right
now, the following work:
- video: Direct3D, XShm
- audio: ASIO, OSS
- input: Windows, SDL, Xlib
It takes a really long time to rewrite these (six hours to do the
above), so it's going to be a while before we're back at 100%
functionality again.
Errata:
- ASIO needs device(), setDevice()
- need to call setDevice() at program startup to populate
frequency/latency settings properly
- changing the device and/or frequency needs to update the emulator
resampler rates
The really hard part is going to be the last one: the only way to change
the emulator frequency is to flush all the audio streams and then
recompute all the coefficients for the resamplers. If this is called
during emulation, all audio streams will be erased and thus no sound
will be output. I'll most likely be forced to simply ignore
device/frequency changes until the user loads another game. It is at
least possible to toggle the latency dynamically.
2017-07-17 05:11:18 +00:00
|
|
|
auto terminate() -> void {
|
2014-01-05 09:59:17 +00:00
|
|
|
if(!libxinput) return;
|
|
|
|
|
|
|
|
FreeLibrary(libxinput);
|
|
|
|
libxinput = nullptr;
|
2013-12-21 10:45:58 +00:00
|
|
|
}
|
|
|
|
};
|