Files
bsnes/ruby/input/shared/rawinput.cpp
T
Tim Allen a1b2fb0124 Update to v094r12 release.
byuu says:

Changelog:
* added driver selection
* added video scale + aspect correction settings
* added A/V sync + audio mute settings
* added configuration file
* fixed compilation bugs under Windows and Linux
* fixed window sizing
* removed HSU1
* the system menu stays as "System", because "Game Boy Advance" was too
  long a string for the smallest scale size
* some more stuff

You guys probably won't be ecstatic about the video sizing options, but
it's basically your choice of 1x, 2x or 4x scale with optional aspect
correction. 3x was intentionally skipped because it looks horrible on
hires SNES games. The window is resized and recentered upon loading
games. The window doesn't resize otherwise. I never really liked the way
v094 always left you with black screen areas and left you with
off-centered window positions.

I might go ahead and add the pseudo-fullscreen toggle that will jump
into 4x mode (respecting your aspect setting.)

Short-term:
* add input port changing support
* add other input types (mouse-based, etc)
* add save states
* add cheat codes
* add timing configuration (video/audio sync)
* add hotkeys (single state)

We can probably do a new release once the short-term items are
completed.

Long-term:
* add slotted cart loader (SGB, BSX, ST)
* add DIP switch selection window (NSS)
* add cheat code database
* add state manager
* add overscan masking

Not planned:
* video color adjustments (will allow emulated color vs raw color; but
  no more sliders)
* pixel shaders
* ananke integration (will need to make a command-line version to get my
  games in)
* fancy audio adjustment controls (resampler, latency, volume)
* input focus settings
* relocating game library (not hard, just don't feel like it)
* localization support (not enough users)
* window geometry memory
* anything else not in higan v094
2015-03-03 21:26:44 +11:00

163 lines
4.5 KiB
C++

#ifndef RUBY_INPUT_SHARED_RAWINPUT
#define RUBY_INPUT_SHARED_RAWINPUT
namespace ruby {
LRESULT CALLBACK RawInputWindowProc(HWND, UINT, WPARAM, LPARAM);
struct RawInput {
HANDLE mutex;
HWND hwnd;
bool ready = false;
bool initialized = false;
function<void (RAWINPUT*)> updateKeyboard;
function<void (RAWINPUT*)> updateMouse;
struct Device {
HANDLE handle;
string path;
enum class Type : unsigned { Keyboard, Mouse, Joypad } type;
uint16_t vendorID = 0;
uint16_t productID = 0;
bool isXInputDevice = false;
};
vector<Device> devices;
maybe<Device&> find(uint16_t vendorID, uint16_t productID) {
for(auto& device : devices) {
if(device.vendorID == vendorID && device.productID == productID) return device;
}
return nothing;
}
void scanDevices() {
devices.reset();
unsigned deviceCount = 0;
GetRawInputDeviceList(NULL, &deviceCount, sizeof(RAWINPUTDEVICELIST));
RAWINPUTDEVICELIST* list = new RAWINPUTDEVICELIST[deviceCount];
GetRawInputDeviceList(list, &deviceCount, sizeof(RAWINPUTDEVICELIST));
for(unsigned n = 0; n < deviceCount; n++) {
wchar_t path[4096];
unsigned size = sizeof(path) - 1;
GetRawInputDeviceInfo(list[n].hDevice, RIDI_DEVICENAME, &path, &size);
RID_DEVICE_INFO info;
info.cbSize = size = sizeof(RID_DEVICE_INFO);
GetRawInputDeviceInfo(list[n].hDevice, RIDI_DEVICEINFO, &info, &size);
Device device;
device.path = (const char*)utf8_t(path);
device.handle = list[n].hDevice;
if(info.dwType == RIM_TYPEKEYBOARD) {
device.type = Device::Type::Keyboard;
device.vendorID = 0;
device.productID = 1;
}
if(info.dwType == RIM_TYPEMOUSE) {
device.type = Device::Type::Mouse;
device.vendorID = 0;
device.productID = 2;
}
if(info.dwType == RIM_TYPEHID) {
//verify that this is a joypad device
if(info.hid.usUsagePage != 1 || (info.hid.usUsage != 4 && info.hid.usUsage != 5)) continue;
device.type = Device::Type::Joypad;
device.vendorID = info.hid.dwVendorId;
device.productID = info.hid.dwProductId;
if(device.path.find("IG_")) device.isXInputDevice = true; //"IG_" is only found inside XInput device paths
}
devices.append(device);
}
delete[] list;
}
LRESULT windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
if(msg != WM_INPUT) return DefWindowProc(hwnd, msg, wparam, lparam);
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) {
if(updateKeyboard) updateKeyboard(input);
}
if(input->header.dwType == RIM_TYPEMOUSE) {
if(updateMouse) updateMouse(input);
}
ReleaseMutex(mutex);
LRESULT result = DefRawInputProc(&input, size, sizeof(RAWINPUTHEADER));
delete[] input;
return result;
}
void main() {
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 = L"RawInputClass";
wc.lpszMenuName = 0;
wc.style = CS_VREDRAW | CS_HREDRAW;
RegisterClass(&wc);
hwnd = CreateWindow(L"RawInputClass", L"RawInputClass", WS_POPUP, 0, 0, 64, 64, 0, 0, GetModuleHandle(0), 0);
scanDevices();
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);
}
}
};
static RawInput rawinput;
DWORD WINAPI RawInputThreadProc(void*) {
rawinput.main();
return 0;
}
LRESULT CALLBACK RawInputWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
return rawinput.windowProc(hwnd, msg, wparam, lparam);
}
}
#endif