mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-04-02 10:42:14 -04:00
This represents a major code restructuring. The dot-based and scanline-based renderers are now split into two separate core libraries, asnes and bsnes. For now at least, these are -internal- names. I'm not entirely decided on how I'm going to handle releasing these two separate builds. Regardless, the folders need names. asnes has had all of the processor subfolders collapsed back into their parent folders. In other words, ppu's functions were moved into ppu/sppu, and then ppu was deleted, and then ppu/sppu became the new ppu. Repeat this for the cpu, smp and dsp and there you go. asnes/dsp also removed the DSP_STATE_MACHINE option. This was done for the sake of consistency with the rest of the core. asnes' debugger mode is currently extremely broken, but I will be fixing it in time. And for now, bsnes has kept the processor abstraction layer. I may keep it around, not sure yet. It doesn't hurt speed or anything, so I'm not too worried about making a decision right away. I may throw snesfilter, snesreader and supergameboy into this folder, just to have everything in one place. The alternate GUI forks are definitely going in there as dotnet, cocoa and python. Compiled output goes to the out/ folder now, to prevent conflicts with a file and folder named bsnes, for instance.
797 lines
26 KiB
C++
797 lines
26 KiB
C++
//RawInput driver
|
|
//author: byuu
|
|
|
|
//this driver utilizes RawInput (WM_INPUT) to capture keyboard and mouse input.
|
|
//although this requires WinXP or newer, it is the only way to uniquely identify
|
|
//and independently map multiple keyboards and mice. DirectInput merges all
|
|
//keyboards and mice into one device per.
|
|
//
|
|
//as WM_INPUT lacks specific RAWINPUT structures for gamepads, giving only raw
|
|
//data, and because DirectInput supports up to 16 joypads, DirectInput is used
|
|
//for joypad mapping.
|
|
//
|
|
//further, Xbox 360 controllers are explicitly detected and supported through
|
|
//XInput. this is because under DirectInput, the LT / RT (trigger) buttons are
|
|
//merged into a single Z-axis -- making it impossible to detect both buttons
|
|
//being pressed at the same time. with XInput, the state of both trigger
|
|
//buttons can be read independently.
|
|
//
|
|
//so in essence, this is actually more of a hybrid driver.
|
|
|
|
#define DIRECTINPUT_VERSION 0x0800
|
|
#include <dinput.h>
|
|
#include <xinput.h>
|
|
|
|
namespace ruby {
|
|
|
|
static DWORD WINAPI RawInputThreadProc(void*);
|
|
static LRESULT CALLBACK RawInputWindowProc(HWND, UINT, WPARAM, LPARAM);
|
|
|
|
class RawInput {
|
|
public:
|
|
HANDLE mutex;
|
|
HWND hwnd;
|
|
bool initialized;
|
|
bool ready;
|
|
|
|
struct Device {
|
|
HANDLE handle;
|
|
};
|
|
|
|
struct Keyboard : Device {
|
|
bool state[nall::Keyboard::Size];
|
|
|
|
void update(RAWINPUT *input) {
|
|
unsigned code = input->data.keyboard.MakeCode;
|
|
unsigned flags = input->data.keyboard.Flags;
|
|
|
|
#define map(id, flag, name) if(code == id) state[name] = (bool)(flags == flag);
|
|
map(0x0001, 0, nall::Keyboard::Escape)
|
|
map(0x003b, 0, nall::Keyboard::F1)
|
|
map(0x003c, 0, nall::Keyboard::F2)
|
|
map(0x003d, 0, nall::Keyboard::F3)
|
|
map(0x003e, 0, nall::Keyboard::F4)
|
|
map(0x003f, 0, nall::Keyboard::F5)
|
|
map(0x0040, 0, nall::Keyboard::F6)
|
|
map(0x0041, 0, nall::Keyboard::F7)
|
|
map(0x0042, 0, nall::Keyboard::F8)
|
|
map(0x0043, 0, nall::Keyboard::F9)
|
|
map(0x0044, 0, nall::Keyboard::F10)
|
|
map(0x0057, 0, nall::Keyboard::F11)
|
|
map(0x0058, 0, nall::Keyboard::F12)
|
|
|
|
map(0x0037, 2, nall::Keyboard::PrintScreen)
|
|
map(0x0046, 0, nall::Keyboard::ScrollLock)
|
|
map(0x001d, 4, nall::Keyboard::Pause)
|
|
map(0x0029, 0, nall::Keyboard::Tilde)
|
|
|
|
map(0x0002, 0, nall::Keyboard::Num1)
|
|
map(0x0003, 0, nall::Keyboard::Num2)
|
|
map(0x0004, 0, nall::Keyboard::Num3)
|
|
map(0x0005, 0, nall::Keyboard::Num4)
|
|
map(0x0006, 0, nall::Keyboard::Num5)
|
|
map(0x0007, 0, nall::Keyboard::Num6)
|
|
map(0x0008, 0, nall::Keyboard::Num7)
|
|
map(0x0009, 0, nall::Keyboard::Num8)
|
|
map(0x000a, 0, nall::Keyboard::Num9)
|
|
map(0x000b, 0, nall::Keyboard::Num0)
|
|
|
|
map(0x000c, 0, nall::Keyboard::Dash)
|
|
map(0x000d, 0, nall::Keyboard::Equal)
|
|
map(0x000e, 0, nall::Keyboard::Backspace)
|
|
|
|
map(0x0052, 2, nall::Keyboard::Insert)
|
|
map(0x0053, 2, nall::Keyboard::Delete)
|
|
map(0x0047, 2, nall::Keyboard::Home)
|
|
map(0x004f, 2, nall::Keyboard::End)
|
|
map(0x0049, 2, nall::Keyboard::PageUp)
|
|
map(0x0051, 2, nall::Keyboard::PageDown)
|
|
|
|
map(0x001e, 0, nall::Keyboard::A)
|
|
map(0x0030, 0, nall::Keyboard::B)
|
|
map(0x002e, 0, nall::Keyboard::C)
|
|
map(0x0020, 0, nall::Keyboard::D)
|
|
map(0x0012, 0, nall::Keyboard::E)
|
|
map(0x0021, 0, nall::Keyboard::F)
|
|
map(0x0022, 0, nall::Keyboard::G)
|
|
map(0x0023, 0, nall::Keyboard::H)
|
|
map(0x0017, 0, nall::Keyboard::I)
|
|
map(0x0024, 0, nall::Keyboard::J)
|
|
map(0x0025, 0, nall::Keyboard::K)
|
|
map(0x0026, 0, nall::Keyboard::L)
|
|
map(0x0032, 0, nall::Keyboard::M)
|
|
map(0x0031, 0, nall::Keyboard::N)
|
|
map(0x0018, 0, nall::Keyboard::O)
|
|
map(0x0019, 0, nall::Keyboard::P)
|
|
map(0x0010, 0, nall::Keyboard::Q)
|
|
map(0x0013, 0, nall::Keyboard::R)
|
|
map(0x001f, 0, nall::Keyboard::S)
|
|
map(0x0014, 0, nall::Keyboard::T)
|
|
map(0x0016, 0, nall::Keyboard::U)
|
|
map(0x002f, 0, nall::Keyboard::V)
|
|
map(0x0011, 0, nall::Keyboard::W)
|
|
map(0x002d, 0, nall::Keyboard::X)
|
|
map(0x0015, 0, nall::Keyboard::Y)
|
|
map(0x002c, 0, nall::Keyboard::Z)
|
|
|
|
map(0x001a, 0, nall::Keyboard::LeftBracket)
|
|
map(0x001b, 0, nall::Keyboard::RightBracket)
|
|
map(0x002b, 0, nall::Keyboard::Backslash)
|
|
map(0x0027, 0, nall::Keyboard::Semicolon)
|
|
map(0x0028, 0, nall::Keyboard::Apostrophe)
|
|
map(0x0033, 0, nall::Keyboard::Comma)
|
|
map(0x0034, 0, nall::Keyboard::Period)
|
|
map(0x0035, 0, nall::Keyboard::Slash)
|
|
|
|
map(0x004f, 0, nall::Keyboard::Keypad1)
|
|
map(0x0050, 0, nall::Keyboard::Keypad2)
|
|
map(0x0051, 0, nall::Keyboard::Keypad3)
|
|
map(0x004b, 0, nall::Keyboard::Keypad4)
|
|
map(0x004c, 0, nall::Keyboard::Keypad5)
|
|
map(0x004d, 0, nall::Keyboard::Keypad6)
|
|
map(0x0047, 0, nall::Keyboard::Keypad7)
|
|
map(0x0048, 0, nall::Keyboard::Keypad8)
|
|
map(0x0049, 0, nall::Keyboard::Keypad9)
|
|
map(0x0052, 0, nall::Keyboard::Keypad0)
|
|
|
|
map(0x0053, 0, nall::Keyboard::Point)
|
|
map(0x001c, 2, nall::Keyboard::Enter)
|
|
map(0x004e, 0, nall::Keyboard::Add)
|
|
map(0x004a, 0, nall::Keyboard::Subtract)
|
|
map(0x0037, 0, nall::Keyboard::Multiply)
|
|
map(0x0035, 2, nall::Keyboard::Divide)
|
|
|
|
map(0x0045, 0, nall::Keyboard::NumLock)
|
|
map(0x003a, 0, nall::Keyboard::CapsLock)
|
|
|
|
//Pause signals 0x1d:4 + 0x45:0, whereas NumLock signals only 0x45:0.
|
|
//this makes it impractical to detect both Pause+NumLock independently.
|
|
//workaround: always detect Pause; detect NumLock only when Pause is released.
|
|
if(state[nall::Keyboard::Pause]) state[nall::Keyboard::NumLock] = false;
|
|
|
|
map(0x0048, 2, nall::Keyboard::Up)
|
|
map(0x0050, 2, nall::Keyboard::Down)
|
|
map(0x004b, 2, nall::Keyboard::Left)
|
|
map(0x004d, 2, nall::Keyboard::Right)
|
|
|
|
map(0x000f, 0, nall::Keyboard::Tab)
|
|
map(0x001c, 0, nall::Keyboard::Return)
|
|
map(0x0039, 0, nall::Keyboard::Spacebar)
|
|
map(0x005d, 2, nall::Keyboard::Menu)
|
|
|
|
//merge left and right modifiers to one ID
|
|
if(code == 0x002a && flags == 0) state[nall::Keyboard::Shift] = 1; //left shift
|
|
if(code == 0x002a && flags == 1) state[nall::Keyboard::Shift] = 0;
|
|
if(code == 0x0036 && flags == 0) state[nall::Keyboard::Shift] = 1; //right shift
|
|
if(code == 0x0036 && flags == 1) state[nall::Keyboard::Shift] = 0;
|
|
|
|
if(code == 0x001d && flags == 0) state[nall::Keyboard::Control] = 1; //left control
|
|
if(code == 0x001d && flags == 1) state[nall::Keyboard::Control] = 0;
|
|
if(code == 0x001d && flags == 2) state[nall::Keyboard::Control] = 1; //right control
|
|
if(code == 0x001d && flags == 3) state[nall::Keyboard::Control] = 0;
|
|
|
|
if(code == 0x0038 && flags == 0) state[nall::Keyboard::Alt] = 1; //left alt
|
|
if(code == 0x0038 && flags == 1) state[nall::Keyboard::Alt] = 0;
|
|
if(code == 0x0038 && flags == 2) state[nall::Keyboard::Alt] = 1; //right alt
|
|
if(code == 0x0038 && flags == 3) state[nall::Keyboard::Alt] = 0;
|
|
|
|
if(code == 0x005b && flags == 2) state[nall::Keyboard::Super] = 1; //left super
|
|
if(code == 0x005b && flags == 3) state[nall::Keyboard::Super] = 0;
|
|
if(code == 0x005c && flags == 2) state[nall::Keyboard::Super] = 1; //right super
|
|
if(code == 0x005c && flags == 3) state[nall::Keyboard::Super] = 0;
|
|
#undef map
|
|
}
|
|
|
|
Keyboard() {
|
|
for(unsigned i = 0; i < nall::Keyboard::Size; i++) state[i] = false;
|
|
}
|
|
};
|
|
|
|
struct Mouse : Device {
|
|
signed xDistance;
|
|
signed yDistance;
|
|
signed zDistance;
|
|
unsigned buttonState;
|
|
|
|
void sync() {
|
|
xDistance = 0;
|
|
yDistance = 0;
|
|
zDistance = 0;
|
|
}
|
|
|
|
void update(RAWINPUT *input) {
|
|
if((input->data.mouse.usFlags & 1) == MOUSE_MOVE_RELATIVE) {
|
|
xDistance += input->data.mouse.lLastX;
|
|
yDistance += input->data.mouse.lLastY;
|
|
}
|
|
|
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) buttonState |= 1 << 0;
|
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP ) buttonState &=~ 1 << 0;
|
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) buttonState |= 1 << 2; //swap middle and right buttons,
|
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP ) buttonState &=~ 1 << 2; //for consistency with Linux:
|
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) buttonState |= 1 << 1; //left = 0, middle = 1, right = 2
|
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP ) buttonState &=~ 1 << 1;
|
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) buttonState |= 1 << 3;
|
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP ) buttonState &=~ 1 << 3;
|
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) buttonState |= 1 << 4;
|
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP ) buttonState &=~ 1 << 4;
|
|
|
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_WHEEL) {
|
|
zDistance += (int16_t)input->data.mouse.usButtonData;
|
|
}
|
|
}
|
|
|
|
Mouse() {
|
|
xDistance = yDistance = zDistance = 0;
|
|
buttonState = 0;
|
|
}
|
|
};
|
|
|
|
//keep track of gamepads for the sole purpose of distinguishing XInput devices
|
|
//from all other devices. this is necessary, as DirectInput does not provide
|
|
//a way to retrieve the necessary RIDI_DEVICENAME string.
|
|
struct Gamepad : Device {
|
|
bool isXInputDevice;
|
|
uint16_t vendorId;
|
|
uint16_t productId;
|
|
};
|
|
|
|
linear_vector<Keyboard> lkeyboard;
|
|
linear_vector<Mouse> lmouse;
|
|
linear_vector<Gamepad> lgamepad;
|
|
|
|
LRESULT window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
|
if(msg == WM_INPUT) {
|
|
unsigned size = 0;
|
|
GetRawInputData((HRAWINPUT)lparam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
|
|
RAWINPUT *input = new RAWINPUT[size];
|
|
GetRawInputData((HRAWINPUT)lparam, RID_INPUT, input, &size, sizeof(RAWINPUTHEADER));
|
|
WaitForSingleObject(mutex, INFINITE);
|
|
|
|
if(input->header.dwType == RIM_TYPEKEYBOARD) {
|
|
for(unsigned i = 0; i < lkeyboard.size(); i++) {
|
|
if(input->header.hDevice == lkeyboard[i].handle) {
|
|
lkeyboard[i].update(input);
|
|
break;
|
|
}
|
|
}
|
|
} else if(input->header.dwType == RIM_TYPEMOUSE) {
|
|
for(unsigned i = 0; i < lmouse.size(); i++) {
|
|
if(input->header.hDevice == lmouse[i].handle) {
|
|
lmouse[i].update(input);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ReleaseMutex(mutex);
|
|
//allow propogation of WM_INPUT message
|
|
LRESULT result = DefRawInputProc(&input, size, sizeof(RAWINPUTHEADER));
|
|
delete[] input;
|
|
return result;
|
|
}
|
|
|
|
return DefWindowProc(hwnd, msg, wparam, lparam);
|
|
}
|
|
|
|
//this is used to sort device IDs
|
|
struct DevicePool {
|
|
HANDLE handle;
|
|
char name[4096];
|
|
bool operator<(const DevicePool &pool) const { return strcmp(name, pool.name) < 0; }
|
|
};
|
|
|
|
int main() {
|
|
//create an invisible window to act as a sink, capturing all WM_INPUT messages
|
|
WNDCLASS wc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
|
|
wc.hCursor = LoadCursor(0, IDC_ARROW);
|
|
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
|
|
wc.hInstance = GetModuleHandle(0);
|
|
wc.lpfnWndProc = RawInputWindowProc;
|
|
wc.lpszClassName = "RawInputClass";
|
|
wc.lpszMenuName = 0;
|
|
wc.style = CS_VREDRAW | CS_HREDRAW;
|
|
RegisterClass(&wc);
|
|
|
|
hwnd = CreateWindow("RawInputClass", "RawInputClass", WS_POPUP,
|
|
0, 0, 64, 64, 0, 0, GetModuleHandle(0), 0);
|
|
|
|
//enumerate all HID devices
|
|
unsigned devices = 0;
|
|
GetRawInputDeviceList(NULL, &devices, sizeof(RAWINPUTDEVICELIST));
|
|
RAWINPUTDEVICELIST *list = new RAWINPUTDEVICELIST[devices];
|
|
GetRawInputDeviceList(list, &devices, sizeof(RAWINPUTDEVICELIST));
|
|
|
|
//sort all devices by name. this has two important properties:
|
|
//1) it consistently orders peripherals, so mapped IDs remain constant
|
|
//2) it sorts the virtual keyboard and mouse to the bottom of the list
|
|
// (real devices start with \\?\HID#, virtual with \\?\Root#)
|
|
DevicePool pool[devices];
|
|
for(unsigned i = 0; i < devices; i++) {
|
|
pool[i].handle = list[i].hDevice;
|
|
unsigned size = sizeof(pool[i].name) - 1;
|
|
GetRawInputDeviceInfo(list[i].hDevice, RIDI_DEVICENAME, &pool[i].name, &size);
|
|
}
|
|
nall::sort(pool, devices);
|
|
delete[] list;
|
|
|
|
for(unsigned i = 0; i < devices; i++) {
|
|
RID_DEVICE_INFO info;
|
|
info.cbSize = sizeof(RID_DEVICE_INFO);
|
|
|
|
unsigned size = info.cbSize;
|
|
GetRawInputDeviceInfo(pool[i].handle, RIDI_DEVICEINFO, &info, &size);
|
|
|
|
if(info.dwType == RIM_TYPEKEYBOARD) {
|
|
unsigned n = lkeyboard.size();
|
|
lkeyboard[n].handle = pool[i].handle;
|
|
} else if(info.dwType == RIM_TYPEMOUSE) {
|
|
unsigned n = lmouse.size();
|
|
lmouse[n].handle = pool[i].handle;
|
|
} else if(info.dwType == RIM_TYPEHID) {
|
|
//if this is a gamepad or joystick device ...
|
|
if(info.hid.usUsagePage == 1 && (info.hid.usUsage == 4 || info.hid.usUsage == 5)) {
|
|
//... then cache device information for later use
|
|
unsigned n = lgamepad.size();
|
|
lgamepad[n].handle = pool[i].handle;
|
|
lgamepad[n].vendorId = (uint16_t)info.hid.dwVendorId;
|
|
lgamepad[n].productId = (uint16_t)info.hid.dwProductId;
|
|
|
|
//per MSDN: XInput devices have "IG_" in their device strings,
|
|
//which is how they should be identified.
|
|
const char *p = strstr(pool[i].name, "IG_");
|
|
lgamepad[n].isXInputDevice = (bool)p;
|
|
}
|
|
}
|
|
}
|
|
|
|
RAWINPUTDEVICE device[2];
|
|
//capture all keyboard input
|
|
device[0].usUsagePage = 1;
|
|
device[0].usUsage = 6;
|
|
device[0].dwFlags = RIDEV_INPUTSINK;
|
|
device[0].hwndTarget = hwnd;
|
|
//capture all mouse input
|
|
device[1].usUsagePage = 1;
|
|
device[1].usUsage = 2;
|
|
device[1].dwFlags = RIDEV_INPUTSINK;
|
|
device[1].hwndTarget = hwnd;
|
|
RegisterRawInputDevices(device, 2, sizeof(RAWINPUTDEVICE));
|
|
|
|
WaitForSingleObject(mutex, INFINITE);
|
|
ready = true;
|
|
ReleaseMutex(mutex);
|
|
|
|
while(true) {
|
|
MSG msg;
|
|
GetMessage(&msg, hwnd, 0, 0);
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
RawInput() : initialized(false), ready(false) {
|
|
}
|
|
};
|
|
|
|
static RawInput rawinput;
|
|
|
|
DWORD WINAPI RawInputThreadProc(void*) {
|
|
return rawinput.main();
|
|
}
|
|
|
|
LRESULT CALLBACK RawInputWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
|
return rawinput.window_proc(hwnd, msg, wparam, lparam);
|
|
}
|
|
|
|
class XInput {
|
|
public:
|
|
HMODULE libxinput;
|
|
DWORD WINAPI (*pXInputGetState)(DWORD, XINPUT_STATE*);
|
|
|
|
struct Gamepad {
|
|
unsigned id;
|
|
|
|
int16_t hat;
|
|
int16_t axis[6];
|
|
bool button[10];
|
|
|
|
void poll(XINPUT_STATE &state) {
|
|
hat = Joypad::HatCenter;
|
|
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ) hat |= Joypad::HatUp;
|
|
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) hat |= Joypad::HatRight;
|
|
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ) hat |= Joypad::HatDown;
|
|
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ) hat |= Joypad::HatLeft;
|
|
|
|
axis[0] = (int16_t)state.Gamepad.sThumbLX;
|
|
axis[1] = (int16_t)state.Gamepad.sThumbLY;
|
|
axis[2] = (int16_t)state.Gamepad.sThumbRX;
|
|
axis[3] = (int16_t)state.Gamepad.sThumbRY;
|
|
|
|
//transform left and right trigger ranges:
|
|
//from: 0 (low, eg released) to 255 (high, eg pressed all the way down)
|
|
//to: +32767 (low) to -32768 (high)
|
|
uint16_t triggerX = state.Gamepad.bLeftTrigger;
|
|
uint16_t triggerY = state.Gamepad.bRightTrigger;
|
|
|
|
triggerX = (triggerX << 8) | triggerX;
|
|
triggerY = (triggerY << 8) | triggerY;
|
|
|
|
axis[4] = (~triggerX) - 32768;
|
|
axis[5] = (~triggerY) - 32768;
|
|
|
|
button[0] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_A);
|
|
button[1] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_B);
|
|
button[2] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_X);
|
|
button[3] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_Y);
|
|
button[4] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK);
|
|
button[5] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_START);
|
|
button[6] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER);
|
|
button[7] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER);
|
|
button[8] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB);
|
|
button[9] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB);
|
|
}
|
|
|
|
Gamepad() {
|
|
hat = Joypad::HatCenter;
|
|
for(unsigned n = 0; n < 6; n++) axis[n] = 0;
|
|
for(unsigned n = 0; n < 10; n++) button[n] = false;
|
|
}
|
|
};
|
|
|
|
linear_vector<Gamepad> lgamepad;
|
|
|
|
void poll() {
|
|
if(!pXInputGetState) return;
|
|
|
|
for(unsigned i = 0; i < lgamepad.size(); i++) {
|
|
XINPUT_STATE state;
|
|
DWORD result = pXInputGetState(lgamepad[i].id, &state);
|
|
if(result == ERROR_SUCCESS) lgamepad[i].poll(state);
|
|
}
|
|
}
|
|
|
|
void init() {
|
|
if(!pXInputGetState) return;
|
|
|
|
//XInput only supports up to four controllers
|
|
for(unsigned i = 0; i <= 3; i++) {
|
|
XINPUT_STATE state;
|
|
DWORD result = pXInputGetState(i, &state);
|
|
if(result == ERROR_SUCCESS) {
|
|
//valid controller detected, add to gamepad list
|
|
unsigned n = lgamepad.size();
|
|
lgamepad[n].id = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
XInput() : pXInputGetState(0) {
|
|
//bind xinput1 dynamically, as it does not ship with Windows Vista or below
|
|
libxinput = LoadLibraryA("xinput1_3.dll");
|
|
if(!libxinput) libxinput = LoadLibraryA("xinput1_2.dll");
|
|
if(!libxinput) libxinput = LoadLibraryA("xinput1_1.dll");
|
|
if(!libxinput) return;
|
|
pXInputGetState = (DWORD WINAPI (*)(DWORD, XINPUT_STATE*))GetProcAddress(libxinput, "XInputGetState");
|
|
}
|
|
|
|
~XInput() {
|
|
if(libxinput) FreeLibrary(libxinput);
|
|
}
|
|
};
|
|
|
|
static BOOL CALLBACK DirectInput_EnumJoypadsCallback(const DIDEVICEINSTANCE*, void*);
|
|
static BOOL CALLBACK DirectInput_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE*, void*);
|
|
|
|
class DirectInput {
|
|
public:
|
|
HWND handle;
|
|
LPDIRECTINPUT8 context;
|
|
struct Gamepad {
|
|
LPDIRECTINPUTDEVICE8 handle;
|
|
|
|
int16_t hat[4];
|
|
int16_t axis[6];
|
|
bool button[128];
|
|
|
|
void poll(DIJOYSTATE2 &state) {
|
|
//POV hats
|
|
for(unsigned n = 0; n < 4; n++) {
|
|
hat[n] = Joypad::HatCenter;
|
|
|
|
//POV value is in clockwise-hundredth degree units
|
|
unsigned pov = state.rgdwPOV[n];
|
|
|
|
//some drivers report a centered POV hat as -1U, others as 65535U.
|
|
//>= 36000 will match both, as well as invalid ranges.
|
|
if(pov >= 36000) continue;
|
|
|
|
if(pov >= 31500 || pov <= 4500) hat[n] |= Joypad::HatUp;
|
|
if(pov >= 4500 && pov <= 13500) hat[n] |= Joypad::HatRight;
|
|
if(pov >= 13500 && pov <= 22500) hat[n] |= Joypad::HatDown;
|
|
if(pov >= 22500 && pov <= 31500) hat[n] |= Joypad::HatLeft;
|
|
}
|
|
|
|
//axes
|
|
axis[0] = state.lX;
|
|
axis[1] = state.lY;
|
|
axis[2] = state.lZ;
|
|
axis[3] = state.lRx;
|
|
axis[4] = state.lRy;
|
|
axis[5] = state.lRz;
|
|
|
|
//buttons
|
|
for(unsigned n = 0; n < 128; n++) {
|
|
button[n] = (bool)state.rgbButtons[n];
|
|
}
|
|
}
|
|
|
|
Gamepad() {
|
|
handle = 0;
|
|
for(unsigned n = 0; n < 4; n++) hat[n] = Joypad::HatCenter;
|
|
for(unsigned n = 0; n < 6; n++) axis[n] = 0;
|
|
for(unsigned n = 0; n < 128; n++) button[n] = false;
|
|
}
|
|
};
|
|
linear_vector<Gamepad> lgamepad;
|
|
|
|
void poll() {
|
|
for(unsigned i = 0; i < lgamepad.size(); i++) {
|
|
if(FAILED(lgamepad[i].handle->Poll())) {
|
|
lgamepad[i].handle->Acquire();
|
|
continue;
|
|
}
|
|
|
|
DIJOYSTATE2 state;
|
|
lgamepad[i].handle->GetDeviceState(sizeof(DIJOYSTATE2), &state);
|
|
lgamepad[i].poll(state);
|
|
}
|
|
}
|
|
|
|
bool init_joypad(const DIDEVICEINSTANCE *instance) {
|
|
//if this is an XInput device, do not acquire it via DirectInput ...
|
|
//the XInput driver above will handle said device.
|
|
for(unsigned i = 0; i < rawinput.lgamepad.size(); i++) {
|
|
uint32_t guid = MAKELONG(rawinput.lgamepad[i].vendorId, rawinput.lgamepad[i].productId);
|
|
if(guid == instance->guidProduct.Data1) {
|
|
if(rawinput.lgamepad[i].isXInputDevice == true) {
|
|
return DIENUM_CONTINUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(FAILED(context->CreateDevice(instance->guidInstance, &device, 0))) {
|
|
return DIENUM_CONTINUE;
|
|
}
|
|
|
|
device->SetDataFormat(&c_dfDIJoystick2);
|
|
device->SetCooperativeLevel(handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
|
|
device->EnumObjects(DirectInput_EnumJoypadAxesCallback, (void*)this, DIDFT_ABSAXIS);
|
|
unsigned n = lgamepad.size();
|
|
lgamepad[n].handle = device;
|
|
return DIENUM_CONTINUE;
|
|
}
|
|
|
|
bool init_axis(const DIDEVICEOBJECTINSTANCE *instance) {
|
|
DIPROPRANGE range;
|
|
range.diph.dwSize = sizeof(DIPROPRANGE);
|
|
range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
|
range.diph.dwHow = DIPH_BYID;
|
|
range.diph.dwObj = instance->dwType;
|
|
range.lMin = -32768;
|
|
range.lMax = +32767;
|
|
device->SetProperty(DIPROP_RANGE, &range.diph);
|
|
return DIENUM_CONTINUE;
|
|
}
|
|
|
|
void init(HWND handle_) {
|
|
handle = handle_;
|
|
DirectInput8Create(GetModuleHandle(0), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&context, 0);
|
|
context->EnumDevices(DI8DEVCLASS_GAMECTRL, DirectInput_EnumJoypadsCallback, (void*)this, DIEDFL_ATTACHEDONLY);
|
|
}
|
|
|
|
void term() {
|
|
for(unsigned i = 0; i < lgamepad.size(); i++) {
|
|
lgamepad[i].handle->Unacquire();
|
|
lgamepad[i].handle->Release();
|
|
}
|
|
lgamepad.reset();
|
|
|
|
if(context) {
|
|
context->Release();
|
|
context = 0;
|
|
}
|
|
}
|
|
|
|
private:
|
|
LPDIRECTINPUTDEVICE8 device;
|
|
};
|
|
|
|
BOOL CALLBACK DirectInput_EnumJoypadsCallback(const DIDEVICEINSTANCE *instance, void *p) {
|
|
return ((DirectInput*)p)->init_joypad(instance);
|
|
}
|
|
|
|
BOOL CALLBACK DirectInput_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE *instance, void *p) {
|
|
return ((DirectInput*)p)->init_axis(instance);
|
|
}
|
|
|
|
class pInputRaw {
|
|
public:
|
|
XInput xinput;
|
|
DirectInput dinput;
|
|
|
|
bool acquire_mouse;
|
|
bool cursor_visible;
|
|
|
|
struct {
|
|
HWND handle;
|
|
} settings;
|
|
|
|
bool cap(const string& name) {
|
|
if(name == Input::Handle) return true;
|
|
if(name == Input::KeyboardSupport) return true;
|
|
if(name == Input::MouseSupport) return true;
|
|
if(name == Input::JoypadSupport) return true;
|
|
return false;
|
|
}
|
|
|
|
any get(const string& name) {
|
|
if(name == Input::Handle) return (uintptr_t)settings.handle;
|
|
return false;
|
|
}
|
|
|
|
bool set(const string& name, const any& value) {
|
|
if(name == Input::Handle) {
|
|
settings.handle = (HWND)any_cast<uintptr_t>(value);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool acquire() {
|
|
acquire_mouse = true;
|
|
if(cursor_visible == true) {
|
|
ShowCursor(cursor_visible = false);
|
|
}
|
|
return acquired();
|
|
}
|
|
|
|
bool unacquire() {
|
|
acquire_mouse = false;
|
|
ReleaseCapture();
|
|
ClipCursor(NULL);
|
|
if(cursor_visible == false) {
|
|
ShowCursor(cursor_visible = true);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool acquired() {
|
|
if(acquire_mouse == true) {
|
|
SetFocus(settings.handle);
|
|
SetCapture(settings.handle);
|
|
RECT rc;
|
|
GetWindowRect(settings.handle, &rc);
|
|
ClipCursor(&rc);
|
|
}
|
|
return GetCapture() == settings.handle;
|
|
}
|
|
|
|
bool poll(int16_t *table) {
|
|
memset(table, 0, Scancode::Limit * sizeof(int16_t));
|
|
|
|
WaitForSingleObject(rawinput.mutex, INFINITE);
|
|
|
|
//=========
|
|
//Keyboards
|
|
//=========
|
|
for(unsigned i = 0; i < min(rawinput.lkeyboard.size(), (unsigned)Keyboard::Count); i++) {
|
|
for(unsigned n = 0; n < nall::Keyboard::Size; n++) {
|
|
table[keyboard(i).key(n)] = rawinput.lkeyboard[i].state[n];
|
|
}
|
|
}
|
|
|
|
//====
|
|
//Mice
|
|
//====
|
|
for(unsigned i = 0; i < min(rawinput.lmouse.size(), (unsigned)Mouse::Count); i++) {
|
|
table[mouse(i).axis(0)] = rawinput.lmouse[i].xDistance;
|
|
table[mouse(i).axis(1)] = rawinput.lmouse[i].yDistance;
|
|
table[mouse(i).axis(2)] = rawinput.lmouse[i].zDistance;
|
|
|
|
for(unsigned n = 0; n < min(5U, (unsigned)Mouse::Buttons); n++) {
|
|
table[mouse(i).button(n)] = (bool)(rawinput.lmouse[i].buttonState & (1 << n));
|
|
}
|
|
|
|
rawinput.lmouse[i].sync();
|
|
}
|
|
|
|
ReleaseMutex(rawinput.mutex);
|
|
|
|
unsigned joy = 0;
|
|
|
|
//==================
|
|
//XInput controllers
|
|
//==================
|
|
xinput.poll();
|
|
for(unsigned i = 0; i < xinput.lgamepad.size(); i++) {
|
|
if(joy >= Joypad::Count) break;
|
|
|
|
table[joypad(i).hat(0)] = xinput.lgamepad[i].hat;
|
|
|
|
for(unsigned axis = 0; axis < min(6U, (unsigned)Joypad::Axes); axis++) {
|
|
table[joypad(i).axis(axis)] = xinput.lgamepad[i].axis[axis];
|
|
}
|
|
|
|
for(unsigned button = 0; button < min(10U, (unsigned)Joypad::Buttons); button++) {
|
|
table[joypad(i).button(button)] = xinput.lgamepad[i].button[button];
|
|
}
|
|
}
|
|
|
|
//=======================
|
|
//DirectInput controllers
|
|
//=======================
|
|
dinput.poll();
|
|
for(unsigned i = 0; i < dinput.lgamepad.size(); i++) {
|
|
if(joy >= Joypad::Count) break;
|
|
|
|
for(unsigned hat = 0; hat < min(4U, (unsigned)Joypad::Hats); hat++) {
|
|
table[joypad(i).hat(hat)] = dinput.lgamepad[i].hat[hat];
|
|
}
|
|
|
|
for(unsigned axis = 0; axis < min(6U, (unsigned)Joypad::Axes); axis++) {
|
|
table[joypad(i).axis(axis)] = dinput.lgamepad[i].axis[axis];
|
|
}
|
|
|
|
for(unsigned button = 0; button < min(128U, (unsigned)Joypad::Buttons); button++) {
|
|
table[joypad(i).button(button)] = dinput.lgamepad[i].button[button];
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool init() {
|
|
//only spawn RawInput processing thread one time
|
|
if(rawinput.initialized == false) {
|
|
rawinput.initialized = true;
|
|
rawinput.mutex = CreateMutex(NULL, FALSE, NULL);
|
|
CreateThread(NULL, 0, RawInputThreadProc, 0, 0, NULL);
|
|
|
|
//RawInput device calibration needs to finish before initializing DirectInput;
|
|
//as it needs device GUIDs to distinguish XInput devices from ordinary joypads.
|
|
bool ready = false;
|
|
do {
|
|
Sleep(10);
|
|
WaitForSingleObject(rawinput.mutex, INFINITE);
|
|
ready = rawinput.ready;
|
|
ReleaseMutex(rawinput.mutex);
|
|
} while(ready == false);
|
|
}
|
|
|
|
xinput.init();
|
|
dinput.init(settings.handle);
|
|
|
|
acquire_mouse = false;
|
|
cursor_visible = true;
|
|
return true;
|
|
}
|
|
|
|
void term() {
|
|
unacquire();
|
|
dinput.term();
|
|
}
|
|
|
|
pInputRaw() {
|
|
}
|
|
};
|
|
|
|
DeclareInput(Raw)
|
|
|
|
};
|