Update to v093r11 release.

byuu says:

Changelog:
- GBA: SOUND_CTL_H is readable, fixes sound effects in Mario&Luigi
  Superstar Saga [Cydrak] (note: game is still unplayable due to other
  bugs)
- phoenix/Windows: workaround for Win32 API ListView bug, fixes slot
  loading behavior
- ruby: added udev driver for Linux with rumble support, and added
  rumble support to existing RawInput driver for XInput and DirectInput
- ethos: added new "Rumble" mapping to GBA input assignment, use it to
  tell higan which controller to rumble (clear it to disable rumble)
- GBA: Game Boy Player rumble is now fully emulated
- core: added new normalized raw-color palette mode for Display
  Emulation shaders

The way rumble was added to ethos was somewhat hackish. The support
doesn't really exist in nall.

I need to redesign the entire input system, but that's not a change
I want to make so close to a release.
This commit is contained in:
Tim Allen
2013-12-21 21:45:58 +11:00
parent 84fab07756
commit 73be2e729c
36 changed files with 1247 additions and 263 deletions

View File

@@ -143,6 +143,9 @@ struct pInputCarbon {
return true;
}
void rumble(unsigned id, bool enable) {
}
bool init() {
return true;
}

View File

@@ -251,7 +251,10 @@ public:
return true;
}
bool init_joypad(const DIDEVICEINSTANCE* instance) {
void rumble(unsigned id, bool enable) {
}
bool initJoypad(const DIDEVICEINSTANCE* instance) {
unsigned n;
for(n = 0; n < Joypad::Count; n++) { if(!device.gamepad[n]) break; }
if(n >= Joypad::Count) return DIENUM_STOP;
@@ -267,7 +270,7 @@ public:
return DIENUM_CONTINUE;
}
bool init_axis(const DIDEVICEOBJECTINSTANCE* instance) {
bool initAxis(const DIDEVICEOBJECTINSTANCE* instance) {
signed n;
for(n = Joypad::Count - 1; n >= 0; n--) { if(device.gamepad[n]) break; }
if(n < 0) return DIENUM_STOP;
@@ -375,11 +378,11 @@ public:
};
BOOL CALLBACK DI_EnumJoypadsCallback(const DIDEVICEINSTANCE* instance, void* p) {
return ((pInputDI*)p)->init_joypad(instance);
return ((pInputDI*)p)->initJoypad(instance);
}
BOOL CALLBACK DI_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, void* p) {
return ((pInputDI*)p)->init_axis(instance);
return ((pInputDI*)p)->initAxis(instance);
}
DeclareInput(DI)

254
ruby/input/joypad/udev.cpp Normal file
View File

@@ -0,0 +1,254 @@
namespace ruby {
struct InputJoypadUdev {
udev* context = nullptr;
udev_monitor* monitor = nullptr;
udev_enumerate* enumerator = nullptr;
udev_list_entry* devices = nullptr;
udev_list_entry* item = nullptr;
struct JoystickInput {
signed code = 0;
unsigned id = 0;
int16_t value = 0;
input_absinfo info;
JoystickInput() {}
JoystickInput(signed code) : code(code) {}
JoystickInput(signed code, unsigned id) : code(code), id(id) {}
bool operator< (const JoystickInput& source) const { return code < source.code; }
bool operator==(const JoystickInput& source) const { return code == source.code; }
};
struct Joystick {
string path;
dev_t device = 0;
int fd = -1;
uint8_t evbit[(EV_MAX + 7) / 8] = {0};
uint8_t keybit[(KEY_MAX + 7) / 8] = {0};
uint8_t absbit[(ABS_MAX + 7) / 8] = {0};
uint8_t ffbit[(FF_MAX + 7) / 8] = {0};
unsigned effects = 0;
string name;
string manufacturer;
string product;
string serial;
string vendorID;
string productID;
set<JoystickInput> axes;
set<JoystickInput> hats;
set<JoystickInput> buttons;
bool rumble = false;
unsigned effectID = 0;
};
vector<Joystick> joysticks;
bool poll(int16_t* table) {
unsigned i = 0;
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;
signed axis = (value - input().info.minimum) * 65535ll / range - 32767;
if(axis > +32767) axis = +32767;
if(axis < -32768) axis = -32768;
input().value = axis;
}
if(auto input = js.hats.find({code})) {
input().value = value;
}
}
if(type == EV_KEY) {
if(code >= BTN_MISC) {
if(auto input = js.buttons.find({code})) {
input().value = value;
}
}
}
}
}
for(auto input : js.axes) {
table[joypad(i).axis(input.id)] = input.value;
}
for(unsigned id = 0; id < (js.hats.size() + 1) / 2; id++) {
table[joypad(i).hat(id)] = 0;
}
for(auto input : js.hats) {
unsigned hat = 0;
if(input.code == ABS_HAT0X || input.code == ABS_HAT0Y) hat = 0;
if(input.code == ABS_HAT1X || input.code == ABS_HAT1Y) hat = 1;
if(input.code == ABS_HAT2X || input.code == ABS_HAT2Y) hat = 2;
if(input.code == ABS_HAT3X || input.code == ABS_HAT3Y) hat = 3;
bool orientation = 0;
if(input.code == ABS_HAT0X || input.code == ABS_HAT1X || input.code == ABS_HAT2X || input.code == ABS_HAT3X) orientation = 0;
if(input.code == ABS_HAT0Y || input.code == ABS_HAT1Y || input.code == ABS_HAT2Y || input.code == ABS_HAT3Y) orientation = 1;
signed value = 0;
if(orientation == 0) {
if(input.value < 0) value |= Joypad::HatLeft;
if(input.value > 0) value |= Joypad::HatRight;
} else {
if(input.value < 0) value |= Joypad::HatUp;
if(input.value > 0) value |= Joypad::HatDown;
}
table[joypad(i).hat(hat)] |= value;
}
for(auto input : js.buttons) {
table[joypad(i).button(input.id)] = input.value;
}
i++;
}
return true;
}
void rumble(unsigned id, bool enable) {
if(id >= joysticks.size()) return;
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));
}
bool init() {
context = udev_new();
if(context == nullptr) return false;
monitor = udev_monitor_new_from_netlink(context, "udev");
if(monitor) {
udev_monitor_filter_add_match_subsystem_devtype(monitor, "input", nullptr);
udev_monitor_enable_receiving(monitor);
}
enumerator = udev_enumerate_new(context);
if(enumerator) {
udev_enumerate_add_match_property(enumerator, "ID_INPUT_JOYSTICK", "1");
udev_enumerate_scan_devices(enumerator);
devices = udev_enumerate_get_list_entry(enumerator);
for(udev_list_entry* item = devices; item != nullptr; item = udev_list_entry_get_next(item)) {
const char* name = udev_list_entry_get_name(item);
struct udev_device* device = udev_device_new_from_syspath(context, name);
const char* deviceNode = udev_device_get_devnode(device);
if(deviceNode) createJoystick(device, deviceNode);
udev_device_unref(device);
}
}
return true;
}
void term() {
if(enumerator) { udev_enumerate_unref(enumerator); enumerator = nullptr; }
}
private:
void createJoystick(udev_device* device, const char* path) {
Joystick js;
js.path = path;
struct stat st;
if(stat(path, &st) < 0) return;
js.device = st.st_rdev;
js.fd = open(path, O_RDWR | O_NONBLOCK);
if(js.fd < 0) return;
uint8_t evbit[(EV_MAX + 7) / 8] = {0};
uint8_t keybit[(KEY_MAX + 7) / 8] = {0};
uint8_t absbit[(ABS_MAX + 7) / 8] = {0};
ioctl(js.fd, EVIOCGBIT(0, sizeof(js.evbit)), js.evbit);
ioctl(js.fd, EVIOCGBIT(EV_KEY, sizeof(js.keybit)), js.keybit);
ioctl(js.fd, EVIOCGBIT(EV_ABS, sizeof(js.absbit)), js.absbit);
ioctl(js.fd, EVIOCGBIT(EV_FF, sizeof(js.ffbit)), js.ffbit);
ioctl(js.fd, EVIOCGEFFECTS, &js.effects);
#define testBit(buffer, bit) (buffer[(bit) >> 3] & 1 << ((bit) & 7))
if(testBit(js.evbit, EV_KEY)) {
if(udev_device* parent = udev_device_get_parent_with_subsystem_devtype(device, "input", nullptr)) {
js.name = udev_device_get_sysattr_value(parent, "name");
js.vendorID = udev_device_get_sysattr_value(parent, "id/vendor");
js.productID = udev_device_get_sysattr_value(parent, "id/product");
if(udev_device* root = udev_device_get_parent_with_subsystem_devtype(parent, "usb", "usb_device")) {
if(js.vendorID == udev_device_get_sysattr_value(root, "idVendor")
&& js.productID == udev_device_get_sysattr_value(root, "idProduct")
) {
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");
}
}
}
unsigned axes = 0;
unsigned hats = 0;
unsigned buttons = 0;
for(signed i = 0; i < ABS_MISC; i++) {
if(testBit(js.absbit, i)) {
if(i >= ABS_HAT0X && i <= ABS_HAT3Y) {
if(auto hat = js.hats.insert({i, hats++})) {
ioctl(js.fd, EVIOCGABS(i), &hat().info);
}
} else {
if(auto axis = js.axes.insert({i, axes++})) {
ioctl(js.fd, EVIOCGABS(i), &axis().info);
}
}
}
}
for(signed i = BTN_JOYSTICK; i < KEY_MAX; i++) {
if(testBit(js.keybit, i)) {
js.buttons.insert({i, buttons++});
}
}
for(signed i = BTN_MISC; i < BTN_JOYSTICK; i++) {
if(testBit(js.keybit, i)) {
js.buttons.insert({i, buttons++});
}
}
js.rumble = js.effects >= 2 && testBit(js.ffbit, FF_RUMBLE);
if(js.rumble) {
ff_effect effect;
memset(&effect, 0, sizeof(ff_effect));
effect.type = FF_RUMBLE;
effect.id = -1;
effect.u.rumble.strong_magnitude = 65535;
effect.u.rumble.weak_magnitude = 65535;
ioctl(js.fd, EVIOCSFF, &effect);
js.effectID = effect.id;
}
joysticks.append(js);
}
#undef testBit
}
};
}

View File

@@ -0,0 +1,101 @@
#include <xinput.h>
namespace ruby {
struct InputJoypadXInput {
HMODULE libxinput = nullptr;
DWORD WINAPI (*XInputGetState)(DWORD, XINPUT_STATE*) = nullptr;
DWORD WINAPI (*XInputSetState)(DWORD, XINPUT_VIBRATION*) = nullptr;
struct Joystick {
unsigned id;
int16_t hat = 0;
int16_t axis[6] = {0};
bool button[10] = {0};
};
vector<Joystick> joysticks;
bool poll(int16_t* table) {
if(!XInputGetState) return false;
for(auto& js : joysticks) {
XINPUT_STATE state;
if(XInputGetState(js.id, &state) != ERROR_SUCCESS) continue;
int16_t hat = 0;
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ) hat |= Joypad::HatUp;
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ) hat |= Joypad::HatDown;
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ) hat |= Joypad::HatLeft;
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) hat |= Joypad::HatRight;
//scale trigger ranges from (0 to 255) to (-32768 to +32767)
uint16_t triggerL = state.Gamepad.bLeftTrigger;
uint16_t triggerR = state.Gamepad.bRightTrigger;
triggerL = triggerL << 8 | triggerL << 0;
triggerR = triggerR << 8 | triggerR << 0;
table[joypad(js.id).axis(0)] = (int16_t)state.Gamepad.sThumbLX;
table[joypad(js.id).axis(1)] = (int16_t)state.Gamepad.sThumbLY;
table[joypad(js.id).axis(2)] = (int16_t)state.Gamepad.sThumbRX;
table[joypad(js.id).axis(3)] = (int16_t)state.Gamepad.sThumbRY;
table[joypad(js.id).axis(4)] = (int16_t)((~triggerL) - 32768);
table[joypad(js.id).axis(5)] = (int16_t)((~triggerR) - 32768);
table[joypad(js.id).hat(0)] = (int16_t)hat;
table[joypad(js.id).button(0)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_A);
table[joypad(js.id).button(1)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_B);
table[joypad(js.id).button(2)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_X);
table[joypad(js.id).button(3)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_Y);
table[joypad(js.id).button(4)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK);
table[joypad(js.id).button(5)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_START);
table[joypad(js.id).button(6)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER);
table[joypad(js.id).button(7)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER);
table[joypad(js.id).button(8)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB);
table[joypad(js.id).button(9)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB);
}
return true;
}
void rumble(unsigned id, bool enable) {
if(!XInputSetState) return;
if(id >= joysticks.size()) return;
XINPUT_VIBRATION vibration;
memset(&vibration, 0, sizeof(XINPUT_VIBRATION));
vibration.wLeftMotorSpeed = enable ? 65535 : 0; //low-frequency motor (0 = off, 65535 = max)
vibration.wRightMotorSpeed = enable ? 65535 : 0; //high-frequency motor (0 = off, 65535 = max)
XInputSetState(joysticks(id).id, &vibration);
}
bool init() {
if(!libxinput) libxinput = LoadLibraryA("xinput1_3.dll");
if(!libxinput) libxinput = LoadLibraryA("xinput1_2.dll");
if(!libxinput) libxinput = LoadLibraryA("xinput1_1.dll");
if(!libxinput) return false;
XInputGetState = (DWORD WINAPI (*)(DWORD, XINPUT_STATE*))GetProcAddress(libxinput, "XInputGetState");
XInputSetState = (DWORD WINAPI (*)(DWORD, XINPUT_VIBRATION*))GetProcAddress(libxinput, "XInputSetState");
//XInput supports a maximum of four controllers
for(unsigned id = 0; id < 4; id++) {
XINPUT_STATE state;
if(XInputGetState(id, &state) == ERROR_SUCCESS) {
Joystick js;
js.id = id;
joysticks.append(js);
}
}
return true;
}
void term() {
if(libxinput) {
FreeLibrary(libxinput);
libxinput = nullptr;
}
}
};
}

View File

@@ -0,0 +1,277 @@
namespace ruby {
struct InputKeyboardXlib {
Display* display = nullptr;
uint8_t scancode[256] = {0};
enum XScancode : unsigned {
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,
};
bool poll(int16_t* table) {
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
return true;
}
bool init() {
display = XOpenDisplay(0);
#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
return true;
}
void term() {
if(display) {
XCloseDisplay(display);
display = nullptr;
}
}
};
}

128
ruby/input/mouse/xlib.cpp Normal file
View File

@@ -0,0 +1,128 @@
namespace ruby {
struct InputMouseXlib {
uintptr_t handle = 0;
Display* display = nullptr;
Window rootWindow;
Cursor invisibleCursor;
unsigned screenWidth = 0;
unsigned screenHeight = 0;
struct Mouse {
bool acquired = false;
signed numerator = 0;
signed denominator = 0;
signed threshold = 0;
unsigned relativeX = 0;
unsigned relativeY = 0;
} ms;
bool acquire() {
if(acquired()) return true;
if(XGrabPointer(display, handle, True, 0, GrabModeAsync, GrabModeAsync, rootWindow, invisibleCursor, CurrentTime) == GrabSuccess) {
//backup existing cursor acceleration settings
XGetPointerControl(display, &ms.numerator, &ms.denominator, &ms.threshold);
//disable cursor acceleration
XChangePointerControl(display, True, False, 1, 1, 0);
//center cursor (so that first relative poll returns 0, 0 if mouse has not moved)
XWarpPointer(display, None, rootWindow, 0, 0, 0, 0, screenWidth / 2, screenHeight / 2);
return ms.acquired = true;
} else {
return ms.acquired = false;
}
}
bool unacquire() {
if(acquired()) {
//restore cursor acceleration and release cursor
XChangePointerControl(display, True, True, ms.numerator, ms.denominator, ms.threshold);
XUngrabPointer(display, CurrentTime);
ms.acquired = false;
}
return true;
}
bool acquired() {
return ms.acquired;
}
bool poll(int16_t* table) {
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
table[mouse(0).axis(0)] = (int16_t)(rootXReturn - screenWidth / 2);
table[mouse(0).axis(1)] = (int16_t)(rootYReturn - screenHeight / 2);
if(table[mouse(0).axis(0)] != 0 || table[mouse(0).axis(1)] != 0) {
//if mouse moved, re-center mouse for next poll
XWarpPointer(display, None, rootWindow, 0, 0, 0, 0, screenWidth / 2, screenHeight / 2);
}
} else {
table[mouse(0).axis(0)] = (int16_t)(rootXReturn - ms.relativeX);
table[mouse(0).axis(1)] = (int16_t)(rootYReturn - ms.relativeY);
ms.relativeX = rootXReturn;
ms.relativeY = rootYReturn;
}
table[mouse(0).button(0)] = (bool)(maskReturn & Button1Mask);
table[mouse(0).button(1)] = (bool)(maskReturn & Button2Mask);
table[mouse(0).button(2)] = (bool)(maskReturn & Button3Mask);
table[mouse(0).button(3)] = (bool)(maskReturn & Button4Mask);
table[mouse(0).button(4)] = (bool)(maskReturn & Button5Mask);
return true;
}
bool init(uintptr_t handle) {
this->handle = handle;
display = XOpenDisplay(0);
rootWindow = DefaultRootWindow(display);
XWindowAttributes attributes;
XGetWindowAttributes(display, rootWindow, &attributes);
screenWidth = attributes.width;
screenHeight = attributes.height;
//create invisible cursor for use when mouse is acquired
Pixmap pixmap;
XColor black, unused;
static char invisibleData[8] = {0};
Colormap colormap = DefaultColormap(display, DefaultScreen(display));
XAllocNamedColor(display, colormap, "black", &black, &unused);
pixmap = XCreateBitmapFromData(display, handle, invisibleData, 8, 8);
invisibleCursor = XCreatePixmapCursor(display, pixmap, pixmap, &black, &black, 0, 0);
XFreePixmap(display, pixmap);
XFreeColors(display, colormap, &black.pixel, 1, 0);
ms.acquired = false;
ms.relativeX = 0;
ms.relativeY = 0;
return true;
}
void term() {
unacquire();
XFreeCursor(display, invisibleCursor);
XCloseDisplay(display);
}
};
}

View File

@@ -20,15 +20,15 @@
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#include <xinput.h>
#include "joypad/xinput.cpp"
namespace ruby {
static DWORD WINAPI RawInputThreadProc(void*);
static LRESULT CALLBACK RawInputWindowProc(HWND, UINT, WPARAM, LPARAM);
class RawInput {
public:
struct RawInput {
HANDLE mutex;
HWND hwnd;
bool initialized;
@@ -393,107 +393,11 @@ LRESULT CALLBACK RawInputWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM l
return rawinput.window_proc(hwnd, msg, wparam, lparam);
}
class XInput {
public:
HMODULE libxinput;
DWORD WINAPI (*pXInputGetState)(DWORD, XINPUT_STATE*);
struct Gamepad {
unsigned id;
int16_t hat;
int16_t axis[6];
bool button[10];
void poll(XINPUT_STATE &state) {
hat = Joypad::HatCenter;
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ) hat |= Joypad::HatUp;
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) hat |= Joypad::HatRight;
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ) hat |= Joypad::HatDown;
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ) hat |= Joypad::HatLeft;
axis[0] = (int16_t)state.Gamepad.sThumbLX;
axis[1] = (int16_t)state.Gamepad.sThumbLY;
axis[2] = (int16_t)state.Gamepad.sThumbRX;
axis[3] = (int16_t)state.Gamepad.sThumbRY;
//transform left and right trigger ranges:
//from: 0 (low, eg released) to 255 (high, eg pressed all the way down)
//to: +32767 (low) to -32768 (high)
uint16_t triggerX = state.Gamepad.bLeftTrigger;
uint16_t triggerY = state.Gamepad.bRightTrigger;
triggerX = (triggerX << 8) | triggerX;
triggerY = (triggerY << 8) | triggerY;
axis[4] = (~triggerX) - 32768;
axis[5] = (~triggerY) - 32768;
button[0] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_A);
button[1] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_B);
button[2] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_X);
button[3] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_Y);
button[4] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK);
button[5] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_START);
button[6] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER);
button[7] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER);
button[8] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB);
button[9] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB);
}
Gamepad() {
hat = Joypad::HatCenter;
for(unsigned n = 0; n < 6; n++) axis[n] = 0;
for(unsigned n = 0; n < 10; n++) button[n] = false;
}
};
vector<Gamepad> lgamepad;
void poll() {
if(!pXInputGetState) return;
for(unsigned i = 0; i < lgamepad.size(); i++) {
XINPUT_STATE state;
DWORD result = pXInputGetState(lgamepad(i).id, &state);
if(result == ERROR_SUCCESS) lgamepad(i).poll(state);
}
}
void init() {
if(!pXInputGetState) return;
//XInput only supports up to four controllers
for(unsigned i = 0; i <= 3; i++) {
XINPUT_STATE state;
DWORD result = pXInputGetState(i, &state);
if(result == ERROR_SUCCESS) {
//valid controller detected, add to gamepad list
unsigned n = lgamepad.size();
lgamepad(n).id = i;
}
}
}
XInput() : pXInputGetState(0) {
//bind xinput1 dynamically, as it does not ship with Windows Vista or below
libxinput = LoadLibraryA("xinput1_3.dll");
if(!libxinput) libxinput = LoadLibraryA("xinput1_2.dll");
if(!libxinput) libxinput = LoadLibraryA("xinput1_1.dll");
if(!libxinput) return;
pXInputGetState = (DWORD WINAPI (*)(DWORD, XINPUT_STATE*))GetProcAddress(libxinput, "XInputGetState");
}
~XInput() {
if(libxinput) FreeLibrary(libxinput);
}
};
static BOOL CALLBACK DirectInput_EnumJoypadsCallback(const DIDEVICEINSTANCE*, void*);
static BOOL CALLBACK DirectInput_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE*, void*);
static BOOL CALLBACK DirectInput_EnumJoypadFeedbacksCallback(const DIDEVICEOBJECTINSTANCE*, void*);
class DirectInput {
public:
struct DirectInput {
HWND handle;
LPDIRECTINPUT8 context;
struct Gamepad {
@@ -502,6 +406,7 @@ public:
int16_t hat[4];
int16_t axis[6];
bool button[128];
LPDIRECTINPUTEFFECT effect = nullptr;
void poll(DIJOYSTATE2& state) {
//POV hats
@@ -557,7 +462,17 @@ public:
}
}
bool init_joypad(const DIDEVICEINSTANCE* instance) {
void rumble(unsigned id, bool enable) {
if(lgamepad(id).effect == nullptr) return;
if(enable) {
lgamepad(id).effect->Start(1, 0);
} else {
lgamepad(id).effect->Stop();
}
}
bool initJoypad(const DIDEVICEINSTANCE* instance) {
//if this is an XInput device, do not acquire it via DirectInput ...
//the XInput driver above will handle said device.
for(unsigned i = 0; i < rawinput.lgamepad.size(); i++) {
@@ -573,15 +488,53 @@ public:
return DIENUM_CONTINUE;
}
device->SetDataFormat(&c_dfDIJoystick2);
device->SetCooperativeLevel(handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
device->EnumObjects(DirectInput_EnumJoypadAxesCallback, (void*)this, DIDFT_ABSAXIS);
unsigned n = lgamepad.size();
lgamepad(n).handle = device;
device->SetDataFormat(&c_dfDIJoystick2);
device->SetCooperativeLevel(handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
forceFeedbackAxes = 0;
device->EnumObjects(DirectInput_EnumJoypadAxesCallback, (void*)this, DIDFT_ABSAXIS);
device->EnumObjects(DirectInput_EnumJoypadFeedbacksCallback, (void*)this, DIDFT_FFACTUATOR);
if(forceFeedbackAxes == 0) return DIENUM_CONTINUE;
//disable auto-centering spring for rumble support
DIPROPDWORD property;
memset(&property, 0, sizeof(DIPROPDWORD));
property.diph.dwSize = sizeof(DIPROPDWORD);
property.diph.dwHeaderSize = sizeof(DIPROPHEADER);
property.diph.dwObj = 0;
property.diph.dwHow = DIPH_DEVICE;
property.dwData = false;
device->SetProperty(DIPROP_AUTOCENTER, &property.diph);
DWORD dwAxes[2] = {DIJOFS_X, DIJOFS_Y};
LONG lDirection[2] = {0, 0};
DICONSTANTFORCE force;
force.lMagnitude = DI_FFNOMINALMAX; //full force
DIEFFECT effect;
memset(&effect, 0, sizeof(DIEFFECT));
effect.dwSize = sizeof(DIEFFECT);
effect.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
effect.dwDuration = INFINITE;
effect.dwSamplePeriod = 0;
effect.dwGain = DI_FFNOMINALMAX;
effect.dwTriggerButton = DIEB_NOTRIGGER;
effect.dwTriggerRepeatInterval = 0;
effect.cAxes = 2;
effect.rgdwAxes = dwAxes;
effect.rglDirection = lDirection;
effect.lpEnvelope = 0;
effect.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
effect.lpvTypeSpecificParams = &force;
effect.dwStartDelay = 0;
device->CreateEffect(GUID_ConstantForce, &effect, &lgamepad(n).effect, NULL);
return DIENUM_CONTINUE;
}
bool init_axis(const DIDEVICEOBJECTINSTANCE* instance) {
bool initAxis(const DIDEVICEOBJECTINSTANCE* instance) {
DIPROPRANGE range;
range.diph.dwSize = sizeof(DIPROPRANGE);
range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
@@ -593,6 +546,11 @@ public:
return DIENUM_CONTINUE;
}
bool initFeedback(const DIDEVICEOBJECTINSTANCE* instance) {
forceFeedbackAxes++;
return DIENUM_CONTINUE;
}
void init(HWND handle_) {
handle = handle_;
DirectInput8Create(GetModuleHandle(0), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&context, 0);
@@ -602,37 +560,42 @@ public:
void term() {
for(unsigned i = 0; i < lgamepad.size(); i++) {
lgamepad(i).handle->Unacquire();
if(lgamepad(i).effect) lgamepad(i).effect->Release();
lgamepad(i).handle->Release();
}
lgamepad.reset();
if(context) {
context->Release();
context = 0;
context = nullptr;
}
}
private:
LPDIRECTINPUTDEVICE8 device;
unsigned forceFeedbackAxes;
};
BOOL CALLBACK DirectInput_EnumJoypadsCallback(const DIDEVICEINSTANCE* instance, void* p) {
return ((DirectInput*)p)->init_joypad(instance);
return ((DirectInput*)p)->initJoypad(instance);
}
BOOL CALLBACK DirectInput_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, void* p) {
return ((DirectInput*)p)->init_axis(instance);
return ((DirectInput*)p)->initAxis(instance);
}
class pInputRaw {
public:
XInput xinput;
BOOL CALLBACK DirectInput_EnumJoypadFeedbacksCallback(const DIDEVICEOBJECTINSTANCE* instance, void* p) {
return ((DirectInput*)p)->initFeedback(instance);
}
struct pInputRaw {
InputJoypadXInput xinput;
DirectInput dinput;
bool acquire_mouse;
bool cursor_visible;
struct {
struct Settings {
HWND handle;
} settings;
@@ -641,6 +604,7 @@ public:
if(name == Input::KeyboardSupport) return true;
if(name == Input::MouseSupport) return true;
if(name == Input::JoypadSupport) return true;
if(name == Input::JoypadRumbleSupport) return true;
return false;
}
@@ -720,31 +684,15 @@ public:
ReleaseMutex(rawinput.mutex);
unsigned joy = 0;
//==================
//XInput controllers
//==================
xinput.poll();
for(unsigned i = 0; i < xinput.lgamepad.size(); i++) {
if(joy >= Joypad::Count) break;
table[joypad(joy).hat(0)] = xinput.lgamepad(i).hat;
for(unsigned axis = 0; axis < min(6U, (unsigned)Joypad::Axes); axis++) {
table[joypad(joy).axis(axis)] = xinput.lgamepad(i).axis[axis];
}
for(unsigned button = 0; button < min(10U, (unsigned)Joypad::Buttons); button++) {
table[joypad(joy).button(button)] = xinput.lgamepad(i).button[button];
}
joy++;
}
xinput.poll(table);
//=======================
//DirectInput controllers
//=======================
unsigned joy = xinput.joysticks.size();
dinput.poll();
for(unsigned i = 0; i < dinput.lgamepad.size(); i++) {
if(joy >= Joypad::Count) break;
@@ -767,6 +715,15 @@ public:
return true;
}
void rumble(unsigned id, bool enable) {
//id is the nall joypad# to rumble; but we have two lists of joypads
//XInput joypads are enumerated first, and then DirectInput joypads
if(id < xinput.joysticks.size()) return xinput.rumble(id, enable);
id -= xinput.joysticks.size();
if(id < dinput.lgamepad.size()) return dinput.rumble(id, enable);
id -= dinput.lgamepad.size();
}
bool init() {
//only spawn RawInput processing thread one time
if(rawinput.initialized == false) {
@@ -795,6 +752,7 @@ public:
void term() {
unacquire();
xinput.term();
dinput.term();
}

View File

@@ -171,6 +171,9 @@ struct pInputSDL {
return true;
}
void rumble(unsigned id, bool enable) {
}
bool init() {
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
SDL_JoystickEventState(SDL_IGNORE);
@@ -227,4 +230,4 @@ struct pInputSDL {
DeclareInput(SDL)
};
}

86
ruby/input/udev.cpp Normal file
View File

@@ -0,0 +1,86 @@
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <fcntl.h>
#include <libudev.h>
#include <linux/types.h>
#include <linux/input.h>
#include "keyboard/xlib.cpp"
#include "mouse/xlib.cpp"
#include "joypad/udev.cpp"
namespace ruby {
struct pInputUdev {
InputKeyboardXlib xlibKeyboard;
InputMouseXlib xlibMouse;
InputJoypadUdev udev;
struct Settings {
uintptr_t handle = 0;
} settings;
bool cap(const string& name) {
if(name == Input::Handle) return true;
if(name == Input::KeyboardSupport) return true;
if(name == Input::MouseSupport) return true;
if(name == Input::JoypadSupport) return true;
if(name == Input::JoypadRumbleSupport) 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);
udev.poll(table);
}
void rumble(unsigned id, bool enable) {
udev.rumble(id, enable);
}
bool init() {
if(xlibKeyboard.init() == false) return false;
if(xlibMouse.init(settings.handle) == false) return false;
if(udev.init() == false) return false;
return true;
}
void term() {
xlibKeyboard.term();
xlibMouse.term();
udev.term();
}
};
DeclareInput(Udev)
}

View File

@@ -34,6 +34,9 @@ public:
return true;
}
void rumble(unsigned id, bool enable) {
}
bool init() {
display = XOpenDisplay(0);
x_init(display);
@@ -47,4 +50,4 @@ public:
DeclareInput(X)
};
}