diff --git a/emulator/emulator.hpp b/emulator/emulator.hpp index da1fd25e..a6144243 100644 --- a/emulator/emulator.hpp +++ b/emulator/emulator.hpp @@ -3,7 +3,7 @@ namespace Emulator { static const char Name[] = "higan"; - static const char Version[] = "093.11"; + static const char Version[] = "093.12"; static const char Author[] = "byuu"; static const char License[] = "GPLv3"; static const char Website[] = "http://byuu.org/"; diff --git a/emulator/interface.hpp b/emulator/interface.hpp index cb2be617..26ad8906 100644 --- a/emulator/interface.hpp +++ b/emulator/interface.hpp @@ -31,7 +31,7 @@ struct Interface { string name; struct Input { unsigned id; - unsigned type; //0 = digital, 1 = analog (relative), 2 = analog (absolute), 3 = rumble + unsigned type; //0 = digital, 1 = analog (relative), 2 = rumble string name; unsigned guid; }; diff --git a/gba/interface/interface.cpp b/gba/interface/interface.cpp index 54b2b0a3..e5618a5c 100644 --- a/gba/interface/interface.cpp +++ b/gba/interface/interface.cpp @@ -139,7 +139,7 @@ Interface::Interface() { device.input.append({ 7, 0, "Down" }); device.input.append({ 8, 0, "R" }); device.input.append({ 9, 0, "L" }); - device.input.append({10, 3, "Rumble"}); + device.input.append({10, 2, "Rumble"}); device.order = {6, 7, 5, 4, 1, 0, 9, 8, 2, 3, 10}; this->device.append(device); } diff --git a/nall/hid.hpp b/nall/hid.hpp new file mode 100644 index 00000000..d8d865c2 --- /dev/null +++ b/nall/hid.hpp @@ -0,0 +1,117 @@ +#ifndef NALL_HID_HPP +#define NALL_HID_HPP + +namespace nall { + +namespace HID { + struct Input { + string name; + int16_t value = 0; + + Input() {} + Input(const string& name) : name(name) {} + }; + + struct Group { + string name; + vector input; + + Group() {} + Group(const string& name) : name(name) {} + + void append(const string& name) { + input.append({name}); + } + + optional find(const string& name) { + for(unsigned id = 0; id < input.size(); id++) { + if(input[id].name == name) return {true, id}; + } + return false; + } + }; + + struct Device { + uint64_t id = 0; + string name; + vector group; + + Device() { + group.resize(4); + } + + uint32_t pathID() const { return (uint32_t)(id >> 32); } + uint32_t deviceID() const { return (uint32_t)(id >> 0); } + uint16_t vendorID() const { return (uint16_t)(id >> 16); } + uint16_t productID() const { return (uint16_t)(id >> 0); } + + virtual bool isNull() const { return false; } + virtual bool isKeyboard() const { return false; } + virtual bool isMouse() const { return false; } + virtual bool isJoypad() const { return false; } + + optional find(const string& name) { + for(unsigned id = 0; id < group.size(); id++) { + if(group[id].name == name) return {true, id}; + } + return false; + } + }; + + struct Null : Device { + Null() { + name = "Null"; + } + + bool isNull() const { return true; } + }; + + struct Keyboard : Device { + enum GroupID : unsigned { Button }; + Group& button = group[0]; + + Keyboard() { + name = "Keyboard"; + button.name = "Button"; + } + + bool isKeyboard() const { return true; } + }; + + struct Mouse : Device { + enum GroupID : unsigned { Axis, Button }; + Group& axis = group[0]; + Group& button = group[1]; + + Mouse() { + name = "Mouse"; + axis.name = "Axis"; + button.name = "Button"; + } + + bool isMouse() const { return true; } + }; + + struct Joypad : Device { + enum GroupID : unsigned { Axis, Hat, Trigger, Button }; + Group& axis = group[0]; + Group& hat = group[1]; + Group& trigger = group[2]; + Group& button = group[3]; + bool rumble = false; + + Joypad() { + name = "Joypad"; + axis.name = "Axis"; + hat.name = "Hat"; + trigger.name = "Trigger"; + button.name = "Button"; + } + + bool isJoypad() const { return true; } + }; +} + +} + +#endif diff --git a/nall/nall.hpp b/nall/nall.hpp index 3895c916..f021e134 100644 --- a/nall/nall.hpp +++ b/nall/nall.hpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/nall/platform.hpp b/nall/platform.hpp index 7273076c..469ec5bc 100644 --- a/nall/platform.hpp +++ b/nall/platform.hpp @@ -21,6 +21,7 @@ namespace Math { #include #include +#include #include #include #include diff --git a/ruby/implementation.cpp b/ruby/implementation.cpp index 7b187fc1..991eeb3e 100644 --- a/ruby/implementation.cpp +++ b/ruby/implementation.cpp @@ -19,8 +19,7 @@ using namespace nall; /* Video */ #define DeclareVideo(Name) \ - class Video##Name : public Video { \ - public: \ + struct Video##Name : Video { \ bool cap(const string& name) { return p.cap(name); } \ any get(const string& name) { return p.get(name); } \ bool set(const string& name, const any& value) { return p.set(name, value); } \ @@ -79,8 +78,7 @@ using namespace nall; /* Audio */ #define DeclareAudio(Name) \ - class Audio##Name : public Audio { \ - public: \ + struct Audio##Name : Audio { \ bool cap(const string& name) { return p.cap(name); } \ any get(const string& name) { return p.get(name); } \ bool set(const string& name, const any& value) { return p.set(name, value); } \ @@ -132,8 +130,7 @@ using namespace nall; /* Input */ #define DeclareInput(Name) \ - class Input##Name : public Input { \ - public: \ + struct Input##Name : Input { \ bool cap(const string& name) { return p.cap(name); } \ any get(const string& name) { return p.get(name); } \ bool set(const string& name, const any& value) { return p.set(name, value); } \ @@ -142,8 +139,9 @@ using namespace nall; bool unacquire() { return p.unacquire(); } \ bool acquired() { return p.acquired(); } \ \ + vector poll() { return p.poll(); } \ bool poll(int16_t* table) { return p.poll(table); } \ - void rumble(unsigned id, bool enable) { return p.rumble(id, enable); } \ + void rumble(uint64_t id, bool enable) { return p.rumble(id, enable); } \ bool init() { return p.init(); } \ void term() { p.term(); } \ \ @@ -174,6 +172,6 @@ using namespace nall; #include #endif -#ifdef INPUT_X - #include +#ifdef INPUT_XLIB + #include #endif diff --git a/ruby/input.hpp b/ruby/input.hpp index 8da70de2..b4d6d5ca 100644 --- a/ruby/input.hpp +++ b/ruby/input.hpp @@ -13,8 +13,9 @@ struct Input { virtual bool unacquire() { return false; } virtual bool acquired() { return false; } + virtual nall::vector poll() { return {}; } virtual bool poll(int16_t* table) { return false; } - virtual void rumble(unsigned id, bool enable) {} + virtual void rumble(uint64_t id, bool enable) {} virtual bool init() { return true; } virtual void term() {} diff --git a/ruby/input/joypad/sdl.cpp b/ruby/input/joypad/sdl.cpp new file mode 100644 index 00000000..f28f6806 --- /dev/null +++ b/ruby/input/joypad/sdl.cpp @@ -0,0 +1,66 @@ +#ifndef RUBY_INPUT_JOYPAD_SDL +#define RUBY_INPUT_JOYPAD_SDL + +namespace ruby { + +struct InputJoypadSDL { + struct Joystick { + unsigned id = 0; + SDL_Joystick* handle = nullptr; + }; + vector joysticks; + + bool poll(int16_t* table) { + SDL_JoystickUpdate(); + for(auto& js : joysticks) { + unsigned axes = min((unsigned)Joypad::Axes, SDL_JoystickNumAxes(js.handle)); + for(unsigned axis = 0; axis < axes; axis++) { + table[joypad(js.id).axis(axis)] = (int16_t)SDL_JoystickGetAxis(js.handle, axis); + } + + unsigned hats = min((unsigned)Joypad::Hats, SDL_JoystickNumHats(js.handle)); + for(unsigned hat = 0; hat < hats; hat++) { + uint8_t state = SDL_JoystickGetHat(js.handle, hat); + int16_t value = 0; + if(state & SDL_HAT_UP ) value |= Joypad::HatUp; + if(state & SDL_HAT_DOWN ) value |= Joypad::HatDown; + if(state & SDL_HAT_LEFT ) value |= Joypad::HatLeft; + if(state & SDL_HAT_RIGHT) value |= Joypad::HatRight; + table[joypad(js.id).hat(hat)] = value; + } + + //there is no SDL_JoystickNumButtons function + for(unsigned button = 0; button < Joypad::Buttons; button++) { + table[joypad(js.id).button(button)] = (bool)SDL_JoystickGetButton(js.handle, button); + } + } + + return true; + } + + bool init() { + SDL_InitSubSystem(SDL_INIT_JOYSTICK); + SDL_JoystickEventState(SDL_IGNORE); + + unsigned joystickCount = SDL_NumJoysticks(); + for(unsigned id = 0; id < joystickCount; id++) { + Joystick joystick; + joystick.id = id; + joystick.handle = SDL_JoystickOpen(id); + } + + return true; + } + + void term() { + for(auto& js : joysticks) { + SDL_JoystickClose(js.handle); + } + joysticks.reset(); + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); + } +}; + +} + +#endif diff --git a/ruby/input/joypad/udev.cpp b/ruby/input/joypad/udev.cpp index 51126fb1..fc1b23b9 100644 --- a/ruby/input/joypad/udev.cpp +++ b/ruby/input/joypad/udev.cpp @@ -1,3 +1,6 @@ +#ifndef RUBY_INPUT_JOYPAD_UDEV +#define RUBY_INPUT_JOYPAD_UDEV + namespace ruby { struct InputJoypadUdev { @@ -21,9 +24,13 @@ struct InputJoypadUdev { }; struct Joystick { - string path; - dev_t device = 0; + HID::Joypad hid; + int fd = -1; + dev_t device = 0; + string deviceName; + string devicePath; + uint8_t evbit[(EV_MAX + 7) / 8] = {0}; uint8_t keybit[(KEY_MAX + 7) / 8] = {0}; uint8_t absbit[(ABS_MAX + 7) / 8] = {0}; @@ -45,6 +52,48 @@ struct InputJoypadUdev { }; vector joysticks; + void assign(HID::Joypad& hid, unsigned groupID, unsigned inputID, int16_t value) { + auto& group = hid.group[groupID]; + if(group.input[inputID].value == value) return; + if(input.onChange) input.onChange(hid, groupID, inputID, group.input[inputID].value, value); + group.input[inputID].value = value; + } + + void poll(vector& devices) { + for(auto& js : joysticks) { + input_event events[32]; + signed length = 0; + while((length = read(js.fd, events, sizeof(events))) > 0) { + length /= sizeof(input_event); + for(unsigned i = 0; i < length; i++) { + signed code = events[i].code; + signed type = events[i].type; + signed value = events[i].value; + + if(type == EV_ABS) { + if(auto input = js.axes.find({code})) { + signed range = input().info.maximum - input().info.minimum; + value = (value - input().info.minimum) * 65535ll / range - 32767; + assign(js.hid, HID::Joypad::GroupID::Axis, input().id, sclamp<16>(value)); + } else if(auto input = js.hats.find({code})) { + signed range = input().info.maximum - input().info.minimum; + value = (value - input().info.minimum) * 65535ll / range - 32767; + assign(js.hid, HID::Joypad::GroupID::Hat, input().id, sclamp<16>(value)); + } + } else if(type == EV_KEY) { + if(code >= BTN_MISC) { + if(auto input = js.buttons.find({code})) { + assign(js.hid, HID::Joypad::GroupID::Button, input().id, (bool)value); + } + } + } + } + } + + devices.append(&js.hid); + } + } + bool poll(int16_t* table) { unsigned i = 0; for(auto& js : joysticks) { @@ -121,18 +170,18 @@ struct InputJoypadUdev { return true; } - void rumble(unsigned id, bool enable) { - if(id >= joysticks.size()) return; + void rumble(uint64_t id, bool enable) { + for(auto& js : joysticks) { + if(js.hid.id != id) continue; + if(js.hid.rumble == false) continue; - Joystick& js = joysticks[id]; - if(js.rumble == false) return; - - input_event play; - memset(&play, 0, sizeof(input_event)); - play.type = EV_FF; - play.code = js.effectID; - play.value = enable; - write(js.fd, &play, sizeof(input_event)); + input_event play; + memset(&play, 0, sizeof(input_event)); + play.type = EV_FF; + play.code = js.effectID; + play.value = enable; + write(js.fd, &play, sizeof(input_event)); + } } bool init() { @@ -167,15 +216,15 @@ struct InputJoypadUdev { } private: - void createJoystick(udev_device* device, const char* path) { + void createJoystick(udev_device* device, const char* devicePath) { Joystick js; - js.path = path; + js.devicePath = devicePath; struct stat st; - if(stat(path, &st) < 0) return; + if(stat(devicePath, &st) < 0) return; js.device = st.st_rdev; - js.fd = open(path, O_RDWR | O_NONBLOCK); + js.fd = open(devicePath, O_RDWR | O_NONBLOCK); if(js.fd < 0) return; uint8_t evbit[(EV_MAX + 7) / 8] = {0}; @@ -199,6 +248,7 @@ private: if(js.vendorID == udev_device_get_sysattr_value(root, "idVendor") && js.productID == udev_device_get_sysattr_value(root, "idProduct") ) { + js.deviceName = udev_device_get_devpath(root); js.manufacturer = udev_device_get_sysattr_value(root, "manufacturer"); js.product = udev_device_get_sysattr_value(root, "product"); js.serial = udev_device_get_sysattr_value(root, "serial"); @@ -244,11 +294,24 @@ private: js.effectID = effect.id; } + createJoystickHID(js); joysticks.append(js); } #undef testBit } + + void createJoystickHID(Joystick& js) { + uint64_t pathID = crc32_calculate((const uint8_t*)js.deviceName.data(), js.deviceName.size()); + js.hid.id = pathID << 32 | hex(js.vendorID) << 16 | hex(js.productID) << 0; + + for(unsigned n = 0; n < js.axes.size(); n++) js.hid.axis.append({n}); + for(unsigned n = 0; n < js.hats.size(); n++) js.hid.hat.append({n}); + for(unsigned n = 0; n < js.buttons.size(); n++) js.hid.button.append({n}); + js.hid.rumble = js.rumble; + } }; } + +#endif diff --git a/ruby/input/joypad/xinput.cpp b/ruby/input/joypad/xinput.cpp index e9dbbe37..dc4755a9 100644 --- a/ruby/input/joypad/xinput.cpp +++ b/ruby/input/joypad/xinput.cpp @@ -1,3 +1,6 @@ +#ifndef RUBY_INPUT_JOYPAD_XINPUT +#define RUBY_INPUT_JOYPAD_XINPUT + #include namespace ruby { @@ -99,3 +102,5 @@ struct InputJoypadXInput { }; } + +#endif diff --git a/ruby/input/keyboard/xlib.cpp b/ruby/input/keyboard/xlib.cpp index b5f3ef5d..c57335e1 100644 --- a/ruby/input/keyboard/xlib.cpp +++ b/ruby/input/keyboard/xlib.cpp @@ -1,9 +1,21 @@ +#ifndef RUBY_INPUT_KEYBOARD_XLIB +#define RUBY_INPUT_KEYBOARD_XLIB + namespace ruby { struct InputKeyboardXlib { + HID::Keyboard hid; + Display* display = nullptr; uint8_t scancode[256] = {0}; + struct Key { + string name; + unsigned keysym; + unsigned keycode; + }; + vector keys; + enum XScancode : unsigned { Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, ScrollLock, Pause, Tilde, @@ -18,8 +30,28 @@ struct InputKeyboardXlib { Up, Down, Left, Right, Tab, Return, Spacebar, Menu, LeftShift, RightShift, LeftControl, RightControl, LeftAlt, RightAlt, LeftSuper, RightSuper, + Limit, }; + void assign(unsigned inputID, bool value) { + auto& group = hid.group[HID::Keyboard::GroupID::Button]; + if(group.input[inputID].value == value) return; + if(input.onChange) input.onChange(hid, HID::Keyboard::GroupID::Button, inputID, group.input[inputID].value, value); + group.input[inputID].value = value; + } + + void poll(vector& devices) { + char state[32]; + XQueryKeymap(display, state); + + for(unsigned n = 0; n < keys.size(); n++) { + bool value = state[keys[n].keycode >> 3] & (1 << (keys[n].keycode & 7)); + assign(n, value); + } + + devices.append(&hid); + } + bool poll(int16_t* table) { char state[32]; XQueryKeymap(display, state); @@ -146,6 +178,127 @@ struct InputKeyboardXlib { bool init() { display = XOpenDisplay(0); + keys.append({"Escape", XK_Escape}); + + keys.append({"F1", XK_F1}); + keys.append({"F2", XK_F2}); + keys.append({"F3", XK_F3}); + keys.append({"F4", XK_F4}); + keys.append({"F5", XK_F5}); + keys.append({"F6", XK_F6}); + keys.append({"F7", XK_F7}); + keys.append({"F8", XK_F8}); + keys.append({"F9", XK_F9}); + keys.append({"F10", XK_F10}); + keys.append({"F11", XK_F11}); + keys.append({"F12", XK_F12}); + + keys.append({"ScrollLock", XK_Scroll_Lock}); + keys.append({"Pause", XK_Pause}); + + keys.append({"Tilde", XK_asciitilde}); + + keys.append({"Num0", XK_0}); + keys.append({"Num1", XK_1}); + keys.append({"Num2", XK_2}); + keys.append({"Num3", XK_3}); + keys.append({"Num4", XK_4}); + keys.append({"Num5", XK_5}); + keys.append({"Num6", XK_6}); + keys.append({"Num7", XK_7}); + keys.append({"Num8", XK_8}); + keys.append({"Num9", XK_9}); + + keys.append({"Dash", XK_minus}); + keys.append({"Equal", XK_equal}); + keys.append({"Backspace", XK_BackSpace}); + + keys.append({"Insert", XK_Insert}); + keys.append({"Delete", XK_Delete}); + keys.append({"Home", XK_Home}); + keys.append({"End", XK_End}); + keys.append({"PageUp", XK_Prior}); + keys.append({"PageDown", XK_Next}); + + keys.append({"A", XK_A}); + keys.append({"B", XK_B}); + keys.append({"C", XK_C}); + keys.append({"D", XK_D}); + keys.append({"E", XK_E}); + keys.append({"F", XK_F}); + keys.append({"G", XK_G}); + keys.append({"H", XK_H}); + keys.append({"I", XK_I}); + keys.append({"J", XK_J}); + keys.append({"K", XK_K}); + keys.append({"L", XK_L}); + keys.append({"M", XK_M}); + keys.append({"N", XK_N}); + keys.append({"O", XK_O}); + keys.append({"P", XK_P}); + keys.append({"Q", XK_Q}); + keys.append({"R", XK_R}); + keys.append({"S", XK_S}); + keys.append({"T", XK_T}); + keys.append({"U", XK_U}); + keys.append({"V", XK_V}); + keys.append({"W", XK_W}); + keys.append({"X", XK_X}); + keys.append({"Y", XK_Y}); + keys.append({"Z", XK_Z}); + + keys.append({"LeftBracket", XK_bracketleft}); + keys.append({"RightBracket", XK_bracketright}); + keys.append({"Backslash", XK_backslash}); + keys.append({"Semicolon", XK_semicolon}); + keys.append({"Apostrophe", XK_apostrophe}); + keys.append({"Comma", XK_comma}); + keys.append({"Period", XK_period}); + keys.append({"Slash", XK_slash}); + + keys.append({"Keypad0", XK_KP_0}); + keys.append({"Keypad1", XK_KP_1}); + keys.append({"Keypad2", XK_KP_2}); + keys.append({"Keypad3", XK_KP_3}); + keys.append({"Keypad4", XK_KP_4}); + keys.append({"Keypad5", XK_KP_5}); + keys.append({"Keypad6", XK_KP_6}); + keys.append({"Keypad7", XK_KP_7}); + keys.append({"Keypad8", XK_KP_8}); + keys.append({"Keypad9", XK_KP_9}); + + keys.append({"Add", XK_KP_Add}); + keys.append({"Subtract", XK_KP_Subtract}); + keys.append({"Multiply", XK_KP_Multiply}); + keys.append({"Divide", XK_KP_Divide}); + keys.append({"Enter", XK_KP_Enter}); + + keys.append({"Up", XK_Up}); + keys.append({"Down", XK_Down}); + keys.append({"Left", XK_Left}); + keys.append({"Right", XK_Right}); + + keys.append({"Tab", XK_Tab}); + keys.append({"Return", XK_Return}); + keys.append({"Spacebar", XK_space}); + + keys.append({"LeftControl", XK_Control_L}); + keys.append({"RightControl", XK_Control_R}); + keys.append({"LeftAlt", XK_Alt_L}); + keys.append({"RightAlt", XK_Alt_R}); + keys.append({"LeftShift", XK_Shift_L}); + keys.append({"RightShift", XK_Shift_R}); + keys.append({"LeftSuper", XK_Super_L}); + keys.append({"RightSuper", XK_Super_R}); + keys.append({"Menu", XK_Menu}); + + hid.id = 1; + + for(unsigned n = 0; n < keys.size(); n++) { + hid.button.append(keys[n].name); + keys[n].keycode = XKeysymToKeycode(display, keys[n].keysym); + } + #define assign(x, y) scancode[x] = XKeysymToKeycode(display, y) assign(Escape, XK_Escape); @@ -275,3 +428,5 @@ struct InputKeyboardXlib { }; } + +#endif diff --git a/ruby/input/mouse/xlib.cpp b/ruby/input/mouse/xlib.cpp index 52f77970..6748c876 100644 --- a/ruby/input/mouse/xlib.cpp +++ b/ruby/input/mouse/xlib.cpp @@ -1,6 +1,11 @@ +#ifndef RUBY_INPUT_MOUSE_XLIB +#define RUBY_INPUT_MOUSE_XLIB + namespace ruby { struct InputMouseXlib { + HID::Mouse hid; + uintptr_t handle = 0; Display* display = nullptr; @@ -51,6 +56,52 @@ struct InputMouseXlib { return ms.acquired; } + void assign(unsigned groupID, unsigned inputID, int16_t value) { + auto& group = hid.group[groupID]; + if(group.input[inputID].value == value) return; + if(input.onChange) input.onChange(hid, groupID, inputID, group.input[inputID].value, value); + group.input[inputID].value = value; + } + + void poll(vector& devices) { + Window rootReturn; + Window childReturn; + signed rootXReturn = 0; + signed rootYReturn = 0; + signed windowXReturn = 0; + signed windowYReturn = 0; + unsigned maskReturn = 0; + XQueryPointer(display, handle, &rootReturn, &childReturn, &rootXReturn, &rootYReturn, &windowXReturn, &windowYReturn, &maskReturn); + + if(acquired()) { + XWindowAttributes attributes; + XGetWindowAttributes(display, handle, &attributes); + + //absolute -> relative conversion + assign(HID::Mouse::GroupID::Axis, 0, (int16_t)(rootXReturn - screenWidth / 2)); + assign(HID::Mouse::GroupID::Axis, 1, (int16_t)(rootYReturn - screenHeight / 2)); + + if(hid.axis.input[0].value != 0 || hid.axis.input[1].value != 0) { + //if mouse moved, re-center mouse for next poll + XWarpPointer(display, None, rootWindow, 0, 0, 0, 0, screenWidth / 2, screenHeight / 2); + } + } else { + assign(HID::Mouse::GroupID::Axis, 0, (int16_t)(rootXReturn - ms.relativeX)); + assign(HID::Mouse::GroupID::Axis, 1, (int16_t)(rootYReturn - ms.relativeY)); + + ms.relativeX = rootXReturn; + ms.relativeY = rootYReturn; + } + + assign(HID::Mouse::GroupID::Button, 0, (bool)(maskReturn & Button1Mask)); + assign(HID::Mouse::GroupID::Button, 1, (bool)(maskReturn & Button2Mask)); + assign(HID::Mouse::GroupID::Button, 2, (bool)(maskReturn & Button3Mask)); + assign(HID::Mouse::GroupID::Button, 3, (bool)(maskReturn & Button4Mask)); + assign(HID::Mouse::GroupID::Button, 4, (bool)(maskReturn & Button5Mask)); + + devices.append(&hid); + } + bool poll(int16_t* table) { Window rootReturn; Window childReturn; @@ -115,6 +166,17 @@ struct InputMouseXlib { ms.relativeX = 0; ms.relativeY = 0; + hid.id = 2; + + hid.axis.append({"X"}); + hid.axis.append({"Y"}); + + hid.button.append({"Left"}); + hid.button.append({"Middle"}); + hid.button.append({"Right"}); + hid.button.append({"Up"}); + hid.button.append({"Down"}); + return true; } @@ -126,3 +188,5 @@ struct InputMouseXlib { }; } + +#endif diff --git a/ruby/input/sdl.cpp b/ruby/input/sdl.cpp index 05fbaa8e..2f52b85a 100644 --- a/ruby/input/sdl.cpp +++ b/ruby/input/sdl.cpp @@ -1,37 +1,20 @@ -//================ -//SDL input driver -//================ -//Keyboard and mouse are controlled directly via Xlib, -//as SDL cannot capture input from windows it does not create itself. -//SDL is used only to handle joysticks / gamepads. - #include #include #include +#include "keyboard/xlib.cpp" +#include "mouse/xlib.cpp" +#include "joypad/sdl.cpp" + namespace ruby { struct pInputSDL { - #include "xlibkeys.hpp" + InputKeyboardXlib xlibKeyboard; + InputMouseXlib xlibMouse; + InputJoypadSDL sdl; - struct { - Display* display; - Window rootwindow; - Cursor InvisibleCursor; - SDL_Joystick* gamepad[Joypad::Count]; - - unsigned screenwidth, screenheight; - unsigned relativex, relativey; - bool mouseacquired; - - //mouse device settings - int accel_numerator; - int accel_denominator; - int threshold; - } device; - - struct { - uintptr_t handle; + struct Settings { + uintptr_t handle = 0; } settings; bool cap(const string& name) { @@ -57,117 +40,21 @@ struct pInputSDL { } bool acquire() { - if(acquired()) return true; - - if(XGrabPointer(device.display, settings.handle, True, 0, GrabModeAsync, GrabModeAsync, - device.rootwindow, device.InvisibleCursor, CurrentTime) == GrabSuccess) { - //backup existing cursor acceleration settings - XGetPointerControl(device.display, &device.accel_numerator, &device.accel_denominator, &device.threshold); - - //disable cursor acceleration - XChangePointerControl(device.display, True, False, 1, 1, 0); - - //center cursor (so that first relative poll returns 0, 0 if mouse has not moved) - XWarpPointer(device.display, None, device.rootwindow, 0, 0, 0, 0, device.screenwidth / 2, device.screenheight / 2); - - return device.mouseacquired = true; - } else { - return device.mouseacquired = false; - } + return xlibMouse.acquire(); } bool unacquire() { - if(acquired()) { - //restore cursor acceleration and release cursor - XChangePointerControl(device.display, True, True, device.accel_numerator, device.accel_denominator, device.threshold); - XUngrabPointer(device.display, CurrentTime); - device.mouseacquired = false; - } - return true; + return xlibMouse.unacquire(); } bool acquired() { - return device.mouseacquired; + return xlibMouse.acquired(); } bool poll(int16_t* table) { - memset(table, 0, Scancode::Limit * sizeof(int16_t)); - - //======== - //Keyboard - //======== - - x_poll(device.display, table); - - //===== - //Mouse - //===== - - Window root_return, child_return; - int root_x_return = 0, root_y_return = 0; - int win_x_return = 0, win_y_return = 0; - unsigned int mask_return = 0; - XQueryPointer(device.display, settings.handle, - &root_return, &child_return, &root_x_return, &root_y_return, - &win_x_return, &win_y_return, &mask_return); - - if(acquired()) { - XWindowAttributes attributes; - XGetWindowAttributes(device.display, settings.handle, &attributes); - - //absolute -> relative conversion - table[mouse(0).axis(0)] = (int16_t)(root_x_return - device.screenwidth / 2); - table[mouse(0).axis(1)] = (int16_t)(root_y_return - device.screenheight / 2); - - if(table[mouse(0).axis(0)] != 0 || table[mouse(0).axis(1)] != 0) { - //if mouse movement occurred, re-center mouse for next poll - XWarpPointer(device.display, None, device.rootwindow, 0, 0, 0, 0, device.screenwidth / 2, device.screenheight / 2); - } - } else { - table[mouse(0).axis(0)] = (int16_t)(root_x_return - device.relativex); - table[mouse(0).axis(1)] = (int16_t)(root_y_return - device.relativey); - - device.relativex = root_x_return; - device.relativey = root_y_return; - } - - //manual device polling is limited to only five buttons ... - table[mouse(0).button(0)] = (bool)(mask_return & Button1Mask); - table[mouse(0).button(1)] = (bool)(mask_return & Button2Mask); - table[mouse(0).button(2)] = (bool)(mask_return & Button3Mask); - table[mouse(0).button(3)] = (bool)(mask_return & Button4Mask); - table[mouse(0).button(4)] = (bool)(mask_return & Button5Mask); - - //========= - //Joypad(s) - //========= - - SDL_JoystickUpdate(); - for(unsigned i = 0; i < Joypad::Count; i++) { - if(!device.gamepad[i]) continue; - - //POV hats - unsigned hats = min((unsigned)Joypad::Hats, SDL_JoystickNumHats(device.gamepad[i])); - for(unsigned hat = 0; hat < hats; hat++) { - uint8_t state = SDL_JoystickGetHat(device.gamepad[i], hat); - if(state & SDL_HAT_UP ) table[joypad(i).hat(hat)] |= Joypad::HatUp; - if(state & SDL_HAT_RIGHT) table[joypad(i).hat(hat)] |= Joypad::HatRight; - if(state & SDL_HAT_DOWN ) table[joypad(i).hat(hat)] |= Joypad::HatDown; - if(state & SDL_HAT_LEFT ) table[joypad(i).hat(hat)] |= Joypad::HatLeft; - } - - //axes - unsigned axes = min((unsigned)Joypad::Axes, SDL_JoystickNumAxes(device.gamepad[i])); - for(unsigned axis = 0; axis < axes; axis++) { - table[joypad(i).axis(axis)] = (int16_t)SDL_JoystickGetAxis(device.gamepad[i], axis); - } - - //buttons - for(unsigned button = 0; button < Joypad::Buttons; button++) { - table[joypad(i).button(button)] = (bool)SDL_JoystickGetButton(device.gamepad[i], button); - } - } - + xlibKeyboard.poll(table); + xlibMouse.poll(table); + sdl.poll(table); return true; } @@ -175,56 +62,16 @@ struct pInputSDL { } bool init() { - SDL_InitSubSystem(SDL_INIT_JOYSTICK); - SDL_JoystickEventState(SDL_IGNORE); - - device.display = XOpenDisplay(0); - device.rootwindow = DefaultRootWindow(device.display); - XWindowAttributes attributes; - XGetWindowAttributes(device.display, device.rootwindow, &attributes); - device.screenwidth = attributes.width; - device.screenheight = attributes.height; - x_init(device.display); - - //Xlib: "because XShowCursor(false) would be too easy." - //create a fully transparent cursor named InvisibleCursor, - //for use while acquire() / XGrabPointer() is active. - Pixmap pixmap; - XColor black, unused; - static char invisible_data[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - Colormap colormap = DefaultColormap(device.display, DefaultScreen(device.display)); - XAllocNamedColor(device.display, colormap, "black", &black, &unused); - pixmap = XCreateBitmapFromData(device.display, settings.handle, invisible_data, 8, 8); - device.InvisibleCursor = XCreatePixmapCursor(device.display, pixmap, pixmap, &black, &black, 0, 0); - XFreePixmap(device.display, pixmap); - XFreeColors(device.display, colormap, &black.pixel, 1, 0); - - device.mouseacquired = false; - device.relativex = 0; - device.relativey = 0; - - unsigned joypads = min((unsigned)Joypad::Count, SDL_NumJoysticks()); - for(unsigned i = 0; i < joypads; i++) device.gamepad[i] = SDL_JoystickOpen(i); - + if(xlibKeyboard.init() == false) return false; + if(xlibMouse.init(settings.handle) == false) return false; + if(sdl.init() == false) return false; return true; } void term() { - unacquire(); - XFreeCursor(device.display, device.InvisibleCursor); - - for(unsigned i = 0; i < Joypad::Count; i++) { - if(device.gamepad[i]) SDL_JoystickClose(device.gamepad[i]); - device.gamepad[i] = 0; - } - - SDL_QuitSubSystem(SDL_INIT_JOYSTICK); - XCloseDisplay(device.display); - } - - pInputSDL() { - for(unsigned i = 0; i < Joypad::Count; i++) device.gamepad[i] = 0; - settings.handle = 0; + xlibKeyboard.term(); + xlibMouse.term(); + sdl.term(); } }; diff --git a/ruby/input/udev.cpp b/ruby/input/udev.cpp index c3fea635..53c10ce6 100644 --- a/ruby/input/udev.cpp +++ b/ruby/input/udev.cpp @@ -57,13 +57,22 @@ struct pInputUdev { return xlibMouse.acquired(); } + vector poll() { + vector devices; + xlibKeyboard.poll(devices); + xlibMouse.poll(devices); + udev.poll(devices); + return devices; + } + bool poll(int16_t* table) { xlibKeyboard.poll(table); xlibMouse.poll(table); udev.poll(table); + return true; } - void rumble(unsigned id, bool enable) { + void rumble(uint64_t id, bool enable) { udev.rumble(id, enable); } diff --git a/ruby/input/x.cpp b/ruby/input/x.cpp deleted file mode 100644 index be85e075..00000000 --- a/ruby/input/x.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include -#include -#include -#include - -namespace ruby { - -class pInputX { -public: - Display *display; - #include "xlibkeys.hpp" - - bool cap(const string& name) { - if(name == Input::KeyboardSupport) return true; - return false; - } - - any get(const string& name) { - return false; - } - - bool set(const string& name, const any &value) { - return false; - } - - bool acquire() { return false; } - bool unacquire() { return false; } - bool acquired() { return false; } - - bool poll(int16_t* table) { - memset(table, 0, Scancode::Limit * sizeof(int16_t)); - x_poll(display, table); - return true; - } - - void rumble(unsigned id, bool enable) { - } - - bool init() { - display = XOpenDisplay(0); - x_init(display); - return true; - } - - void term() { - XCloseDisplay(display); - } -}; - -DeclareInput(X) - -} diff --git a/ruby/input/xlib.cpp b/ruby/input/xlib.cpp new file mode 100644 index 00000000..e7179355 --- /dev/null +++ b/ruby/input/xlib.cpp @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include + +#include "keyboard/xlib.cpp" +#include "mouse/xlib.cpp" + +namespace ruby { + +struct pInputXlib { + InputKeyboardXlib xlibKeyboard; + InputMouseXlib xlibMouse; + + struct Settings { + uintptr_t handle = 0; + } settings; + + bool cap(const string& name) { + if(name == Input::KeyboardSupport) return true; + if(name == Input::MouseSupport) 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 = any_cast(value); + return true; + } + + return false; + } + + bool acquire() { + return xlibMouse.acquire(); + } + + bool unacquire() { + return xlibMouse.unacquire(); + } + + bool acquired() { + return xlibMouse.acquired(); + } + + bool poll(int16_t* table) { + xlibKeyboard.poll(table); + xlibMouse.poll(table); + return true; + } + + void rumble(unsigned id, bool enable) { + } + + bool init() { + if(xlibKeyboard.init() == false) return false; + if(xlibMouse.init(settings.handle) == false) return false; + return true; + } + + void term() { + xlibKeyboard.term(); + xlibMouse.term(); + } +}; + +DeclareInput(Xlib) + +} diff --git a/ruby/input/xlibkeys.hpp b/ruby/input/xlibkeys.hpp deleted file mode 100644 index c7eff5e3..00000000 --- a/ruby/input/xlibkeys.hpp +++ /dev/null @@ -1,263 +0,0 @@ -uint8_t scancode[256]; - -enum XScancode { - Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, - ScrollLock, Pause, Tilde, - Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Num0, - Dash, Equal, Backspace, - Insert, Delete, Home, End, PageUp, PageDown, - A, B, C, D, E, F, G, H, I, J, K, L, M, - N, O, P, Q, R, S, T, U, V, W, X, Y, Z, - LeftBracket, RightBracket, Backslash, Semicolon, Apostrophe, Comma, Period, Slash, - Keypad1, Keypad2, Keypad3, Keypad4, Keypad5, Keypad6, Keypad7, Keypad8, Keypad9, Keypad0, - Point, Enter, Add, Subtract, Multiply, Divide, - Up, Down, Left, Right, - Tab, Return, Spacebar, Menu, - LeftShift, RightShift, LeftControl, RightControl, LeftAlt, RightAlt, LeftSuper, RightSuper, -}; - -void x_poll(Display* display, int16_t* table) { - if(!display) return; - - char state[32]; - XQueryKeymap(display, state); - - #define key(id) table[keyboard(0)[id]] - #define pressed(id) (bool)(state[scancode[id] >> 3] & (1 << (scancode[id] & 7))) - - key(Keyboard::Escape) = pressed(Escape); - - key(Keyboard::F1) = pressed(F1); - key(Keyboard::F2) = pressed(F2); - key(Keyboard::F3) = pressed(F3); - key(Keyboard::F4) = pressed(F4); - key(Keyboard::F5) = pressed(F5); - key(Keyboard::F6) = pressed(F6); - key(Keyboard::F7) = pressed(F7); - key(Keyboard::F8) = pressed(F8); - key(Keyboard::F9) = pressed(F9); - key(Keyboard::F10) = pressed(F10); - key(Keyboard::F11) = pressed(F11); - key(Keyboard::F12) = pressed(F12); - - key(Keyboard::ScrollLock) = pressed(ScrollLock); - key(Keyboard::Pause) = pressed(Pause); - key(Keyboard::Tilde) = pressed(Tilde); - - key(Keyboard::Num1) = pressed(Num1); - key(Keyboard::Num2) = pressed(Num2); - key(Keyboard::Num3) = pressed(Num3); - key(Keyboard::Num4) = pressed(Num4); - key(Keyboard::Num5) = pressed(Num5); - key(Keyboard::Num6) = pressed(Num6); - key(Keyboard::Num7) = pressed(Num7); - key(Keyboard::Num8) = pressed(Num8); - key(Keyboard::Num9) = pressed(Num9); - key(Keyboard::Num0) = pressed(Num0); - - key(Keyboard::Dash) = pressed(Dash); - key(Keyboard::Equal) = pressed(Equal); - key(Keyboard::Backspace) = pressed(Backspace); - - key(Keyboard::Insert) = pressed(Insert); - key(Keyboard::Delete) = pressed(Delete); - key(Keyboard::Home) = pressed(Home); - key(Keyboard::End) = pressed(End); - key(Keyboard::PageUp) = pressed(PageUp); - key(Keyboard::PageDown) = pressed(PageDown); - - key(Keyboard::A) = pressed(A); - key(Keyboard::B) = pressed(B); - key(Keyboard::C) = pressed(C); - key(Keyboard::D) = pressed(D); - key(Keyboard::E) = pressed(E); - key(Keyboard::F) = pressed(F); - key(Keyboard::G) = pressed(G); - key(Keyboard::H) = pressed(H); - key(Keyboard::I) = pressed(I); - key(Keyboard::J) = pressed(J); - key(Keyboard::K) = pressed(K); - key(Keyboard::L) = pressed(L); - key(Keyboard::M) = pressed(M); - key(Keyboard::N) = pressed(N); - key(Keyboard::O) = pressed(O); - key(Keyboard::P) = pressed(P); - key(Keyboard::Q) = pressed(Q); - key(Keyboard::R) = pressed(R); - key(Keyboard::S) = pressed(S); - key(Keyboard::T) = pressed(T); - key(Keyboard::U) = pressed(U); - key(Keyboard::V) = pressed(V); - key(Keyboard::W) = pressed(W); - key(Keyboard::X) = pressed(X); - key(Keyboard::Y) = pressed(Y); - key(Keyboard::Z) = pressed(Z); - - key(Keyboard::LeftBracket) = pressed(LeftBracket); - key(Keyboard::RightBracket) = pressed(RightBracket); - key(Keyboard::Backslash) = pressed(Backslash); - key(Keyboard::Semicolon) = pressed(Semicolon); - key(Keyboard::Apostrophe) = pressed(Apostrophe); - key(Keyboard::Comma) = pressed(Comma); - key(Keyboard::Period) = pressed(Period); - key(Keyboard::Slash) = pressed(Slash); - - key(Keyboard::Keypad1) = pressed(Keypad1); - key(Keyboard::Keypad2) = pressed(Keypad2); - key(Keyboard::Keypad3) = pressed(Keypad3); - key(Keyboard::Keypad4) = pressed(Keypad4); - key(Keyboard::Keypad5) = pressed(Keypad5); - key(Keyboard::Keypad6) = pressed(Keypad6); - key(Keyboard::Keypad7) = pressed(Keypad7); - key(Keyboard::Keypad8) = pressed(Keypad8); - key(Keyboard::Keypad9) = pressed(Keypad9); - key(Keyboard::Keypad0) = pressed(Keypad0); - - key(Keyboard::Point) = pressed(Point); - key(Keyboard::Enter) = pressed(Enter); - key(Keyboard::Add) = pressed(Add); - key(Keyboard::Subtract) = pressed(Subtract); - key(Keyboard::Multiply) = pressed(Multiply); - key(Keyboard::Divide) = pressed(Divide); - - key(Keyboard::Up) = pressed(Up); - key(Keyboard::Down) = pressed(Down); - key(Keyboard::Left) = pressed(Left); - key(Keyboard::Right) = pressed(Right); - - key(Keyboard::Tab) = pressed(Tab); - key(Keyboard::Return) = pressed(Return); - key(Keyboard::Spacebar) = pressed(Spacebar); - key(Keyboard::Menu) = pressed(Menu); - - key(Keyboard::Shift) = pressed(LeftShift) || pressed(RightShift); - key(Keyboard::Control) = pressed(LeftControl) || pressed(RightControl); - key(Keyboard::Alt) = pressed(LeftAlt) || pressed(RightAlt); - key(Keyboard::Super) = pressed(LeftSuper) || pressed(RightSuper); - - #undef key - #undef pressed -} - -void x_init(Display* display) { - if(!display) return; - - memset(&scancode, 0, sizeof scancode); - - #define assign(x, y) scancode[x] = XKeysymToKeycode(display, y) - assign(Escape, XK_Escape); - - assign(F1, XK_F1); - assign(F2, XK_F2); - assign(F3, XK_F3); - assign(F4, XK_F4); - assign(F5, XK_F5); - assign(F6, XK_F6); - assign(F7, XK_F7); - assign(F8, XK_F8); - assign(F9, XK_F9); - assign(F10, XK_F10); - assign(F11, XK_F11); - assign(F12, XK_F12); - - assign(ScrollLock, XK_Scroll_Lock); - assign(Pause, XK_Pause); - - assign(Tilde, XK_asciitilde); - - assign(Num0, XK_0); - assign(Num1, XK_1); - assign(Num2, XK_2); - assign(Num3, XK_3); - assign(Num4, XK_4); - assign(Num5, XK_5); - assign(Num6, XK_6); - assign(Num7, XK_7); - assign(Num8, XK_8); - assign(Num9, XK_9); - - assign(Dash, XK_minus); - assign(Equal, XK_equal); - assign(Backspace, XK_BackSpace); - - assign(Insert, XK_Insert); - assign(Delete, XK_Delete); - assign(Home, XK_Home); - assign(End, XK_End); - assign(PageUp, XK_Prior); - assign(PageDown, XK_Next); - - assign(A, XK_A); - assign(B, XK_B); - assign(C, XK_C); - assign(D, XK_D); - assign(E, XK_E); - assign(F, XK_F); - assign(G, XK_G); - assign(H, XK_H); - assign(I, XK_I); - assign(J, XK_J); - assign(K, XK_K); - assign(L, XK_L); - assign(M, XK_M); - assign(N, XK_N); - assign(O, XK_O); - assign(P, XK_P); - assign(Q, XK_Q); - assign(R, XK_R); - assign(S, XK_S); - assign(T, XK_T); - assign(U, XK_U); - assign(V, XK_V); - assign(W, XK_W); - assign(X, XK_X); - assign(Y, XK_Y); - assign(Z, XK_Z); - - assign(LeftBracket, XK_bracketleft); - assign(RightBracket, XK_bracketright); - assign(Backslash, XK_backslash); - assign(Semicolon, XK_semicolon); - assign(Apostrophe, XK_apostrophe); - assign(Comma, XK_comma); - assign(Period, XK_period); - assign(Slash, XK_slash); - - assign(Keypad0, XK_KP_0); - assign(Keypad1, XK_KP_1); - assign(Keypad2, XK_KP_2); - assign(Keypad3, XK_KP_3); - assign(Keypad4, XK_KP_4); - assign(Keypad5, XK_KP_5); - assign(Keypad6, XK_KP_6); - assign(Keypad7, XK_KP_7); - assign(Keypad8, XK_KP_8); - assign(Keypad9, XK_KP_9); - - assign(Add, XK_KP_Add); - assign(Subtract, XK_KP_Subtract); - assign(Multiply, XK_KP_Multiply); - assign(Divide, XK_KP_Divide); - assign(Enter, XK_KP_Enter); - - assign(Up, XK_Up); - assign(Down, XK_Down); - assign(Left, XK_Left); - assign(Right, XK_Right); - - assign(Tab, XK_Tab); - assign(Return, XK_Return); - assign(Spacebar, XK_space); - - assign(LeftControl, XK_Control_L); - assign(RightControl, XK_Control_R); - assign(LeftAlt, XK_Alt_L); - assign(RightAlt, XK_Alt_R); - assign(LeftShift, XK_Shift_L); - assign(RightShift, XK_Shift_R); - assign(LeftSuper, XK_Super_L); - assign(RightSuper, XK_Super_R); - assign(Menu, XK_Menu); - - #undef assign -} diff --git a/ruby/ruby.cpp b/ruby/ruby.cpp index b6f34657..567a894f 100644 --- a/ruby/ruby.cpp +++ b/ruby/ruby.cpp @@ -395,8 +395,8 @@ void InputInterface::driver(const char* driver) { else if(!strcmp(driver, "SDL")) p = new InputSDL(); #endif - #ifdef INPUT_X - else if(!strcmp(driver, "X-Windows")) p = new InputX(); + #ifdef INPUT_XLIB + else if(!strcmp(driver, "Xlib")) p = new InputXlib(); #endif else p = new Input(); @@ -415,8 +415,8 @@ const char* InputInterface::optimalDriver() { return "udev"; #elif defined(INPUT_SDL) return "SDL"; - #elif defined(INPUT_X) - return "X-Windows"; + #elif defined(INPUT_XLIB) + return "Xlib"; #else return "None"; @@ -436,8 +436,8 @@ const char* InputInterface::safestDriver() { return "udev"; #elif defined(INPUT_SDL) return "SDL"; - #elif defined(INPUT_X) - return "X-Windows"; + #elif defined(INPUT_XLIB) + return "Xlib"; #else return "none"; @@ -473,8 +473,8 @@ const char* InputInterface::availableDrivers() { "SDL;" #endif - #if defined(INPUT_X) - "X-Windows;" + #if defined(INPUT_XLIB) + "Xlib;" #endif "None"; @@ -499,8 +499,9 @@ bool InputInterface::set(const string& name, const any& value) { return p ? p->s bool InputInterface::acquire() { return p ? p->acquire() : false; } bool InputInterface::unacquire() { return p ? p->unacquire() : false; } bool InputInterface::acquired() { return p ? p->acquired() : false; } +vector InputInterface::poll() { return p ? p->poll() : vector(); } bool InputInterface::poll(int16_t* table) { return p ? p->poll(table) : false; } -void InputInterface::rumble(unsigned id, bool enable) { if(p) return p->rumble(id, enable); } +void InputInterface::rumble(uint64_t id, bool enable) { if(p) return p->rumble(id, enable); } InputInterface::InputInterface() : p(nullptr) {} InputInterface::~InputInterface() { term(); } diff --git a/ruby/ruby.hpp b/ruby/ruby.hpp index 8bcc495e..7ade4fe6 100644 --- a/ruby/ruby.hpp +++ b/ruby/ruby.hpp @@ -63,6 +63,8 @@ private: }; struct InputInterface { + nall::function onChange; + void driver(const char* driver = ""); const char* optimalDriver(); const char* safestDriver(); @@ -78,8 +80,9 @@ struct InputInterface { bool unacquire(); bool acquired(); + nall::vector poll(); bool poll(int16_t* table); - void rumble(unsigned id, bool enable); + void rumble(uint64_t id, bool enable); InputInterface(); ~InputInterface(); diff --git a/target-ethos/Makefile b/target-ethos/Makefile index 2624ab06..2799c30b 100644 --- a/target-ethos/Makefile +++ b/target-ethos/Makefile @@ -25,7 +25,7 @@ else ifeq ($(platform),macosx) else ifeq ($(platform),linux) ruby := video.glx video.xv video.xshm video.sdl ruby += audio.alsa audio.openal audio.oss audio.pulseaudio audio.pulseaudiosimple audio.ao - ruby += input.udev input.sdl input.x + ruby += input.udev else ifeq ($(platform),bsd) ruby := video.glx ruby += audio.openal audio.oss diff --git a/target-ethos/input/hotkeys.cpp b/target-ethos/input/hotkeys.cpp index 34128416..d1c61623 100644 --- a/target-ethos/input/hotkeys.cpp +++ b/target-ethos/input/hotkeys.cpp @@ -2,7 +2,7 @@ void InputManager::appendHotkeys() { { auto hotkey = new HotkeyInput; hotkey->name = "Toggle Fullscreen Mode"; - hotkey->mapping = "KB0::F11"; + hotkey->mapping = "1/Button/F11"; hotkey->press = [] { utility->toggleFullScreen(); diff --git a/target-ethos/input/input.cpp b/target-ethos/input/input.cpp index d7b22838..bd733cf0 100644 --- a/target-ethos/input/input.cpp +++ b/target-ethos/input/input.cpp @@ -1,82 +1,82 @@ #include "../ethos.hpp" #include "hotkeys.cpp" InputManager* inputManager = nullptr; +HID::Null hidNull; void AbstractInput::bind() { inputList.reset(); lstring list = mapping.split(","); for(auto& mapping : list) { - Input::Type type; - if(mapping.endsWith(".Up")) type = Input::Type::HatUp; - else if(mapping.endsWith(".Down")) type = Input::Type::HatDown; - else if(mapping.endsWith(".Left")) type = Input::Type::HatLeft; - else if(mapping.endsWith(".Right")) type = Input::Type::HatRight; - else if(mapping.endsWith(".Lo")) type = Input::Type::AxisLo; - else if(mapping.endsWith(".Hi")) type = Input::Type::AxisHi; - else if(mapping.beginsWith("JP") && mapping.find("Axis")) type = Input::Type::Axis; - else if(mapping.beginsWith("JP") && mapping.endsWith("Rumble")) type = Input::Type::Rumble; - else if(mapping.beginsWith("MS") && mapping.endsWith("axis")) type = Input::Type::MouseAxis; - else if(mapping.beginsWith("MS")) type = Input::Type::MouseButton; - else type = Input::Type::Button; + lstring values = mapping.split("/"); + if(values.size() == 1) continue; //skip "None" mapping - if(type == Input::Type::Rumble) { - unsigned joypad = mapping[2] - '0'; - inputList.append({type, joypad}); - } else { - string decode = mapping; - if(auto position = decode.find(".")) decode.resize(position()); - unsigned scancode = Scancode::decode(decode); - inputList.append({type, scancode}); + uint64_t id = hex(values[0]); + string group = values(1, ""); + string input = values(2, ""); + string qualifier = values(3, ""); + + Input item; + for(auto device : inputManager->devices) { + if(id != device->id) continue; + if(group == "Rumble") { + item.device = device; + item.id = id; + item.group = 0; + item.input = 0; + break; + } + if(auto groupID = device->find(group)) { + if(auto inputID = device->group[groupID()].find(input)) { + item.device = device; + item.id = id; + item.group = groupID(); + item.input = inputID(); + item.qualifier = Input::Qualifier::None; + if(qualifier == "Lo") item.qualifier = Input::Qualifier::Lo; + if(qualifier == "Hi") item.qualifier = Input::Qualifier::Hi; + break; + } + } } + if(item.device == nullptr) continue; + + inputList.append(item); } } bool AbstractInput::append(string encode) { - if(mapping.find(encode)) return true; //mapping already bound + lstring mappings = mapping.split(","); + if(mappings.find(encode)) return true; //mapping already bound if(mapping.empty() || mapping == "None") mapping = encode; //remove "None" else mapping.append(",", encode); //add to existing mapping list bind(); return true; } -AbstractInput::AbstractInput() : state(false) { -} - // -bool DigitalInput::bind(unsigned scancode, int16_t value) { - using nall::Keyboard; - using nall::Mouse; - - if(scancode == Scancode::None || scancode == keyboard(0)[Keyboard::Escape]) { +bool DigitalInput::bind(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue) { + if(device.isNull() || (device.isKeyboard() && device.group[group].input[input].name == "Escape")) { inputList.reset(); mapping = "None"; return true; } - string encode = Scancode::encode(scancode); + string encode = {hex(device.id), "/", device.group[group].name, "/", device.group[group].input[input].name}; - if(Keyboard::isAnyKey(scancode) || Keyboard::isAnyModifier(scancode) || Joypad::isAnyButton(scancode)) { - if(value == 0) return false; - return append(encode); + if((device.isKeyboard() && group == HID::Keyboard::GroupID::Button) + || (device.isMouse() && group == HID::Mouse::GroupID::Button) + || (device.isJoypad() && group == HID::Joypad::GroupID::Button) + ) { + if(newValue != 0) return append(encode); } - if(Mouse::isAnyButton(scancode)) { - if(value == 0) return false; - return append(encode); - } - - if(Joypad::isAnyHat(scancode)) { - if(value & Joypad::HatUp ) { encode.append(".Up" ); return append(encode); } - if(value & Joypad::HatDown ) { encode.append(".Down" ); return append(encode); } - if(value & Joypad::HatLeft ) { encode.append(".Left" ); return append(encode); } - if(value & Joypad::HatRight) { encode.append(".Right"); return append(encode); } - } - - if(Joypad::isAnyAxis(scancode)) { - if(value < -12288) { encode.append(".Lo"); return append(encode); } - if(value > +24576) { encode.append(".Hi"); return append(encode); } + if((device.isJoypad() && group == HID::Joypad::GroupID::Axis) + || (device.isJoypad() && group == HID::Joypad::GroupID::Hat) + ) { + if(newValue < -16384) return append({encode, "/Lo"}); + if(newValue > +16384) return append({encode, "/Hi"}); } return false; @@ -84,20 +84,24 @@ bool DigitalInput::bind(unsigned scancode, int16_t value) { int16_t DigitalInput::poll() { if(program->focused() == false) return 0; + if(inputList.size() == 0) return 0; bool result = logic; for(auto& item : inputList) { - int16_t value = inputManager->poll(item.scancode); + HID::Device& device = *(item.device); + int16_t value = device.group[item.group].input[item.input].value; bool output = logic; - switch(item.type) { - case Input::Type::Button: output = value; break; - case Input::Type::MouseButton: output = value & input.acquired(); break; - case Input::Type::HatUp: output = value & Joypad::HatUp; break; - case Input::Type::HatDown: output = value & Joypad::HatDown; break; - case Input::Type::HatLeft: output = value & Joypad::HatLeft; break; - case Input::Type::HatRight: output = value & Joypad::HatRight; break; - case Input::Type::AxisLo: output = value < -16384; break; - case Input::Type::AxisHi: output = value > +16384; break; + if((device.isKeyboard() && item.group == HID::Keyboard::GroupID::Button) + || (device.isMouse() && item.group == HID::Mouse::GroupID::Button) + || (device.isJoypad() && item.group == HID::Joypad::GroupID::Button) + ) { + output = value; + } + if((device.isJoypad() && item.group == HID::Joypad::GroupID::Axis) + || (device.isJoypad() && item.group == HID::Joypad::GroupID::Hat) + ) { + if(item.qualifier == Input::Qualifier::Lo) output = value < -16384; + if(item.qualifier == Input::Qualifier::Hi) output = value > +16384; } if(logic == 0) result |= output; if(logic == 1) result &= output; @@ -108,34 +112,37 @@ int16_t DigitalInput::poll() { // -bool RelativeInput::bind(unsigned scancode, int16_t value) { - using nall::Keyboard; - using nall::Mouse; - - if(scancode == Scancode::None || scancode == keyboard(0)[Keyboard::Escape]) { +bool RelativeInput::bind(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue) { + if(device.isNull() || (device.isKeyboard() && device.group[group].input[input].name == "Escape")) { inputList.reset(); mapping = "None"; return true; } - string encode = Scancode::encode(scancode); + string encode = {hex(device.id), "/", device.group[group].name, "/", device.group[group].input[input].name}; - if(Mouse::isAnyAxis(scancode)) return append(encode); - if(Joypad::isAnyAxis(scancode)) return append(encode); + if((device.isMouse() && group == HID::Mouse::GroupID::Axis) + || (device.isJoypad() && group == HID::Joypad::GroupID::Axis) + || (device.isJoypad() && group == HID::Joypad::GroupID::Hat) + ) { + if(newValue < -16384) return append(encode); + if(newValue > +16384) return append(encode); + } return false; } int16_t RelativeInput::poll() { if(program->focused() == false) return 0; + if(inputList.size() == 0) return 0; int16_t result = 0; for(auto& item : inputList) { - int16_t value = inputManager->poll(item.scancode); - switch(item.type) { - case Input::Type::MouseAxis: value = input.acquired() ? value : 0; break; - case Input::Type::Axis: value = value; break; - } + HID::Device& device = *(item.device); + int16_t value = device.group[item.group].input[item.input].value; + if(device.isJoypad() && item.group == HID::Joypad::GroupID::Axis) value >>= 8; + if(device.isJoypad() && item.group == HID::Joypad::GroupID::Hat) value = (value < 0 ? -1 : value > 0 ? + 1 : 0); + if(device.isMouse() && input.acquired() == false) value = 0; result += value; } @@ -144,105 +151,28 @@ int16_t RelativeInput::poll() { // -bool AbsoluteInput::bind(unsigned scancode, int16_t value) { - using nall::Keyboard; - using nall::Mouse; - - if(scancode == Scancode::None || scancode == keyboard(0)[Keyboard::Escape]) { +bool RumbleInput::bind(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue) { + if(device.isNull() || (device.isKeyboard() && device.group[group].input[input].name == "Escape")) { inputList.reset(); mapping = "None"; return true; } - string encode = Scancode::encode(scancode); + string encode = {hex(device.id), "/Rumble"}; - if(Mouse::isAnyAxis(scancode)) { - //only one input can be assigned for absolute positioning - inputList.reset(); - mapping = encode; - return true; + if(device.isJoypad() && group == HID::Joypad::GroupID::Button) { + if(newValue != 0) return append(encode); } return false; } -int16_t AbsoluteInput::poll() { - if(program->focused() == false) return -32768; - int16_t result = -32768; //offscreen value - - using nall::Mouse; - - Position position = phoenix::Mouse::position(); - Geometry geometry = presentation->geometry(); - - if(position.x < geometry.x - || position.y < geometry.y - || position.x >= geometry.x + geometry.width - || position.y >= geometry.y + geometry.height) { - //cursor is offscreen - position.x = -32768; - position.y = -32768; - } else { - //convert from screen to viewport coordinates - double x = position.x - geometry.x; - double y = position.y - geometry.y; - - //scale coordinate range to -0.5 to +0.5 (0.0 = center) - x = x * 1.0 / geometry.width - 0.5; - y = y * 1.0 / geometry.height - 0.5; - - //scale coordinates to -32767 to +32767 - signed px = (signed)(x * 65535.0); - signed py = (signed)(y * 65535.0); - - //clamp to valid range - position.x = max(-32767, min(+32767, px)); - position.y = max(-32767, min(+32767, py)); - } - - for(auto& item : inputList) { - if(item.scancode == mouse(0)[Mouse::Xaxis]) { - result = position.x; - } - - if(item.scancode == mouse(0)[Mouse::Yaxis]) { - result = position.y; - } - } - - return result; -} - -bool RumbleInput::bind(unsigned scancode, int16_t value) { - using nall::Keyboard; - - if(scancode == Scancode::None || scancode == keyboard(0)[Keyboard::Escape]) { - inputList.reset(); - mapping = "None"; - return true; - } - - string encode = Scancode::encode(scancode); - - if(Joypad::isAnyButton(scancode)) { - if(value == 0) return false; - if(auto position = encode.find("::")) encode.resize(position()); - encode.append("::Rumble"); - return append(encode); - } - - return false; -} - -int16_t RumbleInput::poll() { - return false; -} - void RumbleInput::rumble(bool enable) { if(program->focused() == false) return; + if(inputList.size() == 0) return; for(auto& item : inputList) { - input.rumble(item.scancode, enable); + input.rumble(item.id, enable); } } @@ -255,48 +185,51 @@ HotkeyInput::HotkeyInput() { // +void InputManager::onChange(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue) { + if(settings->focused()) { + inputSettings->inputEvent(device, group, input, oldValue, newValue); + hotkeySettings->inputEvent(device, group, input, oldValue, newValue); + } +} + +HID::Device* InputManager::findMouse() { + for(auto device : devices) { + if(device->isMouse()) return device; + } + return nullptr; +} + void InputManager::bind() { for(auto& input : inputMap) input->bind(); for(auto& input : hotkeyMap) input->bind(); } void InputManager::poll() { - using nall::Keyboard; - - activeScancode = !activeScancode; - if(input.poll(scancode[activeScancode]) == false) return; - - for(unsigned n = 0; n < Scancode::Limit; n++) { - if(scancode[0][n] != scancode[1][n]) { - if(settings->focused()) { - inputSettings->inputEvent(n, scancode[activeScancode][n]); - hotkeySettings->inputEvent(n, scancode[activeScancode][n]); - } + auto devices = input.poll(); + bool changed = devices.size() != this->devices.size(); + if(changed == false) { + for(unsigned n = 0; n < devices.size(); n++) { + changed = devices[n] != this->devices[n]; + if(changed) break; } } + if(changed == true) { + this->devices = devices; + bind(); + } if(presentation->focused()) pollHotkeys(); } -int16_t InputManager::poll(unsigned scancode) { - return this->scancode[activeScancode][scancode]; -} - void InputManager::saveConfiguration() { config.save(program->path("input.bml")); } InputManager::InputManager() { inputManager = this; - scancode[0] = new int16_t[Scancode::Limit](); - scancode[1] = new int16_t[Scancode::Limit](); - activeScancode = 0; bootstrap(); -} -InputManager::~InputManager() { - delete[] scancode[0]; - delete[] scancode[1]; + input.onChange = {&InputManager::onChange, this}; } void InputManager::bootstrap() { @@ -316,8 +249,7 @@ void InputManager::bootstrap() { AbstractInput* abstract = nullptr; if(input.type == 0) abstract = new DigitalInput; if(input.type == 1) abstract = new RelativeInput; - if(input.type == 2) abstract = new AbsoluteInput; - if(input.type == 3) abstract = new RumbleInput; + if(input.type == 2) abstract = new RumbleInput; if(abstract == nullptr) continue; abstract->name = string{input.name}.replace(" ", ""); diff --git a/target-ethos/input/input.hpp b/target-ethos/input/input.hpp index 2d11f03c..0cbf9205 100644 --- a/target-ethos/input/input.hpp +++ b/target-ethos/input/input.hpp @@ -1,45 +1,42 @@ +extern HID::Null hidNull; + struct AbstractInput { string name; string mapping; - bool logic; //0 = OR, 1 = AND - bool state; + bool logic = 0; //0 = OR, 1 = AND + bool state = 0; struct Input { - enum class Type : unsigned { Button, MouseButton, MouseAxis, HatUp, HatDown, HatLeft, HatRight, Axis, AxisLo, AxisHi, Rumble } type; - unsigned scancode; + HID::Device* device = nullptr; + uint64_t id = 0; + unsigned group = 0; + unsigned input = 0; + enum class Qualifier : unsigned { None, Lo, Hi } qualifier; }; vector inputList; void bind(); bool append(string mapping); - virtual bool bind(unsigned scancode, int16_t value) = 0; - virtual int16_t poll() = 0; + virtual bool bind(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue) { return false; } + virtual int16_t poll() { return 0; } virtual void rumble(bool enable) {} - AbstractInput(); }; struct DigitalInput : AbstractInput { using AbstractInput::bind; - bool bind(unsigned scancode, int16_t value); + bool bind(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue); int16_t poll(); }; struct RelativeInput : AbstractInput { using AbstractInput::bind; - bool bind(unsigned scancode, int16_t value); - int16_t poll(); -}; - -struct AbsoluteInput : AbstractInput { - using AbstractInput::bind; - bool bind(unsigned scancode, int16_t value); + bool bind(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue); int16_t poll(); }; struct RumbleInput : AbstractInput { using AbstractInput::bind; - bool bind(unsigned scancode, int16_t value); - int16_t poll(); + bool bind(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue); void rumble(bool enable); }; @@ -50,18 +47,17 @@ struct HotkeyInput : DigitalInput { }; struct InputManager { + vector devices; vector inputMap; vector hotkeyMap; - int16_t* scancode[2]; - bool activeScancode; + void onChange(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue); + HID::Device* findMouse(); void bind(); void poll(); - int16_t poll(unsigned scancode); void saveConfiguration(); void bootstrap(); InputManager(); - ~InputManager(); //hotkeys.cpp void appendHotkeys(); diff --git a/target-ethos/settings/hotkey.cpp b/target-ethos/settings/hotkey.cpp index 44c32979..4395d1ed 100644 --- a/target-ethos/settings/hotkey.cpp +++ b/target-ethos/settings/hotkey.cpp @@ -26,8 +26,6 @@ void HotkeySettings::refresh() { unsigned index = 0; for(auto& hotkey : inputManager->hotkeyMap) { string mapping = hotkey->mapping; - mapping.replace("KB0::", ""); - mapping.replace("MS0::", "Mouse::"); mapping.replace(",", " and "); inputList.setText(index++, {hotkey->name, mapping}); } @@ -36,7 +34,7 @@ void HotkeySettings::refresh() { void HotkeySettings::eraseInput() { activeInput = inputManager->hotkeyMap[inputList.selection()]; - inputEvent(Scancode::None, 1); + inputEvent(hidNull, 0, 0, 0, 1); } void HotkeySettings::assignInput() { @@ -47,14 +45,11 @@ void HotkeySettings::assignInput() { setEnabled(false); } -void HotkeySettings::inputEvent(unsigned scancode, int16_t value) { - using nall::Mouse; - +void HotkeySettings::inputEvent(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue) { if(activeInput == nullptr) return; - if(value != 1) return; - if(Mouse::isAnyButton(scancode) || Mouse::isAnyAxis(scancode)) return; - if(Joypad::isAnyAxis(scancode)) return; - if(activeInput->bind(scancode, value) == false) return; + if(device.isMouse()) return; + if(device.isJoypad() && group == HID::Joypad::GroupID::Axis) return; + if(activeInput->bind(device, group, input, oldValue, newValue) == false) return; activeInput = nullptr; settings->setStatusText(""); diff --git a/target-ethos/settings/hotkey.hpp b/target-ethos/settings/hotkey.hpp index ac187537..c254df73 100644 --- a/target-ethos/settings/hotkey.hpp +++ b/target-ethos/settings/hotkey.hpp @@ -8,7 +8,7 @@ struct HotkeySettings : SettingsLayout { void refresh(); void eraseInput(); void assignInput(); - void inputEvent(unsigned scancode, int16_t value); + void inputEvent(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue); HotkeySettings(); private: diff --git a/target-ethos/settings/input.cpp b/target-ethos/settings/input.cpp index 3aa0a6a3..a5d5ca13 100644 --- a/target-ethos/settings/input.cpp +++ b/target-ethos/settings/input.cpp @@ -69,8 +69,7 @@ void InputSettings::synchronize() { assign[2].setVisible(true); } - if(dynamic_cast(selectedInput) - || dynamic_cast(selectedInput)) { + if(dynamic_cast(selectedInput)) { assign[0].setText("Mouse X-axis"); assign[1].setText("Mouse Y-axis"); assign[0].setVisible(true); @@ -123,8 +122,6 @@ void InputSettings::inputChanged() { auto& input = activeDevice().input[number]; auto abstract = inputManager->inputMap(input.guid); string mapping = abstract->mapping; - mapping.replace("KB0::", ""); - mapping.replace("MS0::", "Mouse::"); mapping.replace(",", " or "); inputList.setText(index++, {input.name, mapping}); } @@ -138,7 +135,7 @@ void InputSettings::resetInput() { unsigned length = device.input.size(); for(unsigned n = 0; n < length; n++) { activeInput = inputManager->inputMap[device.input[n].guid]; - inputEvent(Scancode::None, 1); + inputEvent(hidNull, 0, 0, 0, 1); } } @@ -146,7 +143,7 @@ void InputSettings::eraseInput() { unsigned number = activeDevice().order[inputList.selection()]; auto& input = activeDevice().input[number]; activeInput = inputManager->inputMap[input.guid]; - inputEvent(Scancode::None, 1); + inputEvent(hidNull, 0, 0, 0, 1); } void InputSettings::assignInput() { @@ -165,20 +162,22 @@ void InputSettings::assignMouseInput(unsigned n) { activeInput = inputManager->inputMap[input.guid]; if(dynamic_cast(activeInput)) { - return inputEvent(mouse(0).button(n), 1, true); + if(auto hidMouse = inputManager->findMouse()) { + return inputEvent(*hidMouse, HID::Mouse::GroupID::Button, n, 0, 1, true); + } } - if(dynamic_cast(activeInput) - || dynamic_cast(activeInput)) { - return inputEvent(mouse(0).axis(n), 1, true); + if(dynamic_cast(activeInput)) { + if(auto hidMouse = inputManager->findMouse()) { + return inputEvent(*hidMouse, HID::Mouse::GroupID::Axis, n, 0, +32767, true); + } } } -void InputSettings::inputEvent(unsigned scancode, int16_t value, bool allowMouseInput) { - using nall::Mouse; +void InputSettings::inputEvent(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue, bool allowMouseInput) { if(activeInput == nullptr) return; - if(allowMouseInput == false && (Mouse::isAnyButton(scancode) || Mouse::isAnyAxis(scancode))) return; - if(activeInput->bind(scancode, value) == false) return; + if(allowMouseInput == false && device.isMouse()) return; + if(activeInput->bind(device, group, input, oldValue, newValue) == false) return; activeInput = nullptr; inputChanged(); diff --git a/target-ethos/settings/input.hpp b/target-ethos/settings/input.hpp index 39ab4348..20a505f3 100644 --- a/target-ethos/settings/input.hpp +++ b/target-ethos/settings/input.hpp @@ -28,7 +28,7 @@ struct InputSettings : SettingsLayout { void eraseInput(); void assignInput(); void assignMouseInput(unsigned n); - void inputEvent(unsigned scancode, int16_t value, bool allowMouseInput = false); + void inputEvent(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue, bool allowMouseInput = false); InputSettings(); private: