Update to v093r12 release.

byuu says:

I've completely redone the ethos InputManager and ruby to work on
HID::Device objects instead of one giant scancode pool.

Currently only the udev driver supports the changes to ruby, so only
Linux users will be able to compile and run this WIP build.

The nice thing about the new system is that it's now possible to
uniquely identify controllers, so if you swap out gamepads, you won't
end up with it working but with all the mappings all screwed up. Since
higan lets you map multiple physical inputs to one emulated input, you
can now configure your keyboard and multiple gamepads to the same
emulated input, and then just use whatever controller you want.

Because USB gamepad makers failed to provide unique serial#s with each
controller, we have to limit the mapping to specific USB ports.
Otherwise, we couldn't distinguish two otherwise identical gamepads. So
basically your computer USB ports act like real game console input port
numbers. Which is kind of neat, I guess.

And the really nice thing about the new system is that we now have the
capability to support hotplugging input devices. I haven't yet added
this to any drivers, but I'm definitely going to add it to udev for v094
official.

Finally, with the device ID (vendor ID + product ID) exposed, we gain
one last really cool feature that we may be able to develop more in the
future. Say we created a joypad.bml file to include with higan. In it,
we'd store the Xbox 360 controller, and pre-defined button mappings for
each emulated system. So if higan detects you have an Xbox 360
controller, you can just plug it in and use it. Even better, we can
clearly specify the difference between triggers and analog axes, and
name each individual input. So you'd see "Xbox 360 Gamepad #1: Left
Trigger" instead of higan v093's "JP0::Axis2.Hi"

Note: for right now, ethos' input manager isn't filtering the device IDs
to look pretty. So you're going to see a 64-bit hex value for a device
ID right now instead of something like Joypad#N for now.
This commit is contained in:
Tim Allen 2013-12-23 22:43:51 +11:00
parent 73be2e729c
commit 3ce1d19f7a
28 changed files with 771 additions and 759 deletions

View File

@ -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/";

View File

@ -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;
};

View File

@ -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);
}

117
nall/hid.hpp Normal file
View File

@ -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> input;
Group() {}
Group(const string& name) : name(name) {}
void append(const string& name) {
input.append({name});
}
optional<unsigned> 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> 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<unsigned> 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

View File

@ -24,6 +24,7 @@
#include <nall/group.hpp>
#include <nall/gzip.hpp>
#include <nall/hashset.hpp>
#include <nall/hid.hpp>
#include <nall/http.hpp>
#include <nall/image.hpp>
#include <nall/inflate.hpp>

View File

@ -21,6 +21,7 @@ namespace Math {
#include <utility>
#include <assert.h>
#include <endian.h>
#include <errno.h>
#include <limits.h>
#include <math.h>

View File

@ -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<HID::Device*> 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 <ruby/input/udev.cpp>
#endif
#ifdef INPUT_X
#include <ruby/input/x.cpp>
#ifdef INPUT_XLIB
#include <ruby/input/xlib.cpp>
#endif

View File

@ -13,8 +13,9 @@ struct Input {
virtual bool unacquire() { return false; }
virtual bool acquired() { return false; }
virtual nall::vector<nall::HID::Device*> 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() {}

66
ruby/input/joypad/sdl.cpp Normal file
View File

@ -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<Joystick> 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

View File

@ -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<Joystick> 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<HID::Device*>& 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

View File

@ -1,3 +1,6 @@
#ifndef RUBY_INPUT_JOYPAD_XINPUT
#define RUBY_INPUT_JOYPAD_XINPUT
#include <xinput.h>
namespace ruby {
@ -99,3 +102,5 @@ struct InputJoypadXInput {
};
}
#endif

View File

@ -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<Key> 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<HID::Device*>& 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

View File

@ -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<HID::Device*>& 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

View File

@ -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 <SDL/SDL.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#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();
}
};

View File

@ -57,13 +57,22 @@ struct pInputUdev {
return xlibMouse.acquired();
}
vector<HID::Device*> poll() {
vector<HID::Device*> 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);
}

View File

@ -1,53 +0,0 @@
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
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)
}

75
ruby/input/xlib.cpp Normal file
View File

@ -0,0 +1,75 @@
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#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<uintptr_t>(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)
}

View File

@ -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
}

View File

@ -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<HID::Device*> InputInterface::poll() { return p ? p->poll() : vector<HID::Device*>(); }
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(); }

View File

@ -63,6 +63,8 @@ private:
};
struct InputInterface {
nall::function<void (nall::HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue)> onChange;
void driver(const char* driver = "");
const char* optimalDriver();
const char* safestDriver();
@ -78,8 +80,9 @@ struct InputInterface {
bool unacquire();
bool acquired();
nall::vector<nall::HID::Device*> poll();
bool poll(int16_t* table);
void rumble(unsigned id, bool enable);
void rumble(uint64_t id, bool enable);
InputInterface();
~InputInterface();

View File

@ -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

View File

@ -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();

View File

@ -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(" ", "");

View File

@ -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<Input> 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<HID::Device*> devices;
vector<AbstractInput*> inputMap;
vector<HotkeyInput*> 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();

View File

@ -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("");

View File

@ -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:

View File

@ -69,8 +69,7 @@ void InputSettings::synchronize() {
assign[2].setVisible(true);
}
if(dynamic_cast<RelativeInput*>(selectedInput)
|| dynamic_cast<AbsoluteInput*>(selectedInput)) {
if(dynamic_cast<RelativeInput*>(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<DigitalInput*>(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<RelativeInput*>(activeInput)
|| dynamic_cast<AbsoluteInput*>(activeInput)) {
return inputEvent(mouse(0).axis(n), 1, true);
if(dynamic_cast<RelativeInput*>(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();

View File

@ -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: