diff options
author | 2016-03-02 21:49:49 +0100 | |
---|---|---|
committer | 2016-03-02 21:49:49 +0100 | |
commit | 9fe9f6d93e672bcd926f9e7461774bc9cc22e094 (patch) | |
tree | 3911b611dd010a4108b90fb8b8c82116eec21117 /src/osd/modules/input/input_rawinput.cpp | |
parent | dbdf21ee465acadf3e643d62d6bedfcc297ada85 (diff) | |
parent | 1914702e6fae052668df5b068a502bca57c9a3b0 (diff) |
Merge remote-tracking branch 'refs/remotes/mamedev/master'
# Resolved Conflicts:
# src/osd/modules/render/d3d/d3dhlsl.cpp
# src/osd/windows/winmain.cpp
Diffstat (limited to 'src/osd/modules/input/input_rawinput.cpp')
-rw-r--r-- | src/osd/modules/input/input_rawinput.cpp | 648 |
1 files changed, 648 insertions, 0 deletions
diff --git a/src/osd/modules/input/input_rawinput.cpp b/src/osd/modules/input/input_rawinput.cpp new file mode 100644 index 00000000000..5f930e5d780 --- /dev/null +++ b/src/osd/modules/input/input_rawinput.cpp @@ -0,0 +1,648 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles, Brad Hughes +//============================================================ +// +// input_rawinput.cpp - Windows RawInput input implementation +// +//============================================================ + +#include "input_module.h" +#include "modules/osdmodule.h" + +#if defined(OSD_WINDOWS) + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <winioctl.h> +#include <tchar.h> +#undef interface + +#include <mutex> + +// MAME headers +#include "emu.h" +#include "osdepend.h" +#include "ui/ui.h" +#include "strconv.h" + +// MAMEOS headers +#include "winmain.h" +#include "window.h" + +#include "input_common.h" +#include "input_windows.h" + +//============================================================ +// MACROS +//============================================================ + +#ifdef UNICODE +#define UNICODE_SUFFIX "W" +#else +#define UNICODE_SUFFIX "A" +#endif + +// RawInput APIs +typedef /*WINUSERAPI*/ INT(WINAPI *get_rawinput_device_list_ptr)(OUT PRAWINPUTDEVICELIST pRawInputDeviceList, IN OUT PINT puiNumDevices, IN UINT cbSize); +typedef /*WINUSERAPI*/ INT(WINAPI *get_rawinput_data_ptr)(IN HRAWINPUT hRawInput, IN UINT uiCommand, OUT LPVOID pData, IN OUT PINT pcbSize, IN UINT cbSizeHeader); +typedef /*WINUSERAPI*/ INT(WINAPI *get_rawinput_device_info_ptr)(IN HANDLE hDevice, IN UINT uiCommand, OUT LPVOID pData, IN OUT PINT pcbSize); +typedef /*WINUSERAPI*/ BOOL(WINAPI *register_rawinput_devices_ptr)(IN PCRAWINPUTDEVICE pRawInputDevices, IN UINT uiNumDevices, IN UINT cbSize); + +//============================================================ +// reg_query_string +//============================================================ + +static TCHAR *reg_query_string(HKEY key, const TCHAR *path) +{ + TCHAR *buffer; + DWORD datalen; + LONG result; + + // first query to get the length + result = RegQueryValueEx(key, path, NULL, NULL, NULL, &datalen); + if (result != ERROR_SUCCESS) + return NULL; + + // allocate a buffer + buffer = global_alloc_array(TCHAR, datalen + sizeof(*buffer)); + buffer[datalen / sizeof(*buffer)] = 0; + + // now get the actual data + result = RegQueryValueEx(key, path, NULL, NULL, (LPBYTE)buffer, &datalen); + if (result == ERROR_SUCCESS) + return buffer; + + // otherwise return a NULL buffer + global_free_array(buffer); + return NULL; +} + +//============================================================ +// rawinput_device_improve_name +//============================================================ + +static TCHAR *rawinput_device_improve_name(TCHAR *name) +{ + static const TCHAR usbbasepath[] = TEXT("SYSTEM\\CurrentControlSet\\Enum\\USB"); + static const TCHAR basepath[] = TEXT("SYSTEM\\CurrentControlSet\\Enum\\"); + TCHAR *regstring = NULL; + TCHAR *parentid = NULL; + TCHAR *regpath = NULL; + const TCHAR *chsrc; + HKEY regkey = NULL; + int usbindex; + TCHAR *chdst; + LONG result; + + // The RAW name received is formatted as: + // \??\type-id#hardware-id#instance-id#{DeviceClasses-id} + // XP starts with "\??\" + // Vista64 starts with "\\?\" + + // ensure the name is something we can handle + if (_tcsncmp(name, TEXT("\\\\?\\"), 4) != 0 && _tcsncmp(name, TEXT("\\??\\"), 4) != 0) + return name; + + // allocate a temporary string and concatenate the base path plus the name + regpath = global_alloc_array(TCHAR, _tcslen(basepath) + 1 + _tcslen(name)); + _tcscpy(regpath, basepath); + chdst = regpath + _tcslen(regpath); + + // convert all # to \ in the name + for (chsrc = name + 4; *chsrc != 0; chsrc++) + *chdst++ = (*chsrc == '#') ? '\\' : *chsrc; + *chdst = 0; + + // remove the final chunk + chdst = _tcsrchr(regpath, '\\'); + if (chdst == NULL) + goto exit; + *chdst = 0; + + // now try to open the registry key + result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, ®key); + if (result != ERROR_SUCCESS) + goto exit; + + // fetch the device description; if it exists, we are finished + regstring = reg_query_string(regkey, TEXT("DeviceDesc")); + if (regstring != NULL) + goto convert; + + // close this key + RegCloseKey(regkey); + regkey = NULL; + + // if the key name does not contain "HID", it's not going to be in the USB tree; give up + if (_tcsstr(regpath, TEXT("HID")) == NULL) + goto exit; + + // extract the expected parent ID from the regpath + parentid = _tcsrchr(regpath, '\\'); + if (parentid == NULL) + goto exit; + parentid++; + + // open the USB key + result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, usbbasepath, 0, KEY_READ, ®key); + if (result != ERROR_SUCCESS) + goto exit; + + // enumerate the USB key + for (usbindex = 0; result == ERROR_SUCCESS && regstring == NULL; usbindex++) + { + TCHAR keyname[MAX_PATH]; + DWORD namelen; + + // get the next enumerated subkey and scan it + namelen = ARRAY_LENGTH(keyname) - 1; + result = RegEnumKeyEx(regkey, usbindex, keyname, &namelen, NULL, NULL, NULL, NULL); + if (result == ERROR_SUCCESS) + { + LONG subresult; + int subindex; + HKEY subkey; + + // open the subkey + subresult = RegOpenKeyEx(regkey, keyname, 0, KEY_READ, &subkey); + if (subresult != ERROR_SUCCESS) + continue; + + // enumerate the subkey + for (subindex = 0; subresult == ERROR_SUCCESS && regstring == NULL; subindex++) + { + // get the next enumerated subkey and scan it + namelen = ARRAY_LENGTH(keyname) - 1; + subresult = RegEnumKeyEx(subkey, subindex, keyname, &namelen, NULL, NULL, NULL, NULL); + if (subresult == ERROR_SUCCESS) + { + TCHAR *endparentid; + LONG endresult; + HKEY endkey; + + // open this final key + endresult = RegOpenKeyEx(subkey, keyname, 0, KEY_READ, &endkey); + if (endresult != ERROR_SUCCESS) + continue; + + // do we have a match? + endparentid = reg_query_string(endkey, TEXT("ParentIdPrefix")); + if (endparentid != NULL && _tcsncmp(parentid, endparentid, _tcslen(endparentid)) == 0) + regstring = reg_query_string(endkey, TEXT("DeviceDesc")); + + // free memory and close the key + if (endparentid != NULL) + global_free_array(endparentid); + RegCloseKey(endkey); + } + } + + // close the subkey + RegCloseKey(subkey); + } + } + + // if we didn't find anything, go to the exit + if (regstring == NULL) + goto exit; + +convert: + // replace the name with the nicer one + global_free_array(name); + + // remove anything prior to the final semicolon + chsrc = _tcsrchr(regstring, ';'); + if (chsrc != NULL) + chsrc++; + else + chsrc = regstring; + name = global_alloc_array(TCHAR, _tcslen(chsrc) + 1); + _tcscpy(name, chsrc); + +exit: + if (regstring != NULL) + global_free_array(regstring); + if (regpath != NULL) + global_free_array(regpath); + if (regkey != NULL) + RegCloseKey(regkey); + + return name; +} + + +//============================================================ +// rawinput_device class +//============================================================ + +class rawinput_device : public event_based_device<RAWINPUT> +{ +private: + HANDLE m_handle; + +public: + rawinput_device(running_machine& machine, const char* name, input_device_class deviceclass, input_module& module) + : event_based_device(machine, name, deviceclass, module) + { + } + + HANDLE device_handle() { return m_handle; } + void set_handle(HANDLE handle) { m_handle = handle; } +}; + +//============================================================ +// rawinput_keyboard_device +//============================================================ + +class rawinput_keyboard_device : public rawinput_device +{ +public: + keyboard_state keyboard; + + rawinput_keyboard_device(running_machine& machine, const char* name, input_module& module) + : rawinput_device(machine, name, DEVICE_CLASS_KEYBOARD, module), + keyboard({0}) + { + } + + void reset() override + { + memset(&keyboard, 0, sizeof(keyboard)); + } + + void process_event(RAWINPUT &rawinput) override + { + // determine the full DIK-compatible scancode + UINT8 scancode = (rawinput.data.keyboard.MakeCode & 0x7f) | ((rawinput.data.keyboard.Flags & RI_KEY_E0) ? 0x80 : 0x00); + + // scancode 0xaa is a special shift code we need to ignore + if (scancode == 0xaa) + return; + + // set or clear the key + keyboard.state[scancode] = (rawinput.data.keyboard.Flags & RI_KEY_BREAK) ? 0x00 : 0x80; + } +}; + +//============================================================ +// rawinput_mouse_device +//============================================================ + +struct rawinput_mouse_state +{ + int raw_x; + int raw_y; + int raw_z; +}; + +class rawinput_mouse_device : public rawinput_device +{ +private: + std::mutex m_device_lock; +public: + //rawinput_mouse_state raw_mouse; + mouse_state mouse; + + rawinput_mouse_device(running_machine& machine, const char* name, input_module& module) + : rawinput_device(machine, name, DEVICE_CLASS_MOUSE, module), + mouse({0}) + { + } + + void poll() override + { + mouse.lX = 0; + mouse.lY = 0; + mouse.lZ = 0; + + rawinput_device::poll(); + + //std::lock_guard<std::mutex> scope_lock(m_device_lock); + + //// copy the accumulated raw state to the actual state + //mouse.lX = raw_mouse.raw_x; + //mouse.lY = raw_mouse.raw_y; + //mouse.lZ = raw_mouse.raw_z; + //raw_mouse.raw_x = 0; + //raw_mouse.raw_y = 0; + //raw_mouse.raw_z = 0; + + //osd_printf_verbose("At poll: lX=%d, lY=%d, lZ=%d\n", (int)mouse.lX, (int)mouse.lY, (int)mouse.lZ); + } + + void reset() override + { + memset(&mouse, 0, sizeof(mouse)); + } + + void process_event(RAWINPUT &rawinput) override + { + // If this data was intended for a rawinput lightgun, ignore it. + if (rawinput.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) + return; + + mouse.lX += rawinput.data.mouse.lLastX * INPUT_RELATIVE_PER_PIXEL; + mouse.lY += rawinput.data.mouse.lLastY * INPUT_RELATIVE_PER_PIXEL; + + /*osd_printf_verbose( + "Mouse Abs : lastX = %d, lastY = %d, rawx = %d, rawy = %d\n", + (int)rawinput.data.mouse.lLastX, + (int)rawinput.data.mouse.lLastY, + (int)raw_mouse.raw_x, + (int)raw_mouse.raw_y);*/ + + // update zaxis + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_WHEEL) + mouse.lZ += (INT16)rawinput.data.mouse.usButtonData * INPUT_RELATIVE_PER_PIXEL; + + // update the button states; always update the corresponding mouse buttons + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) mouse.rgbButtons[0] = 0x80; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP) mouse.rgbButtons[0] = 0x00; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) mouse.rgbButtons[1] = 0x80; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP) mouse.rgbButtons[1] = 0x00; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) mouse.rgbButtons[2] = 0x80; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP) mouse.rgbButtons[2] = 0x00; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) mouse.rgbButtons[3] = 0x80; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP) mouse.rgbButtons[3] = 0x00; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) mouse.rgbButtons[4] = 0x80; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP) mouse.rgbButtons[4] = 0x00; + } +}; + +//============================================================ +// rawinput_module - base class for rawinput modules +//============================================================ + +class rawinput_module : public wininput_module +{ +private: + // RawInput variables + get_rawinput_device_list_ptr get_rawinput_device_list; + get_rawinput_data_ptr get_rawinput_data; + get_rawinput_device_info_ptr get_rawinput_device_info; + register_rawinput_devices_ptr register_rawinput_devices; + std::mutex m_module_lock; + +public: + rawinput_module(const char *type, const char* name) + : wininput_module(type, name), + get_rawinput_device_list(nullptr), + get_rawinput_data(nullptr), + get_rawinput_device_info(nullptr), + register_rawinput_devices(nullptr) + { + } + + void input_init(running_machine &machine) override + { + // get the number of devices, allocate a device list, and fetch it + int device_count = 0; + if ((*get_rawinput_device_list)(NULL, &device_count, sizeof(RAWINPUTDEVICELIST)) != 0) + return; + + if (device_count == 0) + return; + + auto rawinput_devices = std::make_unique<RAWINPUTDEVICELIST[]>(device_count); + if ((*get_rawinput_device_list)(rawinput_devices.get(), &device_count, sizeof(RAWINPUTDEVICELIST)) == -1) + return; + + // iterate backwards through devices; new devices are added at the head + for (int devnum = device_count - 1; devnum >= 0; devnum--) + { + RAWINPUTDEVICELIST *device = &rawinput_devices[devnum]; + add_rawinput_device(machine, device); + } + + // don't enable global inputs when debugging + if (!machine.options().debug()) + { + m_global_inputs_enabled = downcast<windows_options &>(machine.options()).global_inputs(); + } + + // If we added no devices, no need to register for notifications + if (devicelist()->size() == 0) + return; + + // finally, register to receive raw input WM_INPUT messages if we found devices + RAWINPUTDEVICE registration; + registration.usUsagePage = usagepage(); + registration.usUsage = usage(); + registration.dwFlags = m_global_inputs_enabled ? 0x00000100 : 0; + registration.hwndTarget = win_window_list->m_hwnd; + + // register the device + (*register_rawinput_devices)(®istration, 1, sizeof(registration)); + } + +protected: + virtual void add_rawinput_device(running_machine& machine, RAWINPUTDEVICELIST * device) = 0; + virtual USHORT usagepage() = 0; + virtual USHORT usage() = 0; + + int init_internal() override + { + HMODULE user32; + + // look in user32 for the raw input APIs + user32 = LoadLibrary(TEXT("user32.dll")); + if (user32 == NULL) + return 1; + + // look up the entry points + register_rawinput_devices = (register_rawinput_devices_ptr)GetProcAddress(user32, "RegisterRawInputDevices"); + get_rawinput_device_list = (get_rawinput_device_list_ptr)GetProcAddress(user32, "GetRawInputDeviceList"); + get_rawinput_device_info = (get_rawinput_device_info_ptr)GetProcAddress(user32, "GetRawInputDeviceInfo" UNICODE_SUFFIX); + get_rawinput_data = (get_rawinput_data_ptr)GetProcAddress(user32, "GetRawInputData"); + if (register_rawinput_devices == NULL || get_rawinput_device_list == NULL || get_rawinput_device_info == NULL || get_rawinput_data == NULL) + return 1; + + osd_printf_verbose("RawInput: APIs detected\n"); + + return 0; + } + + template<class TDevice> + TDevice* create_rawinput_device(running_machine &machine, PRAWINPUTDEVICELIST rawinputdevice) + { + TDevice* devinfo = nullptr; + INT name_length = 0; + // determine the length of the device name, allocate it, and fetch it if not nameless + if ((*get_rawinput_device_info)(rawinputdevice->hDevice, RIDI_DEVICENAME, NULL, &name_length) != 0) + return nullptr; + + std::unique_ptr<TCHAR[]> tname = std::make_unique<TCHAR[]>(name_length + 1); + if (name_length > 1 && (*get_rawinput_device_info)(rawinputdevice->hDevice, RIDI_DEVICENAME, tname.get(), &name_length) == -1) + return nullptr; + + // if this is an RDP name, skip it + if (_tcsstr(tname.get(), TEXT("Root#RDP_")) != NULL) + return nullptr; + + // improve the name and then allocate a device + tname = std::unique_ptr<TCHAR[]>(rawinput_device_improve_name(tname.release())); + + // convert name to utf8 + auto osd_deleter = [](void *ptr) { osd_free(ptr); }; + auto utf8_name = std::unique_ptr<char, decltype(osd_deleter)>(utf8_from_tstring(tname.get()), osd_deleter); + + devinfo = devicelist()->create_device<TDevice>(machine, utf8_name.get(), *this); + + // Add the handle + devinfo->set_handle(rawinputdevice->hDevice); + + return devinfo; + } + + bool handle_input_event(input_event eventid, void* eventdata) override + { + // Only handle raw input data + if (!input_enabled() || eventid != INPUT_EVENT_RAWINPUT) + return FALSE; + + HRAWINPUT rawinputdevice = *(HRAWINPUT*)eventdata; + + BYTE small_buffer[4096]; + std::unique_ptr<BYTE[]> larger_buffer; + LPBYTE data = small_buffer; + BOOL result = FALSE; + int size; + + // ignore if not enabled + if (!input_enabled()) + return FALSE; + + // determine the size of databuffer we need + if ((*get_rawinput_data)(rawinputdevice, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)) != 0) + return FALSE; + + // if necessary, allocate a temporary buffer and fetch the data + if (size > sizeof(small_buffer)) + { + larger_buffer = std::make_unique<BYTE[]>(size); + data = larger_buffer.get(); + if (data == NULL) + return FALSE; + } + + // fetch the data and process the appropriate message types + result = (*get_rawinput_data)((HRAWINPUT)rawinputdevice, RID_INPUT, data, &size, sizeof(RAWINPUTHEADER)); + if (result) + { + std::lock_guard<std::mutex> scope_lock(m_module_lock); + + rawinput_device *devinfo; + // find the device in the list and update + for (int i = 0; i < devicelist()->size(); i++) + { + devinfo = dynamic_cast<rawinput_device*>(devicelist()->at(i)); + RAWINPUT *input = reinterpret_cast<RAWINPUT*>(data); + if (input->header.hDevice == devinfo->device_handle()) + { + devinfo->queue_events(input, 1); + result = TRUE; + } + } + } + + return result; + } +}; + +//============================================================ +// keyboard_input_rawinput - rawinput keyboard module +//============================================================ + +class keyboard_input_rawinput : public rawinput_module +{ +public: + keyboard_input_rawinput() + : rawinput_module(OSD_KEYBOARDINPUT_PROVIDER, "rawinput") + { + } + +protected: + USHORT usagepage() { return 1; } + USHORT usage() { return 6; } + void add_rawinput_device(running_machine& machine, RAWINPUTDEVICELIST * device) override + { + // make sure this is a keyboard + if (device->dwType != RIM_TYPEKEYBOARD) + return; + + // allocate and link in a new device + rawinput_keyboard_device *devinfo = create_rawinput_device<rawinput_keyboard_device>(machine, device); + if (devinfo == NULL) + return; + + keyboard_trans_table &table = keyboard_trans_table::instance(); + + // populate it + for (int keynum = 0; keynum < MAX_KEYS; keynum++) + { + input_item_id itemid = table.map_di_scancode_to_itemid(keynum); + TCHAR keyname[100]; + char *name; + + // generate the name + if (GetKeyNameText(((keynum & 0x7f) << 16) | ((keynum & 0x80) << 17), keyname, ARRAY_LENGTH(keyname)) == 0) + _sntprintf(keyname, ARRAY_LENGTH(keyname), TEXT("Scan%03d"), keynum); + name = utf8_from_tstring(keyname); + + // add the item to the device + devinfo->device()->add_item(name, itemid, generic_button_get_state, &devinfo->keyboard.state[keynum]); + osd_free(name); + } + } +}; + +//============================================================ +// mouse_input_rawinput - rawinput mouse module +//============================================================ + +class mouse_input_rawinput : public rawinput_module +{ +public: + mouse_input_rawinput() + : rawinput_module(OSD_MOUSEINPUT_PROVIDER, "rawinput") + { + } + +protected: + USHORT usagepage() { return 1; } + USHORT usage() { return 2; } + void add_rawinput_device(running_machine& machine, RAWINPUTDEVICELIST * device) override + { + // make sure this is a mouse + if (device->dwType != RIM_TYPEMOUSE) + return; + + // allocate and link in a new device + rawinput_mouse_device *devinfo = create_rawinput_device<rawinput_mouse_device>(machine, device); + if (devinfo == NULL) + return; + + // populate the axes + for (int axisnum = 0; axisnum < 3; axisnum++) + { + devinfo->device()->add_item(default_axis_name[axisnum], (input_item_id)(ITEM_ID_XAXIS + axisnum), generic_axis_get_state, &devinfo->mouse.lX + axisnum); + } + + // populate the buttons + for (int butnum = 0; butnum < 5; butnum++) + { + devinfo->device()->add_item(default_button_name(butnum), (input_item_id)(ITEM_ID_BUTTON1 + butnum), generic_button_get_state, &devinfo->mouse.rgbButtons[butnum]); + } + } +}; + +#else +MODULE_NOT_SUPPORTED(keyboard_input_rawinput, OSD_KEYBOARDINPUT_PROVIDER, "rawinput") +MODULE_NOT_SUPPORTED(mouse_input_rawinput, OSD_MOUSEINPUT_PROVIDER, "rawinput") +//MODULE_NOT_SUPPORTED(lightgun_input_rawinput, OSD_LIGHTGUNINPUT_PROVIDER, "rawinput") +#endif + +MODULE_DEFINITION(KEYBOARDINPUT_RAWINPUT, keyboard_input_rawinput) +MODULE_DEFINITION(MOUSEINPUT_RAWINPUT, mouse_input_rawinput) +//MODULE_DEFINITION(LIGHTGUNINPUT_RAWINPUT, lightgun_input_rawinput)
\ No newline at end of file |