mirror of https://github.com/bsnes-emu/bsnes.git
Update to v093r12a release.
byuu says: Not an official WIP (a WIP WIP? A meta-WIP?), just throwing in the new fullscreen code, and I noticed that OpenGL colors in 30-bit mode are all fucked up now for some strange reason. So I'm just using this snapshot to debug the issue.
This commit is contained in:
parent
3ce1d19f7a
commit
2b81b630cb
2
Makefile
2
Makefile
|
@ -39,7 +39,7 @@ ifeq ($(platform),windows)
|
||||||
else
|
else
|
||||||
link += -mwindows
|
link += -mwindows
|
||||||
endif
|
endif
|
||||||
link += -s -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32 -lws2_32
|
link += -s -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32 -lws2_32 -ldxguid
|
||||||
link += -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
|
link += -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
|
||||||
else ifeq ($(platform),macosx)
|
else ifeq ($(platform),macosx)
|
||||||
flags += -march=native
|
flags += -march=native
|
||||||
|
|
|
@ -68,8 +68,8 @@ void APU::Wave::power() {
|
||||||
frequency = 0;
|
frequency = 0;
|
||||||
counter = 0;
|
counter = 0;
|
||||||
|
|
||||||
random_lfsr r;
|
//random_lfsr r;
|
||||||
for(auto& n : pattern) n = r() & 15;
|
for(auto& n : pattern) n = 0;
|
||||||
|
|
||||||
output = 0;
|
output = 0;
|
||||||
length = 0;
|
length = 0;
|
||||||
|
|
|
@ -66,6 +66,14 @@ namespace bit {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//return index of the first bit set (or zero of no bits are set)
|
||||||
|
//first(0b1000) == 3
|
||||||
|
inline unsigned first(uintmax_t x) {
|
||||||
|
unsigned first = 0;
|
||||||
|
while(x) { if(x & 1) break; x >>= 1; first++; }
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
//round up to next highest single bit:
|
//round up to next highest single bit:
|
||||||
//round(15) == 16, round(16) == 16, round(17) == 32
|
//round(15) == 16, round(16) == 16, round(17) == 32
|
||||||
inline uintmax_t round(uintmax_t x) {
|
inline uintmax_t round(uintmax_t x) {
|
||||||
|
|
39
nall/hid.hpp
39
nall/hid.hpp
|
@ -36,10 +36,6 @@ namespace HID {
|
||||||
string name;
|
string name;
|
||||||
vector<Group> group;
|
vector<Group> group;
|
||||||
|
|
||||||
Device() {
|
|
||||||
group.resize(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t pathID() const { return (uint32_t)(id >> 32); }
|
uint32_t pathID() const { return (uint32_t)(id >> 32); }
|
||||||
uint32_t deviceID() const { return (uint32_t)(id >> 0); }
|
uint32_t deviceID() const { return (uint32_t)(id >> 0); }
|
||||||
uint16_t vendorID() const { return (uint16_t)(id >> 16); }
|
uint16_t vendorID() const { return (uint16_t)(id >> 16); }
|
||||||
|
@ -50,6 +46,10 @@ namespace HID {
|
||||||
virtual bool isMouse() const { return false; }
|
virtual bool isMouse() const { return false; }
|
||||||
virtual bool isJoypad() const { return false; }
|
virtual bool isJoypad() const { return false; }
|
||||||
|
|
||||||
|
void append(const string& name) {
|
||||||
|
group.append({name});
|
||||||
|
}
|
||||||
|
|
||||||
optional<unsigned> find(const string& name) {
|
optional<unsigned> find(const string& name) {
|
||||||
for(unsigned id = 0; id < group.size(); id++) {
|
for(unsigned id = 0; id < group.size(); id++) {
|
||||||
if(group[id].name == name) return {true, id};
|
if(group[id].name == name) return {true, id};
|
||||||
|
@ -68,11 +68,12 @@ namespace HID {
|
||||||
|
|
||||||
struct Keyboard : Device {
|
struct Keyboard : Device {
|
||||||
enum GroupID : unsigned { Button };
|
enum GroupID : unsigned { Button };
|
||||||
Group& button = group[0];
|
|
||||||
|
Group& button() { return group[GroupID::Button]; }
|
||||||
|
|
||||||
Keyboard() {
|
Keyboard() {
|
||||||
name = "Keyboard";
|
name = "Keyboard";
|
||||||
button.name = "Button";
|
append("Button");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isKeyboard() const { return true; }
|
bool isKeyboard() const { return true; }
|
||||||
|
@ -80,13 +81,14 @@ namespace HID {
|
||||||
|
|
||||||
struct Mouse : Device {
|
struct Mouse : Device {
|
||||||
enum GroupID : unsigned { Axis, Button };
|
enum GroupID : unsigned { Axis, Button };
|
||||||
Group& axis = group[0];
|
|
||||||
Group& button = group[1];
|
Group& axis() { return group[GroupID::Axis]; }
|
||||||
|
Group& button() { return group[GroupID::Button]; }
|
||||||
|
|
||||||
Mouse() {
|
Mouse() {
|
||||||
name = "Mouse";
|
name = "Mouse";
|
||||||
axis.name = "Axis";
|
append("Axis");
|
||||||
button.name = "Button";
|
append("Button");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isMouse() const { return true; }
|
bool isMouse() const { return true; }
|
||||||
|
@ -94,18 +96,19 @@ namespace HID {
|
||||||
|
|
||||||
struct Joypad : Device {
|
struct Joypad : Device {
|
||||||
enum GroupID : unsigned { Axis, Hat, Trigger, Button };
|
enum GroupID : unsigned { Axis, Hat, Trigger, Button };
|
||||||
Group& axis = group[0];
|
|
||||||
Group& hat = group[1];
|
Group& axis() { return group[GroupID::Axis]; }
|
||||||
Group& trigger = group[2];
|
Group& hat() { return group[GroupID::Hat]; }
|
||||||
Group& button = group[3];
|
Group& trigger() { return group[GroupID::Trigger]; }
|
||||||
|
Group& button() { return group[GroupID::Button]; }
|
||||||
bool rumble = false;
|
bool rumble = false;
|
||||||
|
|
||||||
Joypad() {
|
Joypad() {
|
||||||
name = "Joypad";
|
name = "Joypad";
|
||||||
axis.name = "Axis";
|
append("Axis");
|
||||||
hat.name = "Hat";
|
append("Hat");
|
||||||
trigger.name = "Trigger";
|
append("Trigger");
|
||||||
button.name = "Button";
|
append("Button");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isJoypad() const { return true; }
|
bool isJoypad() const { return true; }
|
||||||
|
|
386
nall/input.hpp
386
nall/input.hpp
|
@ -1,386 +0,0 @@
|
||||||
#ifndef NALL_INPUT_HPP
|
|
||||||
#define NALL_INPUT_HPP
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <nall/stdint.hpp>
|
|
||||||
#include <nall/string.hpp>
|
|
||||||
|
|
||||||
namespace nall {
|
|
||||||
|
|
||||||
struct Keyboard;
|
|
||||||
Keyboard& keyboard(unsigned = 0);
|
|
||||||
|
|
||||||
static const char KeyboardScancodeName[][64] = {
|
|
||||||
"Escape", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
|
|
||||||
"PrintScreen", "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",
|
|
||||||
"NumLock", "CapsLock",
|
|
||||||
"Up", "Down", "Left", "Right",
|
|
||||||
"Tab", "Return", "Spacebar", "Menu",
|
|
||||||
"Shift", "Control", "Alt", "Super",
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Keyboard {
|
|
||||||
const unsigned ID;
|
|
||||||
enum { Base = 1 };
|
|
||||||
enum { Count = 8, Size = 128 };
|
|
||||||
|
|
||||||
enum Scancode {
|
|
||||||
Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
|
|
||||||
PrintScreen, 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,
|
|
||||||
NumLock, CapsLock,
|
|
||||||
Up, Down, Left, Right,
|
|
||||||
Tab, Return, Spacebar, Menu,
|
|
||||||
Shift, Control, Alt, Super,
|
|
||||||
Limit,
|
|
||||||
};
|
|
||||||
|
|
||||||
static signed numberDecode(uint16_t scancode) {
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(keyboard(i).belongsTo(scancode)) return i;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static signed keyDecode(uint16_t scancode) {
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(keyboard(i).isKey(scancode)) return scancode - keyboard(i).key(Escape);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static signed modifierDecode(uint16_t scancode) {
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(keyboard(i).isModifier(scancode)) return scancode - keyboard(i).key(Shift);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isAnyKey(uint16_t scancode) {
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(keyboard(i).isKey(scancode)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isAnyModifier(uint16_t scancode) {
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(keyboard(i).isModifier(scancode)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t decode(const char* name) {
|
|
||||||
string s(name);
|
|
||||||
if(!strbegin(name, "KB")) return 0;
|
|
||||||
s.ltrim("KB");
|
|
||||||
unsigned id = decimal(s);
|
|
||||||
auto pos = strpos(s, "::");
|
|
||||||
if(!pos) return 0;
|
|
||||||
s = substr(s, pos() + 2);
|
|
||||||
for(unsigned i = 0; i < Limit; i++) {
|
|
||||||
if(s == KeyboardScancodeName[i]) return Base + Size * id + i;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
string encode(uint16_t code) const {
|
|
||||||
unsigned index = 0;
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(code >= Base + Size * i && code < Base + Size * (i + 1)) {
|
|
||||||
index = code - (Base + Size * i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {"KB", ID, "::", KeyboardScancodeName[index]};
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
|
|
||||||
uint16_t key(unsigned id) const { return Base + Size * ID + id; }
|
|
||||||
bool isKey(unsigned id) const { return id >= key(Escape) && id <= key(Menu); }
|
|
||||||
bool isModifier(unsigned id) const { return id >= key(Shift) && id <= key(Super); }
|
|
||||||
bool belongsTo(uint16_t scancode) const { return isKey(scancode) || isModifier(scancode); }
|
|
||||||
|
|
||||||
Keyboard(unsigned ID_) : ID(ID_) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline Keyboard& keyboard(unsigned id) {
|
|
||||||
static Keyboard kb0(0), kb1(1), kb2(2), kb3(3), kb4(4), kb5(5), kb6(6), kb7(7);
|
|
||||||
switch(id) { default:
|
|
||||||
case 0: return kb0; case 1: return kb1; case 2: return kb2; case 3: return kb3;
|
|
||||||
case 4: return kb4; case 5: return kb5; case 6: return kb6; case 7: return kb7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char MouseScancodeName[][64] = {
|
|
||||||
"Xaxis", "Yaxis", "Zaxis",
|
|
||||||
"Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7",
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Mouse;
|
|
||||||
Mouse& mouse(unsigned = 0);
|
|
||||||
|
|
||||||
struct Mouse {
|
|
||||||
const unsigned ID;
|
|
||||||
enum { Base = Keyboard::Base + Keyboard::Size * Keyboard::Count };
|
|
||||||
enum { Count = 8, Size = 16 };
|
|
||||||
enum { Axes = 3, Buttons = 8 };
|
|
||||||
|
|
||||||
enum Scancode {
|
|
||||||
Xaxis, Yaxis, Zaxis,
|
|
||||||
Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7,
|
|
||||||
Limit,
|
|
||||||
};
|
|
||||||
|
|
||||||
static signed numberDecode(uint16_t scancode) {
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(mouse(i).belongsTo(scancode)) return i;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static signed axisDecode(uint16_t scancode) {
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(mouse(i).isAxis(scancode)) return scancode - mouse(i).axis(0);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static signed buttonDecode(uint16_t scancode) {
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(mouse(i).isButton(scancode)) return scancode - mouse(i).button(0);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isAnyAxis(uint16_t scancode) {
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(mouse(i).isAxis(scancode)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isAnyButton(uint16_t scancode) {
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(mouse(i).isButton(scancode)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t decode(const char* name) {
|
|
||||||
string s(name);
|
|
||||||
if(!strbegin(name, "MS")) return 0;
|
|
||||||
s.ltrim("MS");
|
|
||||||
unsigned id = decimal(s);
|
|
||||||
auto pos = strpos(s, "::");
|
|
||||||
if(!pos) return 0;
|
|
||||||
s = substr(s, pos() + 2);
|
|
||||||
for(unsigned i = 0; i < Limit; i++) {
|
|
||||||
if(s == MouseScancodeName[i]) return Base + Size * id + i;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
string encode(uint16_t code) const {
|
|
||||||
unsigned index = 0;
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(code >= Base + Size * i && code < Base + Size * (i + 1)) {
|
|
||||||
index = code - (Base + Size * i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {"MS", ID, "::", MouseScancodeName[index]};
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
|
|
||||||
uint16_t axis(unsigned id) const { return Base + Size * ID + Xaxis + id; }
|
|
||||||
uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; }
|
|
||||||
bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(2); }
|
|
||||||
bool isButton(unsigned id) const { return id >= button(0) && id <= button(7); }
|
|
||||||
bool belongsTo(uint16_t scancode) const { return isAxis(scancode) || isButton(scancode); }
|
|
||||||
|
|
||||||
Mouse(unsigned ID_) : ID(ID_) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline Mouse& mouse(unsigned id) {
|
|
||||||
static Mouse ms0(0), ms1(1), ms2(2), ms3(3), ms4(4), ms5(5), ms6(6), ms7(7);
|
|
||||||
switch(id) { default:
|
|
||||||
case 0: return ms0; case 1: return ms1; case 2: return ms2; case 3: return ms3;
|
|
||||||
case 4: return ms4; case 5: return ms5; case 6: return ms6; case 7: return ms7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char JoypadScancodeName[][64] = {
|
|
||||||
"Hat0", "Hat1", "Hat2", "Hat3", "Hat4", "Hat5", "Hat6", "Hat7",
|
|
||||||
"Axis0", "Axis1", "Axis2", "Axis3", "Axis4", "Axis5", "Axis6", "Axis7",
|
|
||||||
"Axis8", "Axis9", "Axis10", "Axis11", "Axis12", "Axis13", "Axis14", "Axis15",
|
|
||||||
"Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7",
|
|
||||||
"Button8", "Button9", "Button10", "Button11", "Button12", "Button13", "Button14", "Button15",
|
|
||||||
"Button16", "Button17", "Button18", "Button19", "Button20", "Button21", "Button22", "Button23",
|
|
||||||
"Button24", "Button25", "Button26", "Button27", "Button28", "Button29", "Button30", "Button31",
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Joypad;
|
|
||||||
Joypad& joypad(unsigned = 0);
|
|
||||||
|
|
||||||
struct Joypad {
|
|
||||||
const unsigned ID;
|
|
||||||
enum { Base = Mouse::Base + Mouse::Size * Mouse::Count };
|
|
||||||
enum { Count = 8, Size = 64 };
|
|
||||||
enum { Hats = 8, Axes = 16, Buttons = 32 };
|
|
||||||
|
|
||||||
enum Scancode {
|
|
||||||
Hat0, Hat1, Hat2, Hat3, Hat4, Hat5, Hat6, Hat7,
|
|
||||||
Axis0, Axis1, Axis2, Axis3, Axis4, Axis5, Axis6, Axis7,
|
|
||||||
Axis8, Axis9, Axis10, Axis11, Axis12, Axis13, Axis14, Axis15,
|
|
||||||
Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7,
|
|
||||||
Button8, Button9, Button10, Button11, Button12, Button13, Button14, Button15,
|
|
||||||
Button16, Button17, Button18, Button19, Button20, Button21, Button22, Button23,
|
|
||||||
Button24, Button25, Button26, Button27, Button28, Button29, Button30, Button31,
|
|
||||||
Limit,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum Hat { HatCenter = 0, HatUp = 1, HatRight = 2, HatDown = 4, HatLeft = 8 };
|
|
||||||
|
|
||||||
static signed numberDecode(uint16_t scancode) {
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(joypad(i).belongsTo(scancode)) return i;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static signed hatDecode(uint16_t scancode) {
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(joypad(i).isHat(scancode)) return scancode - joypad(i).hat(0);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static signed axisDecode(uint16_t scancode) {
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(joypad(i).isAxis(scancode)) return scancode - joypad(i).axis(0);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static signed buttonDecode(uint16_t scancode) {
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(joypad(i).isButton(scancode)) return scancode - joypad(i).button(0);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isAnyHat(uint16_t scancode) {
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(joypad(i).isHat(scancode)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isAnyAxis(uint16_t scancode) {
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(joypad(i).isAxis(scancode)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isAnyButton(uint16_t scancode) {
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(joypad(i).isButton(scancode)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t decode(const char* name) {
|
|
||||||
string s(name);
|
|
||||||
if(!strbegin(name, "JP")) return 0;
|
|
||||||
s.ltrim("JP");
|
|
||||||
unsigned id = decimal(s);
|
|
||||||
auto pos = strpos(s, "::");
|
|
||||||
if(!pos) return 0;
|
|
||||||
s = substr(s, pos() + 2);
|
|
||||||
for(unsigned i = 0; i < Limit; i++) {
|
|
||||||
if(s == JoypadScancodeName[i]) return Base + Size * id + i;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
string encode(uint16_t code) const {
|
|
||||||
unsigned index = 0;
|
|
||||||
for(unsigned i = 0; i < Count; i++) {
|
|
||||||
if(code >= Base + Size * i && code < Base + Size * (i + 1)) {
|
|
||||||
index = code - (Base + Size * i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {"JP", ID, "::", JoypadScancodeName[index]};
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
|
|
||||||
uint16_t hat(unsigned id) const { return Base + Size * ID + Hat0 + id; }
|
|
||||||
uint16_t axis(unsigned id) const { return Base + Size * ID + Axis0 + id; }
|
|
||||||
uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; }
|
|
||||||
bool isHat(unsigned id) const { return id >= hat(0) && id <= hat(7); }
|
|
||||||
bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(15); }
|
|
||||||
bool isButton(unsigned id) const { return id >= button(0) && id <= button(31); }
|
|
||||||
bool belongsTo(uint16_t scancode) const { return isHat(scancode) || isAxis(scancode) || isButton(scancode); }
|
|
||||||
|
|
||||||
Joypad(unsigned ID_) : ID(ID_) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline Joypad& joypad(unsigned id) {
|
|
||||||
static Joypad jp0(0), jp1(1), jp2(2), jp3(3), jp4(4), jp5(5), jp6(6), jp7(7);
|
|
||||||
switch(id) { default:
|
|
||||||
case 0: return jp0; case 1: return jp1; case 2: return jp2; case 3: return jp3;
|
|
||||||
case 4: return jp4; case 5: return jp5; case 6: return jp6; case 7: return jp7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Scancode {
|
|
||||||
enum { None = 0, Limit = Joypad::Base + Joypad::Size * Joypad::Count };
|
|
||||||
|
|
||||||
static uint16_t decode(const char* name) {
|
|
||||||
uint16_t code;
|
|
||||||
code = Keyboard::decode(name);
|
|
||||||
if(code) return code;
|
|
||||||
code = Mouse::decode(name);
|
|
||||||
if(code) return code;
|
|
||||||
code = Joypad::decode(name);
|
|
||||||
if(code) return code;
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
static string encode(uint16_t code) {
|
|
||||||
for(unsigned i = 0; i < Keyboard::Count; i++) {
|
|
||||||
if(keyboard(i).belongsTo(code)) return keyboard(i).encode(code);
|
|
||||||
}
|
|
||||||
for(unsigned i = 0; i < Mouse::Count; i++) {
|
|
||||||
if(mouse(i).belongsTo(code)) return mouse(i).encode(code);
|
|
||||||
}
|
|
||||||
for(unsigned i = 0; i < Joypad::Count; i++) {
|
|
||||||
if(joypad(i).belongsTo(code)) return joypad(i).encode(code);
|
|
||||||
}
|
|
||||||
return "None";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -24,7 +24,7 @@ struct ODBC {
|
||||||
template<typename... Args> inline bool execute(Args&&... args);
|
template<typename... Args> inline bool execute(Args&&... args);
|
||||||
inline void release();
|
inline void release();
|
||||||
inline unsigned rows();
|
inline unsigned rows();
|
||||||
inline optional<lstring> read();
|
inline lstring read();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char* buffer = nullptr;
|
char* buffer = nullptr;
|
||||||
|
@ -128,15 +128,15 @@ unsigned ODBC::rows() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//valid after select
|
//valid after select
|
||||||
optional<lstring> ODBC::read() {
|
lstring ODBC::read() {
|
||||||
if(!sqlStatement) return false;
|
if(!sqlStatement) return {};
|
||||||
|
|
||||||
auto result = SQLFetch(sqlStatement);
|
auto result = SQLFetch(sqlStatement);
|
||||||
if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) return false;
|
if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) return {};
|
||||||
|
|
||||||
SQLSMALLINT sqlColumns = 0;
|
SQLSMALLINT sqlColumns = 0;
|
||||||
result = SQLNumResultCols(sqlStatement, &sqlColumns);
|
result = SQLNumResultCols(sqlStatement, &sqlColumns);
|
||||||
if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) return false;
|
if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) return {};
|
||||||
|
|
||||||
lstring data;
|
lstring data;
|
||||||
for(unsigned column = 0; column < sqlColumns; column++) {
|
for(unsigned column = 0; column < sqlColumns; column++) {
|
||||||
|
@ -145,7 +145,7 @@ optional<lstring> ODBC::read() {
|
||||||
data.append(buffer);
|
data.append(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {true, data};
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ namespace Math {
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <endian.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
@ -42,6 +41,7 @@ namespace Math {
|
||||||
#undef interface
|
#undef interface
|
||||||
#define dllexport __declspec(dllexport)
|
#define dllexport __declspec(dllexport)
|
||||||
#else
|
#else
|
||||||
|
#include <endian.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#define dllexport
|
#define dllexport
|
||||||
|
|
|
@ -1,32 +1,43 @@
|
||||||
#ifndef NALL_RANDOM_HPP
|
#ifndef NALL_RANDOM_HPP
|
||||||
#define NALL_RANDOM_HPP
|
#define NALL_RANDOM_HPP
|
||||||
|
|
||||||
//pseudo-random number generator
|
#include <nall/serializer.hpp>
|
||||||
//very low-quality, but very fast (based on CRC32 polynomial)
|
#include <nall/stdint.hpp>
|
||||||
|
|
||||||
namespace nall {
|
namespace nall {
|
||||||
|
|
||||||
inline unsigned prng() {
|
struct RandomNumberGenerator {
|
||||||
static unsigned n = 0;
|
virtual void seed(uint64_t) = 0;
|
||||||
return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320);
|
virtual uint64_t operator()() = 0;
|
||||||
}
|
virtual void serialize(serializer&) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct random_lfsr {
|
//Galois LFSR using CRC64 polynomials
|
||||||
inline void seed(unsigned seed__) {
|
struct LinearFeedbackShiftRegisterGenerator : RandomNumberGenerator {
|
||||||
seed_ = seed__;
|
void seed(uint64_t seed) {
|
||||||
|
lfsr = seed;
|
||||||
|
for(unsigned n = 0; n < 8; n++) operator()();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline unsigned operator()() {
|
uint64_t operator()() {
|
||||||
return seed_ = (seed_ >> 1) ^ (((seed_ & 1) - 1) & 0xedb88320);
|
return lfsr = (lfsr >> 1) ^ (-(lfsr & 1) & crc64jones);
|
||||||
}
|
}
|
||||||
|
|
||||||
random_lfsr() : seed_(0) {
|
void serialize(serializer& s) {
|
||||||
|
s.integer(lfsr);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned seed_;
|
static const uint64_t crc64ecma = 0x42f0e1eba9ea3693;
|
||||||
|
static const uint64_t crc64jones = 0xad93d23594c935a9;
|
||||||
|
uint64_t lfsr = crc64ecma;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline uint64_t random() {
|
||||||
|
static LinearFeedbackShiftRegisterGenerator lfsr;
|
||||||
|
return lfsr();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
30
nall/udl.hpp
30
nall/udl.hpp
|
@ -1,30 +0,0 @@
|
||||||
#ifndef NALL_UDL_HPP
|
|
||||||
#define NALL_UDL_HPP
|
|
||||||
|
|
||||||
//user-defined literals
|
|
||||||
|
|
||||||
#include <nall/atoi.hpp>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace nall {
|
|
||||||
|
|
||||||
constexpr inline uintmax_t operator"" _b(const char *n) { return binary(n); }
|
|
||||||
|
|
||||||
//convert to bytes
|
|
||||||
constexpr inline uintmax_t operator"" _kb(unsigned long long n) { return 1024 * n; }
|
|
||||||
constexpr inline uintmax_t operator"" _mb(unsigned long long n) { return 1024 * 1024 * n; }
|
|
||||||
constexpr inline uintmax_t operator"" _gb(unsigned long long n) { return 1024 * 1024 * 1024 * n; }
|
|
||||||
|
|
||||||
//convert to bits
|
|
||||||
constexpr inline uintmax_t operator"" _kbit(unsigned long long n) { return 1024 * n / 8; }
|
|
||||||
constexpr inline uintmax_t operator"" _mbit(unsigned long long n) { return 1024 * 1024 * n / 8; }
|
|
||||||
constexpr inline uintmax_t operator"" _gbit(unsigned long long n) { return 1024 * 1024 * 1024 * n / 8; }
|
|
||||||
|
|
||||||
//convert to hz
|
|
||||||
constexpr inline uintmax_t operator"" _khz(long double n) { return n * 1000; }
|
|
||||||
constexpr inline uintmax_t operator"" _mhz(long double n) { return n * 1000000; }
|
|
||||||
constexpr inline uintmax_t operator"" _ghz(long double n) { return n * 1000000000; }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
namespace phoenix {
|
||||||
|
|
||||||
|
unsigned pMonitor::count() {
|
||||||
|
@autoreleasepool {
|
||||||
|
return [[NSScreen screens] count];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Geometry pMonitor::geometry(unsigned monitor) {
|
||||||
|
@autoreleasepool {
|
||||||
|
NSRect rectangle = [[[NSScreen screens] objectAtIndex:monitor] frame];
|
||||||
|
return {rectangle.origin.x, rectangle.origin.y, rectangle.size.width, rectangle.size.height};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned pMonitor::primary() {
|
||||||
|
//on OS X, the primary monitor is always the first monitor
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace phoenix {
|
||||||
|
|
||||||
|
struct pMonitor {
|
||||||
|
static unsigned count();
|
||||||
|
static Geometry geometry(unsigned monitor);
|
||||||
|
static unsigned primary();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "font.cpp"
|
#include "font.cpp"
|
||||||
#include "desktop.cpp"
|
#include "desktop.cpp"
|
||||||
|
#include "monitor.cpp"
|
||||||
#include "keyboard.cpp"
|
#include "keyboard.cpp"
|
||||||
#include "mouse.cpp"
|
#include "mouse.cpp"
|
||||||
#include "browser-window.cpp"
|
#include "browser-window.cpp"
|
||||||
|
|
|
@ -8,6 +8,7 @@ namespace phoenix {
|
||||||
|
|
||||||
#include "font.hpp"
|
#include "font.hpp"
|
||||||
#include "desktop.hpp"
|
#include "desktop.hpp"
|
||||||
|
#include "monitor.hpp"
|
||||||
#include "keyboard.hpp"
|
#include "keyboard.hpp"
|
||||||
#include "mouse.hpp"
|
#include "mouse.hpp"
|
||||||
#include "browser-window.hpp"
|
#include "browser-window.hpp"
|
||||||
|
|
|
@ -140,6 +140,21 @@ Geometry Desktop::workspace() {
|
||||||
return pDesktop::workspace();
|
return pDesktop::workspace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Monitor
|
||||||
|
//=======
|
||||||
|
|
||||||
|
unsigned Monitor::count() {
|
||||||
|
return pMonitor::count();
|
||||||
|
}
|
||||||
|
|
||||||
|
Geometry Monitor::geometry(unsigned monitor) {
|
||||||
|
return pMonitor::geometry(monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned Monitor::primary() {
|
||||||
|
return pMonitor::primary();
|
||||||
|
}
|
||||||
|
|
||||||
//Keyboard
|
//Keyboard
|
||||||
//========
|
//========
|
||||||
|
|
||||||
|
|
|
@ -134,6 +134,12 @@ struct Desktop {
|
||||||
Desktop() = delete;
|
Desktop() = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Monitor {
|
||||||
|
static unsigned count();
|
||||||
|
static Geometry geometry(unsigned monitor);
|
||||||
|
static unsigned primary();
|
||||||
|
};
|
||||||
|
|
||||||
struct Keyboard {
|
struct Keyboard {
|
||||||
#include "keyboard.hpp"
|
#include "keyboard.hpp"
|
||||||
static bool pressed(Scancode scancode);
|
static bool pressed(Scancode scancode);
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace phoenix {
|
||||||
|
|
||||||
|
unsigned pMonitor::count() {
|
||||||
|
return gdk_screen_get_n_monitors(gdk_screen_get_default());
|
||||||
|
}
|
||||||
|
|
||||||
|
Geometry pMonitor::geometry(unsigned monitor) {
|
||||||
|
GdkRectangle rectangle = {0};
|
||||||
|
gdk_screen_get_monitor_geometry(gdk_screen_get_default(), monitor, &rectangle);
|
||||||
|
return {rectangle.x, rectangle.y, rectangle.width, rectangle.height};
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned pMonitor::primary() {
|
||||||
|
return gdk_screen_get_primary_monitor(gdk_screen_get_default());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
#include "settings.cpp"
|
#include "settings.cpp"
|
||||||
|
|
||||||
#include "desktop.cpp"
|
#include "desktop.cpp"
|
||||||
|
#include "monitor.cpp"
|
||||||
#include "keyboard.cpp"
|
#include "keyboard.cpp"
|
||||||
#include "mouse.cpp"
|
#include "mouse.cpp"
|
||||||
#include "browser-window.cpp"
|
#include "browser-window.cpp"
|
||||||
|
|
|
@ -55,6 +55,12 @@ struct pDesktop {
|
||||||
static Geometry workspace();
|
static Geometry workspace();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct pMonitor {
|
||||||
|
static unsigned count();
|
||||||
|
static Geometry geometry(unsigned monitor);
|
||||||
|
static unsigned primary();
|
||||||
|
};
|
||||||
|
|
||||||
struct pKeyboard {
|
struct pKeyboard {
|
||||||
static bool pressed(Keyboard::Scancode scancode);
|
static bool pressed(Keyboard::Scancode scancode);
|
||||||
static vector<bool> state();
|
static vector<bool> state();
|
||||||
|
|
|
@ -187,13 +187,12 @@ bool pWindow::focused() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Geometry pWindow::geometry() {
|
Geometry pWindow::geometry() {
|
||||||
if(window.state.fullScreen == true) return {
|
if(window.state.fullScreen) {
|
||||||
0,
|
int x, y, width, height;
|
||||||
menuHeight(),
|
gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
|
||||||
Desktop::size().width,
|
gtk_window_get_size(GTK_WINDOW(widget), &width, &height);
|
||||||
Desktop::size().height - menuHeight() - statusHeight()
|
return {x, y + menuHeight(), width, height - menuHeight() - statusHeight()};
|
||||||
};
|
}
|
||||||
|
|
||||||
return window.state.geometry;
|
return window.state.geometry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,6 +226,13 @@ void pWindow::setFullScreen(bool fullScreen) {
|
||||||
gtk_window_unfullscreen(GTK_WINDOW(widget));
|
gtk_window_unfullscreen(GTK_WINDOW(widget));
|
||||||
} else {
|
} else {
|
||||||
gtk_window_fullscreen(GTK_WINDOW(widget));
|
gtk_window_fullscreen(GTK_WINDOW(widget));
|
||||||
|
/*unsigned monitor = gdk_screen_get_monitor_at_window(gdk_screen_get_default(), gtk_widget_get_window(widget));
|
||||||
|
GdkRectangle rectangle = {0};
|
||||||
|
gdk_screen_get_monitor_geometry(gdk_screen_get_default(), monitor, &rectangle);
|
||||||
|
gtk_window_set_decorated(GTK_WINDOW(widget), false);
|
||||||
|
gtk_window_move(GTK_WINDOW(widget), rectangle.x, rectangle.y);
|
||||||
|
gtk_window_resize(GTK_WINDOW(widget), rectangle.width, rectangle.height);
|
||||||
|
gtk_widget_set_size_request(formContainer, rectangle.width, rectangle.height);*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
namespace phoenix {
|
||||||
|
|
||||||
|
unsigned pMonitor::count() {
|
||||||
|
return QApplication::desktop()->screenCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
Geometry pMonitor::geometry(unsigned monitor) {
|
||||||
|
QRect rectangle = QApplication::desktop()->screenGeometry(monitor);
|
||||||
|
return {rectangle.x(), rectangle.y(), rectangle.width(), rectangle.height()};
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned pMonitor::primary() {
|
||||||
|
return QApplication::desktop()->primaryScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
#include "settings.cpp"
|
#include "settings.cpp"
|
||||||
|
|
||||||
#include "desktop.cpp"
|
#include "desktop.cpp"
|
||||||
|
#include "monitor.cpp"
|
||||||
#include "keyboard.cpp"
|
#include "keyboard.cpp"
|
||||||
#include "mouse.cpp"
|
#include "mouse.cpp"
|
||||||
#include "browser-window.cpp"
|
#include "browser-window.cpp"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
** Meta object code from reading C++ file 'platform.moc.hpp'
|
** Meta object code from reading C++ file 'platform.moc.hpp'
|
||||||
**
|
**
|
||||||
** Created: Fri Nov 29 09:24:08 2013
|
** Created: Sun Jan 5 03:02:03 2014
|
||||||
** by: The Qt Meta Object Compiler version 63 (Qt 4.8.2)
|
** by: The Qt Meta Object Compiler version 63 (Qt 4.8.2)
|
||||||
**
|
**
|
||||||
** WARNING! All changes made in this file will be lost!
|
** WARNING! All changes made in this file will be lost!
|
||||||
|
|
|
@ -51,6 +51,12 @@ struct pDesktop {
|
||||||
static Geometry workspace();
|
static Geometry workspace();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct pMonitor {
|
||||||
|
static unsigned count();
|
||||||
|
static Geometry geometry(unsigned monitor);
|
||||||
|
static unsigned primary();
|
||||||
|
};
|
||||||
|
|
||||||
struct pKeyboard {
|
struct pKeyboard {
|
||||||
static bool pressed(Keyboard::Scancode scancode);
|
static bool pressed(Keyboard::Scancode scancode);
|
||||||
static vector<bool> state();
|
static vector<bool> state();
|
||||||
|
|
|
@ -45,7 +45,8 @@ Geometry pWindow::geometry() {
|
||||||
if(window.state.fullScreen) {
|
if(window.state.fullScreen) {
|
||||||
unsigned menuHeight = window.state.menuVisible ? qtMenu->height() : 0;
|
unsigned menuHeight = window.state.menuVisible ? qtMenu->height() : 0;
|
||||||
unsigned statusHeight = window.state.statusVisible ? qtStatus->height() : 0;
|
unsigned statusHeight = window.state.statusVisible ? qtStatus->height() : 0;
|
||||||
return {0, menuHeight, Desktop::size().width, Desktop::size().height - menuHeight - statusHeight};
|
QRect geometry = qtWindow->geometry(); //frameGeometry() includes frame even though it's not visible in fullscreen mode
|
||||||
|
return {geometry.x(), geometry.y() + menuHeight, geometry.width(), geometry.height() - menuHeight - statusHeight};
|
||||||
}
|
}
|
||||||
return window.state.geometry;
|
return window.state.geometry;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
namespace phoenix {
|
||||||
|
|
||||||
|
unsigned pMonitor::count() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Geometry pMonitor::geometry(unsigned monitor) {
|
||||||
|
return {0, 0, 0, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned pMonitor::primary() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace phoenix {
|
||||||
|
|
||||||
|
struct pMonitor {
|
||||||
|
static unsigned count();
|
||||||
|
static Geometry geometry(unsigned monitor);
|
||||||
|
static unsigned primary();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "font.cpp"
|
#include "font.cpp"
|
||||||
#include "desktop.cpp"
|
#include "desktop.cpp"
|
||||||
|
#include "monitor.cpp"
|
||||||
#include "keyboard.cpp"
|
#include "keyboard.cpp"
|
||||||
#include "mouse.cpp"
|
#include "mouse.cpp"
|
||||||
#include "browser-window.cpp"
|
#include "browser-window.cpp"
|
||||||
|
|
|
@ -8,6 +8,7 @@ namespace phoenix {
|
||||||
|
|
||||||
#include "font.hpp"
|
#include "font.hpp"
|
||||||
#include "desktop.hpp"
|
#include "desktop.hpp"
|
||||||
|
#include "monitor.hpp"
|
||||||
#include "keyboard.hpp"
|
#include "keyboard.hpp"
|
||||||
#include "mouse.hpp"
|
#include "mouse.hpp"
|
||||||
#include "browser-window.hpp"
|
#include "browser-window.hpp"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
namespace phoenix {
|
namespace phoenix {
|
||||||
|
|
||||||
Size pDesktop::size() {
|
Size pDesktop::size() {
|
||||||
return {GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)};
|
return {GetSystemMetrics(SM_CXVIRTUALSCREEN), GetSystemMetrics(SM_CYVIRTUALSCREEN)};
|
||||||
}
|
}
|
||||||
|
|
||||||
Geometry pDesktop::workspace() {
|
Geometry pDesktop::workspace() {
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
namespace phoenix {
|
||||||
|
|
||||||
|
struct MonitorInfo {
|
||||||
|
unsigned monitor = 0;
|
||||||
|
unsigned primary = 0;
|
||||||
|
unsigned index = 0;
|
||||||
|
Geometry geometry;
|
||||||
|
};
|
||||||
|
|
||||||
|
static BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
|
||||||
|
MonitorInfo& info = *(MonitorInfo*)dwData;
|
||||||
|
MONITORINFOEX mi;
|
||||||
|
memset(&mi, 0, sizeof(MONITORINFOEX));
|
||||||
|
mi.cbSize = sizeof(MONITORINFOEX);
|
||||||
|
GetMonitorInfo(hMonitor, &mi);
|
||||||
|
string displayName = (const char*)utf8_t(mi.szDevice);
|
||||||
|
if(displayName.beginsWith(R"(\\.\DISPLAYV)")) return TRUE; //ignore pseudo-monitors
|
||||||
|
if(mi.dwFlags & MONITORINFOF_PRIMARY) info.primary = info.index;
|
||||||
|
if(info.monitor == info.index) {
|
||||||
|
info.geometry = {lprcMonitor->left, lprcMonitor->top, lprcMonitor->right - lprcMonitor->left, lprcMonitor->bottom - lprcMonitor->top};
|
||||||
|
}
|
||||||
|
info.index++;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned pMonitor::count() {
|
||||||
|
return GetSystemMetrics(SM_CMONITORS);
|
||||||
|
}
|
||||||
|
|
||||||
|
Geometry pMonitor::geometry(unsigned monitor) {
|
||||||
|
MonitorInfo info;
|
||||||
|
info.monitor = monitor;
|
||||||
|
EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&info);
|
||||||
|
return info.geometry;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned pMonitor::primary() {
|
||||||
|
MonitorInfo info;
|
||||||
|
EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&info);
|
||||||
|
return info.primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
#include "settings.cpp"
|
#include "settings.cpp"
|
||||||
|
|
||||||
#include "desktop.cpp"
|
#include "desktop.cpp"
|
||||||
|
#include "monitor.cpp"
|
||||||
#include "keyboard.cpp"
|
#include "keyboard.cpp"
|
||||||
#include "mouse.cpp"
|
#include "mouse.cpp"
|
||||||
#include "browser-window.cpp"
|
#include "browser-window.cpp"
|
||||||
|
|
|
@ -48,6 +48,12 @@ struct pDesktop {
|
||||||
static Geometry workspace();
|
static Geometry workspace();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct pMonitor {
|
||||||
|
static unsigned count();
|
||||||
|
static Geometry geometry(unsigned monitor);
|
||||||
|
static unsigned primary();
|
||||||
|
};
|
||||||
|
|
||||||
struct pKeyboard {
|
struct pKeyboard {
|
||||||
static bool pressed(Keyboard::Scancode scancode);
|
static bool pressed(Keyboard::Scancode scancode);
|
||||||
static vector<bool> state();
|
static vector<bool> state();
|
||||||
|
|
|
@ -141,9 +141,19 @@ void pWindow::setFullScreen(bool fullScreen) {
|
||||||
SetWindowLongPtr(hwnd, GWL_STYLE, WS_VISIBLE | (window.state.resizable ? ResizableStyle : FixedStyle));
|
SetWindowLongPtr(hwnd, GWL_STYLE, WS_VISIBLE | (window.state.resizable ? ResizableStyle : FixedStyle));
|
||||||
setGeometry(window.state.geometry);
|
setGeometry(window.state.geometry);
|
||||||
} else {
|
} else {
|
||||||
|
HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||||
|
MONITORINFOEX info;
|
||||||
|
memset(&info, 0, sizeof(MONITORINFOEX));
|
||||||
|
info.cbSize = sizeof(MONITORINFOEX);
|
||||||
|
GetMonitorInfo(monitor, &info);
|
||||||
|
RECT rc = info.rcMonitor;
|
||||||
|
Geometry geometry = {rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top};
|
||||||
SetWindowLongPtr(hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP);
|
SetWindowLongPtr(hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP);
|
||||||
Geometry margin = frameMargin();
|
Geometry margin = frameMargin();
|
||||||
setGeometry({margin.x, margin.y, GetSystemMetrics(SM_CXSCREEN) - margin.width, GetSystemMetrics(SM_CYSCREEN) - margin.height});
|
setGeometry({
|
||||||
|
geometry.x + margin.x, geometry.y + margin.y,
|
||||||
|
geometry.width - margin.width, geometry.height - margin.height
|
||||||
|
});
|
||||||
}
|
}
|
||||||
locked = false;
|
locked = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,8 @@ rubylink += $(if $(findstring audio.pulseaudio,$(ruby)),-lpulse)
|
||||||
rubylink += $(if $(findstring audio.pulseaudiosimple,$(ruby)),-lpulse-simple)
|
rubylink += $(if $(findstring audio.pulseaudiosimple,$(ruby)),-lpulse-simple)
|
||||||
rubylink += $(if $(findstring audio.xaudio2,$(ruby)),-lole32)
|
rubylink += $(if $(findstring audio.xaudio2,$(ruby)),-lole32)
|
||||||
|
|
||||||
rubylink += $(if $(findstring input.directinput,$(ruby)),-ldinput8 -ldxguid)
|
|
||||||
rubylink += $(if $(findstring input.rawinput,$(ruby)),-ldinput8 -ldxguid)
|
|
||||||
rubylink += $(if $(findstring input.udev,$(ruby)),-ludev)
|
rubylink += $(if $(findstring input.udev,$(ruby)),-ludev)
|
||||||
|
rubylink += $(if $(findstring input.windows,$(ruby)),-ldinput8 -ldxguid)
|
||||||
|
|
||||||
rubylink += $(if $(findstring .sdl,$(ruby)),`sdl-config --libs`)
|
rubylink += $(if $(findstring .sdl,$(ruby)),`sdl-config --libs`)
|
||||||
|
|
||||||
|
|
|
@ -140,8 +140,7 @@ using namespace nall;
|
||||||
bool acquired() { return p.acquired(); } \
|
bool acquired() { return p.acquired(); } \
|
||||||
\
|
\
|
||||||
vector<HID::Device*> poll() { return p.poll(); } \
|
vector<HID::Device*> poll() { return p.poll(); } \
|
||||||
bool poll(int16_t* table) { return p.poll(table); } \
|
bool rumble(uint64_t 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(); } \
|
bool init() { return p.init(); } \
|
||||||
void term() { p.term(); } \
|
void term() { p.term(); } \
|
||||||
\
|
\
|
||||||
|
@ -156,14 +155,6 @@ using namespace nall;
|
||||||
#include <ruby/input/carbon.cpp>
|
#include <ruby/input/carbon.cpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef INPUT_DIRECTINPUT
|
|
||||||
#include <ruby/input/directinput.cpp>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef INPUT_RAWINPUT
|
|
||||||
#include <ruby/input/rawinput.cpp>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef INPUT_SDL
|
#ifdef INPUT_SDL
|
||||||
#include <ruby/input/sdl.cpp>
|
#include <ruby/input/sdl.cpp>
|
||||||
#endif
|
#endif
|
||||||
|
@ -172,6 +163,10 @@ using namespace nall;
|
||||||
#include <ruby/input/udev.cpp>
|
#include <ruby/input/udev.cpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef INPUT_WINDOWS
|
||||||
|
#include <ruby/input/windows.cpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef INPUT_XLIB
|
#ifdef INPUT_XLIB
|
||||||
#include <ruby/input/xlib.cpp>
|
#include <ruby/input/xlib.cpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -14,8 +14,7 @@ struct Input {
|
||||||
virtual bool acquired() { return false; }
|
virtual bool acquired() { return false; }
|
||||||
|
|
||||||
virtual nall::vector<nall::HID::Device*> poll() { return {}; }
|
virtual nall::vector<nall::HID::Device*> poll() { return {}; }
|
||||||
virtual bool poll(int16_t* table) { return false; }
|
virtual bool rumble(uint64_t id, bool enable) {}
|
||||||
virtual void rumble(uint64_t id, bool enable) {}
|
|
||||||
virtual bool init() { return true; }
|
virtual bool init() { return true; }
|
||||||
virtual void term() {}
|
virtual void term() {}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,18 @@
|
||||||
namespace ruby {
|
namespace ruby {
|
||||||
|
|
||||||
struct pInputCarbon {
|
struct pInputCarbon {
|
||||||
|
struct Key {
|
||||||
|
uint8_t id;
|
||||||
|
string name;
|
||||||
|
};
|
||||||
|
vector<Key> keys;
|
||||||
|
|
||||||
|
struct Keyboard {
|
||||||
|
HID::Keyboard hid;
|
||||||
|
} kb;
|
||||||
|
|
||||||
bool cap(const string& name) {
|
bool cap(const string& name) {
|
||||||
|
if(name == Input::KeyboardSupport) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,136 +28,148 @@ struct pInputCarbon {
|
||||||
bool unacquire() { return false; }
|
bool unacquire() { return false; }
|
||||||
bool acquired() { return false; }
|
bool acquired() { return false; }
|
||||||
|
|
||||||
bool poll(int16_t* table) {
|
void assign(HID::Device& hid, unsigned groupID, unsigned inputID, int16_t value) {
|
||||||
memset(table, 0, Scancode::Limit * sizeof(int16_t));
|
auto& group = hid.group[groupID];
|
||||||
|
if(group.input[inputID].value == value) return;
|
||||||
KeyMap keys;
|
if(input.onChange) input.onChange(hid, groupID, inputID, group.input[inputID].value, value);
|
||||||
GetKeys(keys);
|
group.input[inputID].value = value;
|
||||||
uint8_t* keymap = (uint8_t*)keys;
|
|
||||||
|
|
||||||
#define map(id, name) table[keyboard(0)[name]] = (bool)(keymap[id >> 3] & (1 << (id & 7)))
|
|
||||||
map(0x35, Keyboard::Escape);
|
|
||||||
|
|
||||||
map(0x7a, Keyboard::F1);
|
|
||||||
map(0x78, Keyboard::F2);
|
|
||||||
map(0x63, Keyboard::F3);
|
|
||||||
map(0x76, Keyboard::F4);
|
|
||||||
map(0x60, Keyboard::F5);
|
|
||||||
map(0x61, Keyboard::F6);
|
|
||||||
map(0x62, Keyboard::F7);
|
|
||||||
map(0x64, Keyboard::F8);
|
|
||||||
map(0x65, Keyboard::F9);
|
|
||||||
map(0x6d, Keyboard::F10);
|
|
||||||
map(0x67, Keyboard::F11);
|
|
||||||
//map(0x??, Keyboard::F12);
|
|
||||||
|
|
||||||
map(0x69, Keyboard::PrintScreen);
|
|
||||||
//map(0x??, Keyboard::ScrollLock);
|
|
||||||
map(0x71, Keyboard::Pause);
|
|
||||||
|
|
||||||
map(0x32, Keyboard::Tilde);
|
|
||||||
map(0x12, Keyboard::Num1);
|
|
||||||
map(0x13, Keyboard::Num2);
|
|
||||||
map(0x14, Keyboard::Num3);
|
|
||||||
map(0x15, Keyboard::Num4);
|
|
||||||
map(0x17, Keyboard::Num5);
|
|
||||||
map(0x16, Keyboard::Num6);
|
|
||||||
map(0x1a, Keyboard::Num7);
|
|
||||||
map(0x1c, Keyboard::Num8);
|
|
||||||
map(0x19, Keyboard::Num9);
|
|
||||||
map(0x1d, Keyboard::Num0);
|
|
||||||
|
|
||||||
map(0x1b, Keyboard::Dash);
|
|
||||||
map(0x18, Keyboard::Equal);
|
|
||||||
map(0x33, Keyboard::Backspace);
|
|
||||||
|
|
||||||
map(0x72, Keyboard::Insert);
|
|
||||||
map(0x75, Keyboard::Delete);
|
|
||||||
map(0x73, Keyboard::Home);
|
|
||||||
map(0x77, Keyboard::End);
|
|
||||||
map(0x74, Keyboard::PageUp);
|
|
||||||
map(0x79, Keyboard::PageDown);
|
|
||||||
|
|
||||||
map(0x00, Keyboard::A);
|
|
||||||
map(0x0b, Keyboard::B);
|
|
||||||
map(0x08, Keyboard::C);
|
|
||||||
map(0x02, Keyboard::D);
|
|
||||||
map(0x0e, Keyboard::E);
|
|
||||||
map(0x03, Keyboard::F);
|
|
||||||
map(0x05, Keyboard::G);
|
|
||||||
map(0x04, Keyboard::H);
|
|
||||||
map(0x22, Keyboard::I);
|
|
||||||
map(0x26, Keyboard::J);
|
|
||||||
map(0x28, Keyboard::K);
|
|
||||||
map(0x25, Keyboard::L);
|
|
||||||
map(0x2e, Keyboard::M);
|
|
||||||
map(0x2d, Keyboard::N);
|
|
||||||
map(0x1f, Keyboard::O);
|
|
||||||
map(0x23, Keyboard::P);
|
|
||||||
map(0x0c, Keyboard::Q);
|
|
||||||
map(0x0f, Keyboard::R);
|
|
||||||
map(0x01, Keyboard::S);
|
|
||||||
map(0x11, Keyboard::T);
|
|
||||||
map(0x20, Keyboard::U);
|
|
||||||
map(0x09, Keyboard::V);
|
|
||||||
map(0x0d, Keyboard::W);
|
|
||||||
map(0x07, Keyboard::X);
|
|
||||||
map(0x10, Keyboard::Y);
|
|
||||||
map(0x06, Keyboard::Z);
|
|
||||||
|
|
||||||
map(0x21, Keyboard::LeftBracket);
|
|
||||||
map(0x1e, Keyboard::RightBracket);
|
|
||||||
map(0x2a, Keyboard::Backslash);
|
|
||||||
map(0x29, Keyboard::Semicolon);
|
|
||||||
map(0x27, Keyboard::Apostrophe);
|
|
||||||
map(0x2b, Keyboard::Comma);
|
|
||||||
map(0x2f, Keyboard::Period);
|
|
||||||
map(0x2c, Keyboard::Slash);
|
|
||||||
|
|
||||||
map(0x53, Keyboard::Keypad1);
|
|
||||||
map(0x54, Keyboard::Keypad2);
|
|
||||||
map(0x55, Keyboard::Keypad3);
|
|
||||||
map(0x56, Keyboard::Keypad4);
|
|
||||||
map(0x57, Keyboard::Keypad5);
|
|
||||||
map(0x58, Keyboard::Keypad6);
|
|
||||||
map(0x59, Keyboard::Keypad7);
|
|
||||||
map(0x5b, Keyboard::Keypad8);
|
|
||||||
map(0x5c, Keyboard::Keypad9);
|
|
||||||
map(0x52, Keyboard::Keypad0);
|
|
||||||
|
|
||||||
//map(0x??, Keyboard::Point);
|
|
||||||
map(0x4c, Keyboard::Enter);
|
|
||||||
map(0x45, Keyboard::Add);
|
|
||||||
map(0x4e, Keyboard::Subtract);
|
|
||||||
map(0x43, Keyboard::Multiply);
|
|
||||||
map(0x4b, Keyboard::Divide);
|
|
||||||
|
|
||||||
map(0x47, Keyboard::NumLock);
|
|
||||||
//map(0x39, Keyboard::CapsLock);
|
|
||||||
|
|
||||||
map(0x7e, Keyboard::Up);
|
|
||||||
map(0x7d, Keyboard::Down);
|
|
||||||
map(0x7b, Keyboard::Left);
|
|
||||||
map(0x7c, Keyboard::Right);
|
|
||||||
|
|
||||||
map(0x30, Keyboard::Tab);
|
|
||||||
map(0x24, Keyboard::Return);
|
|
||||||
map(0x31, Keyboard::Spacebar);
|
|
||||||
//map(0x??, Keyboard::Menu);
|
|
||||||
|
|
||||||
map(0x38, Keyboard::Shift);
|
|
||||||
map(0x3b, Keyboard::Control);
|
|
||||||
map(0x3a, Keyboard::Alt);
|
|
||||||
map(0x37, Keyboard::Super);
|
|
||||||
#undef map
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void rumble(unsigned id, bool enable) {
|
void poll(vector<HID::Device*>& devices) {
|
||||||
|
KeyMap keymap;
|
||||||
|
GetKeys(keymap);
|
||||||
|
uint8_t* buffer = (uint8_t*)keymap;
|
||||||
|
|
||||||
|
unsigned inputID = 0;
|
||||||
|
for(auto& key : keys) {
|
||||||
|
bool value = buffer[key.id >> 3] & (1 << (key.id & 7)));
|
||||||
|
assign(kb.hid, HID::Keyboard::GroupID::Button, inputID++, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
devices.append(&kb.hid);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rumble(uint64_t id, bool enable) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool init() {
|
bool init() {
|
||||||
|
keys.append({0x35, "Escape"});
|
||||||
|
keys.append({0x7a, "F1"});
|
||||||
|
keys.append({0x78, "F2"});
|
||||||
|
keys.append({0x63, "F3"});
|
||||||
|
keys.append({0x76, "F4"});
|
||||||
|
keys.append({0x60, "F5"});
|
||||||
|
keys.append({0x61, "F6"});
|
||||||
|
keys.append({0x62, "F7"});
|
||||||
|
keys.append({0x64, "F8"});
|
||||||
|
keys.append({0x65, "F9"});
|
||||||
|
keys.append({0x6d, "F10"});
|
||||||
|
keys.append({0x67, "F11"});
|
||||||
|
//keys.append({0x??, "F12"});
|
||||||
|
|
||||||
|
keys.append({0x69, "PrintScreen"});
|
||||||
|
//keys.append({0x??, "ScrollLock"});
|
||||||
|
keys.append({0x71, "Pause"});
|
||||||
|
|
||||||
|
keys.append({0x32, "Tilde"});
|
||||||
|
keys.append({0x12, "Num1"});
|
||||||
|
keys.append({0x13, "Num2"});
|
||||||
|
keys.append({0x14, "Num3"});
|
||||||
|
keys.append({0x15, "Num4"});
|
||||||
|
keys.append({0x17, "Num5"});
|
||||||
|
keys.append({0x16, "Num6"});
|
||||||
|
keys.append({0x1a, "Num7"});
|
||||||
|
keys.append({0x1c, "Num8"});
|
||||||
|
keys.append({0x19, "Num9"});
|
||||||
|
keys.append({0x1d, "Num0"});
|
||||||
|
|
||||||
|
keys.append({0x1b, "Dash"});
|
||||||
|
keys.append({0x18, "Equal"});
|
||||||
|
keys.append({0x33, "Backspace"});
|
||||||
|
|
||||||
|
keys.append({0x72, "Insert"});
|
||||||
|
keys.append({0x75, "Delete"});
|
||||||
|
keys.append({0x73, "Home"});
|
||||||
|
keys.append({0x77, "End"});
|
||||||
|
keys.append({0x74, "PageUp"});
|
||||||
|
keys.append({0x79, "PageDown"});
|
||||||
|
|
||||||
|
keys.append({0x00, "A"});
|
||||||
|
keys.append({0x0b, "B"});
|
||||||
|
keys.append({0x08, "C"});
|
||||||
|
keys.append({0x02, "D"});
|
||||||
|
keys.append({0x0e, "E"});
|
||||||
|
keys.append({0x03, "F"});
|
||||||
|
keys.append({0x05, "G"});
|
||||||
|
keys.append({0x04, "H"});
|
||||||
|
keys.append({0x22, "I"});
|
||||||
|
keys.append({0x26, "J"});
|
||||||
|
keys.append({0x28, "K"});
|
||||||
|
keys.append({0x25, "L"});
|
||||||
|
keys.append({0x2e, "M"});
|
||||||
|
keys.append({0x2d, "N"});
|
||||||
|
keys.append({0x1f, "O"});
|
||||||
|
keys.append({0x23, "P"});
|
||||||
|
keys.append({0x0c, "Q"});
|
||||||
|
keys.append({0x0f, "R"});
|
||||||
|
keys.append({0x01, "S"});
|
||||||
|
keys.append({0x11, "T"});
|
||||||
|
keys.append({0x20, "U"});
|
||||||
|
keys.append({0x09, "V"});
|
||||||
|
keys.append({0x0d, "W"});
|
||||||
|
keys.append({0x07, "X"});
|
||||||
|
keys.append({0x10, "Y"});
|
||||||
|
keys.append({0x06, "Z"});
|
||||||
|
|
||||||
|
keys.append({0x21, "LeftBracket"});
|
||||||
|
keys.append({0x1e, "RightBracket"});
|
||||||
|
keys.append({0x2a, "Backslash"});
|
||||||
|
keys.append({0x29, "Semicolon"});
|
||||||
|
keys.append({0x27, "Apostrophe"});
|
||||||
|
keys.append({0x2b, "Comma"});
|
||||||
|
keys.append({0x2f, "Period"});
|
||||||
|
keys.append({0x2c, "Slash"});
|
||||||
|
|
||||||
|
keys.append({0x53, "Keypad1"});
|
||||||
|
keys.append({0x54, "Keypad2"});
|
||||||
|
keys.append({0x55, "Keypad3"});
|
||||||
|
keys.append({0x56, "Keypad4"});
|
||||||
|
keys.append({0x57, "Keypad5"});
|
||||||
|
keys.append({0x58, "Keypad6"});
|
||||||
|
keys.append({0x59, "Keypad7"});
|
||||||
|
keys.append({0x5b, "Keypad8"});
|
||||||
|
keys.append({0x5c, "Keypad9"});
|
||||||
|
keys.append({0x52, "Keypad0"});
|
||||||
|
|
||||||
|
//keys.append({0x??, "Point"});
|
||||||
|
keys.append({0x4c, "Enter"});
|
||||||
|
keys.append({0x45, "Add"});
|
||||||
|
keys.append({0x4e, "Subtract"});
|
||||||
|
keys.append({0x43, "Multiply"});
|
||||||
|
keys.append({0x4b, "Divide"});
|
||||||
|
|
||||||
|
keys.append({0x47, "NumLock"});
|
||||||
|
//keys.append({0x39, "CapsLock"});
|
||||||
|
|
||||||
|
keys.append({0x7e, "Up"});
|
||||||
|
keys.append({0x7d, "Down"});
|
||||||
|
keys.append({0x7b, "Left"});
|
||||||
|
keys.append({0x7c, "Right"});
|
||||||
|
|
||||||
|
keys.append({0x30, "Tab"});
|
||||||
|
keys.append({0x24, "Return"});
|
||||||
|
keys.append({0x31, "Spacebar"});
|
||||||
|
//keys.append({0x??, "Menu"});
|
||||||
|
|
||||||
|
keys.append({0x38, "Shift"});
|
||||||
|
keys.append({0x3b, "Control"});
|
||||||
|
keys.append({0x3a, "Alt"});
|
||||||
|
keys.append({0x37, "Super"});
|
||||||
|
|
||||||
|
kb.hid.id = 1;
|
||||||
|
for(auto& key : keys) kb.hid.button().append({key.name});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,390 +0,0 @@
|
||||||
#define DIRECTINPUT_VERSION 0x0800
|
|
||||||
#include <dinput.h>
|
|
||||||
|
|
||||||
namespace ruby {
|
|
||||||
|
|
||||||
static BOOL CALLBACK DI_EnumJoypadsCallback(const DIDEVICEINSTANCE*, void*);
|
|
||||||
static BOOL CALLBACK DI_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE*, void*);
|
|
||||||
|
|
||||||
using namespace nall;
|
|
||||||
|
|
||||||
class pInputDI {
|
|
||||||
public:
|
|
||||||
struct {
|
|
||||||
LPDIRECTINPUT8 context;
|
|
||||||
LPDIRECTINPUTDEVICE8 keyboard;
|
|
||||||
LPDIRECTINPUTDEVICE8 mouse;
|
|
||||||
LPDIRECTINPUTDEVICE8 gamepad[Joypad::Count];
|
|
||||||
bool mouseacquired;
|
|
||||||
} device;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
HWND handle;
|
|
||||||
} 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;
|
|
||||||
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 = (HWND)any_cast<uintptr_t>(value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool poll(int16_t* table) {
|
|
||||||
memset(table, 0, Scancode::Limit * sizeof(int16_t));
|
|
||||||
|
|
||||||
//========
|
|
||||||
//Keyboard
|
|
||||||
//========
|
|
||||||
|
|
||||||
if(device.keyboard) {
|
|
||||||
uint8_t state[256];
|
|
||||||
if(FAILED(device.keyboard->GetDeviceState(sizeof state, state))) {
|
|
||||||
device.keyboard->Acquire();
|
|
||||||
if(FAILED(device.keyboard->GetDeviceState(sizeof state, state))) {
|
|
||||||
memset(state, 0, sizeof state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define key(id) table[keyboard(0)[id]]
|
|
||||||
|
|
||||||
key(Keyboard::Escape) = (bool)(state[0x01] & 0x80);
|
|
||||||
key(Keyboard::F1 ) = (bool)(state[0x3b] & 0x80);
|
|
||||||
key(Keyboard::F2 ) = (bool)(state[0x3c] & 0x80);
|
|
||||||
key(Keyboard::F3 ) = (bool)(state[0x3d] & 0x80);
|
|
||||||
key(Keyboard::F4 ) = (bool)(state[0x3e] & 0x80);
|
|
||||||
key(Keyboard::F5 ) = (bool)(state[0x3f] & 0x80);
|
|
||||||
key(Keyboard::F6 ) = (bool)(state[0x40] & 0x80);
|
|
||||||
key(Keyboard::F7 ) = (bool)(state[0x41] & 0x80);
|
|
||||||
key(Keyboard::F8 ) = (bool)(state[0x42] & 0x80);
|
|
||||||
key(Keyboard::F9 ) = (bool)(state[0x43] & 0x80);
|
|
||||||
key(Keyboard::F10 ) = (bool)(state[0x44] & 0x80);
|
|
||||||
key(Keyboard::F11 ) = (bool)(state[0x57] & 0x80);
|
|
||||||
key(Keyboard::F12 ) = (bool)(state[0x58] & 0x80);
|
|
||||||
|
|
||||||
key(Keyboard::PrintScreen) = (bool)(state[0xb7] & 0x80);
|
|
||||||
key(Keyboard::ScrollLock ) = (bool)(state[0x46] & 0x80);
|
|
||||||
key(Keyboard::Pause ) = (bool)(state[0xc5] & 0x80);
|
|
||||||
key(Keyboard::Tilde ) = (bool)(state[0x29] & 0x80);
|
|
||||||
|
|
||||||
key(Keyboard::Num1) = (bool)(state[0x02] & 0x80);
|
|
||||||
key(Keyboard::Num2) = (bool)(state[0x03] & 0x80);
|
|
||||||
key(Keyboard::Num3) = (bool)(state[0x04] & 0x80);
|
|
||||||
key(Keyboard::Num4) = (bool)(state[0x05] & 0x80);
|
|
||||||
key(Keyboard::Num5) = (bool)(state[0x06] & 0x80);
|
|
||||||
key(Keyboard::Num6) = (bool)(state[0x07] & 0x80);
|
|
||||||
key(Keyboard::Num7) = (bool)(state[0x08] & 0x80);
|
|
||||||
key(Keyboard::Num8) = (bool)(state[0x09] & 0x80);
|
|
||||||
key(Keyboard::Num9) = (bool)(state[0x0a] & 0x80);
|
|
||||||
key(Keyboard::Num0) = (bool)(state[0x0b] & 0x80);
|
|
||||||
|
|
||||||
key(Keyboard::Dash ) = (bool)(state[0x0c] & 0x80);
|
|
||||||
key(Keyboard::Equal ) = (bool)(state[0x0d] & 0x80);
|
|
||||||
key(Keyboard::Backspace) = (bool)(state[0x0e] & 0x80);
|
|
||||||
|
|
||||||
key(Keyboard::Insert ) = (bool)(state[0xd2] & 0x80);
|
|
||||||
key(Keyboard::Delete ) = (bool)(state[0xd3] & 0x80);
|
|
||||||
key(Keyboard::Home ) = (bool)(state[0xc7] & 0x80);
|
|
||||||
key(Keyboard::End ) = (bool)(state[0xcf] & 0x80);
|
|
||||||
key(Keyboard::PageUp ) = (bool)(state[0xc9] & 0x80);
|
|
||||||
key(Keyboard::PageDown) = (bool)(state[0xd1] & 0x80);
|
|
||||||
|
|
||||||
key(Keyboard::A) = (bool)(state[0x1e] & 0x80);
|
|
||||||
key(Keyboard::B) = (bool)(state[0x30] & 0x80);
|
|
||||||
key(Keyboard::C) = (bool)(state[0x2e] & 0x80);
|
|
||||||
key(Keyboard::D) = (bool)(state[0x20] & 0x80);
|
|
||||||
key(Keyboard::E) = (bool)(state[0x12] & 0x80);
|
|
||||||
key(Keyboard::F) = (bool)(state[0x21] & 0x80);
|
|
||||||
key(Keyboard::G) = (bool)(state[0x22] & 0x80);
|
|
||||||
key(Keyboard::H) = (bool)(state[0x23] & 0x80);
|
|
||||||
key(Keyboard::I) = (bool)(state[0x17] & 0x80);
|
|
||||||
key(Keyboard::J) = (bool)(state[0x24] & 0x80);
|
|
||||||
key(Keyboard::K) = (bool)(state[0x25] & 0x80);
|
|
||||||
key(Keyboard::L) = (bool)(state[0x26] & 0x80);
|
|
||||||
key(Keyboard::M) = (bool)(state[0x32] & 0x80);
|
|
||||||
key(Keyboard::N) = (bool)(state[0x31] & 0x80);
|
|
||||||
key(Keyboard::O) = (bool)(state[0x18] & 0x80);
|
|
||||||
key(Keyboard::P) = (bool)(state[0x19] & 0x80);
|
|
||||||
key(Keyboard::Q) = (bool)(state[0x10] & 0x80);
|
|
||||||
key(Keyboard::R) = (bool)(state[0x13] & 0x80);
|
|
||||||
key(Keyboard::S) = (bool)(state[0x1f] & 0x80);
|
|
||||||
key(Keyboard::T) = (bool)(state[0x14] & 0x80);
|
|
||||||
key(Keyboard::U) = (bool)(state[0x16] & 0x80);
|
|
||||||
key(Keyboard::V) = (bool)(state[0x2f] & 0x80);
|
|
||||||
key(Keyboard::W) = (bool)(state[0x11] & 0x80);
|
|
||||||
key(Keyboard::X) = (bool)(state[0x2d] & 0x80);
|
|
||||||
key(Keyboard::Y) = (bool)(state[0x15] & 0x80);
|
|
||||||
key(Keyboard::Z) = (bool)(state[0x2c] & 0x80);
|
|
||||||
|
|
||||||
key(Keyboard::LeftBracket ) = (bool)(state[0x1a] & 0x80);
|
|
||||||
key(Keyboard::RightBracket) = (bool)(state[0x1b] & 0x80);
|
|
||||||
key(Keyboard::Backslash ) = (bool)(state[0x2b] & 0x80);
|
|
||||||
key(Keyboard::Semicolon ) = (bool)(state[0x27] & 0x80);
|
|
||||||
key(Keyboard::Apostrophe ) = (bool)(state[0x28] & 0x80);
|
|
||||||
key(Keyboard::Comma ) = (bool)(state[0x33] & 0x80);
|
|
||||||
key(Keyboard::Period ) = (bool)(state[0x34] & 0x80);
|
|
||||||
key(Keyboard::Slash ) = (bool)(state[0x35] & 0x80);
|
|
||||||
|
|
||||||
key(Keyboard::Keypad1) = (bool)(state[0x4f] & 0x80);
|
|
||||||
key(Keyboard::Keypad2) = (bool)(state[0x50] & 0x80);
|
|
||||||
key(Keyboard::Keypad3) = (bool)(state[0x51] & 0x80);
|
|
||||||
key(Keyboard::Keypad4) = (bool)(state[0x4b] & 0x80);
|
|
||||||
key(Keyboard::Keypad5) = (bool)(state[0x4c] & 0x80);
|
|
||||||
key(Keyboard::Keypad6) = (bool)(state[0x4d] & 0x80);
|
|
||||||
key(Keyboard::Keypad7) = (bool)(state[0x47] & 0x80);
|
|
||||||
key(Keyboard::Keypad8) = (bool)(state[0x48] & 0x80);
|
|
||||||
key(Keyboard::Keypad9) = (bool)(state[0x49] & 0x80);
|
|
||||||
key(Keyboard::Keypad0) = (bool)(state[0x52] & 0x80);
|
|
||||||
key(Keyboard::Point ) = (bool)(state[0x53] & 0x80);
|
|
||||||
|
|
||||||
key(Keyboard::Add ) = (bool)(state[0x4e] & 0x80);
|
|
||||||
key(Keyboard::Subtract) = (bool)(state[0x4a] & 0x80);
|
|
||||||
key(Keyboard::Multiply) = (bool)(state[0x37] & 0x80);
|
|
||||||
key(Keyboard::Divide ) = (bool)(state[0xb5] & 0x80);
|
|
||||||
key(Keyboard::Enter ) = (bool)(state[0x9c] & 0x80);
|
|
||||||
|
|
||||||
key(Keyboard::NumLock ) = (bool)(state[0x45] & 0x80);
|
|
||||||
key(Keyboard::CapsLock) = (bool)(state[0x3a] & 0x80);
|
|
||||||
|
|
||||||
key(Keyboard::Up ) = (bool)(state[0xc8] & 0x80);
|
|
||||||
key(Keyboard::Down ) = (bool)(state[0xd0] & 0x80);
|
|
||||||
key(Keyboard::Left ) = (bool)(state[0xcb] & 0x80);
|
|
||||||
key(Keyboard::Right) = (bool)(state[0xcd] & 0x80);
|
|
||||||
|
|
||||||
key(Keyboard::Tab ) = (bool)(state[0x0f] & 0x80);
|
|
||||||
key(Keyboard::Return ) = (bool)(state[0x1c] & 0x80);
|
|
||||||
key(Keyboard::Spacebar) = (bool)(state[0x39] & 0x80);
|
|
||||||
key(Keyboard::Menu ) = (bool)(state[0xdd] & 0x80);
|
|
||||||
|
|
||||||
key(Keyboard::Shift ) = (bool)(state[0x2a] & 0x80) || (bool)(state[0x36] & 0x80);
|
|
||||||
key(Keyboard::Control) = (bool)(state[0x1d] & 0x80) || (bool)(state[0x9d] & 0x80);
|
|
||||||
key(Keyboard::Alt ) = (bool)(state[0x38] & 0x80) || (bool)(state[0xb8] & 0x80);
|
|
||||||
key(Keyboard::Super ) = (bool)(state[0xdb] & 0x80) || (bool)(state[0xdc] & 0x80);
|
|
||||||
|
|
||||||
#undef key
|
|
||||||
}
|
|
||||||
|
|
||||||
//=====
|
|
||||||
//Mouse
|
|
||||||
//=====
|
|
||||||
|
|
||||||
if(device.mouse) {
|
|
||||||
DIMOUSESTATE2 state;
|
|
||||||
if(FAILED(device.mouse->GetDeviceState(sizeof(DIMOUSESTATE2), (void*)&state))) {
|
|
||||||
device.mouse->Acquire();
|
|
||||||
if(FAILED(device.mouse->GetDeviceState(sizeof(DIMOUSESTATE2), (void*)&state))) {
|
|
||||||
memset(&state, 0, sizeof(DIMOUSESTATE2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table[mouse(0).axis(0)] = state.lX;
|
|
||||||
table[mouse(0).axis(1)] = state.lY;
|
|
||||||
table[mouse(0).axis(2)] = state.lZ / WHEEL_DELTA;
|
|
||||||
for(unsigned n = 0; n < Mouse::Buttons; n++) {
|
|
||||||
table[mouse(0).button(n)] = (bool)state.rgbButtons[n];
|
|
||||||
}
|
|
||||||
|
|
||||||
//on Windows, 0 = left, 1 = right, 2 = middle
|
|
||||||
//swap middle and right buttons for consistency with Linux
|
|
||||||
int16_t temp = table[mouse(0).button(1)];
|
|
||||||
table[mouse(0).button(1)] = table[mouse(0).button(2)];
|
|
||||||
table[mouse(0).button(2)] = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
//=========
|
|
||||||
//Joypad(s)
|
|
||||||
//=========
|
|
||||||
|
|
||||||
for(unsigned i = 0; i < Joypad::Count; i++) {
|
|
||||||
if(!device.gamepad[i]) continue;
|
|
||||||
|
|
||||||
if(FAILED(device.gamepad[i]->Poll())) {
|
|
||||||
device.gamepad[i]->Acquire();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
DIJOYSTATE2 state;
|
|
||||||
device.gamepad[i]->GetDeviceState(sizeof(DIJOYSTATE2), &state);
|
|
||||||
|
|
||||||
//POV hats
|
|
||||||
for(unsigned n = 0; n < min((unsigned)Joypad::Hats, 4); n++) {
|
|
||||||
//POV value is in clockwise-hundredth degree units.
|
|
||||||
unsigned pov = state.rgdwPOV[n];
|
|
||||||
//some drivers report a centered POV hat as -1U, others as 65535U.
|
|
||||||
//>= 36000 will match both, as well as invalid ranges.
|
|
||||||
if(pov < 36000) {
|
|
||||||
if(pov >= 31500 || pov <= 4500) table[joypad(i).hat(n)] |= Joypad::HatUp;
|
|
||||||
if(pov >= 4500 && pov <= 13500) table[joypad(i).hat(n)] |= Joypad::HatRight;
|
|
||||||
if(pov >= 13500 && pov <= 22500) table[joypad(i).hat(n)] |= Joypad::HatDown;
|
|
||||||
if(pov >= 22500 && pov <= 31500) table[joypad(i).hat(n)] |= Joypad::HatLeft;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//axes
|
|
||||||
table[joypad(i).axis(0)] = state.lX;
|
|
||||||
table[joypad(i).axis(1)] = state.lY;
|
|
||||||
table[joypad(i).axis(2)] = state.lZ;
|
|
||||||
table[joypad(i).axis(3)] = state.lRx;
|
|
||||||
table[joypad(i).axis(4)] = state.lRy;
|
|
||||||
table[joypad(i).axis(5)] = state.lRz;
|
|
||||||
|
|
||||||
//buttons
|
|
||||||
for(unsigned n = 0; n < min((unsigned)Joypad::Buttons, 128); n++) {
|
|
||||||
table[joypad(i).button(n)] = (bool)state.rgbButtons[n];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
if(FAILED(device.context->CreateDevice(instance->guidInstance, &device.gamepad[n], 0))) {
|
|
||||||
return DIENUM_CONTINUE; //continue and try next gamepad
|
|
||||||
}
|
|
||||||
|
|
||||||
device.gamepad[n]->SetDataFormat(&c_dfDIJoystick2);
|
|
||||||
device.gamepad[n]->SetCooperativeLevel(settings.handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
|
|
||||||
device.gamepad[n]->EnumObjects(DI_EnumJoypadAxesCallback, (void*)this, DIDFT_ABSAXIS);
|
|
||||||
|
|
||||||
return DIENUM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
DIPROPRANGE range;
|
|
||||||
range.diph.dwSize = sizeof(DIPROPRANGE);
|
|
||||||
range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
|
||||||
range.diph.dwHow = DIPH_BYID;
|
|
||||||
range.diph.dwObj = instance->dwType;
|
|
||||||
range.lMin = -32768;
|
|
||||||
range.lMax = +32767;
|
|
||||||
device.gamepad[n]->SetProperty(DIPROP_RANGE, &range.diph);
|
|
||||||
|
|
||||||
return DIENUM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool init() {
|
|
||||||
device.context = 0;
|
|
||||||
device.keyboard = 0;
|
|
||||||
device.mouse = 0;
|
|
||||||
for(unsigned i = 0; i < Joypad::Count; i++) device.gamepad[i] = 0;
|
|
||||||
device.mouseacquired = false;
|
|
||||||
|
|
||||||
DirectInput8Create(GetModuleHandle(0), 0x0800, IID_IDirectInput8, (void**)&device.context, 0);
|
|
||||||
|
|
||||||
device.context->CreateDevice(GUID_SysKeyboard, &device.keyboard, 0);
|
|
||||||
device.keyboard->SetDataFormat(&c_dfDIKeyboard);
|
|
||||||
device.keyboard->SetCooperativeLevel(settings.handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
|
|
||||||
device.keyboard->Acquire();
|
|
||||||
|
|
||||||
device.context->CreateDevice(GUID_SysMouse, &device.mouse, 0);
|
|
||||||
device.mouse->SetDataFormat(&c_dfDIMouse2);
|
|
||||||
HRESULT hr = device.mouse->SetCooperativeLevel(settings.handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
|
|
||||||
device.mouse->Acquire();
|
|
||||||
|
|
||||||
device.context->EnumDevices(DI8DEVCLASS_GAMECTRL, DI_EnumJoypadsCallback, (void*)this, DIEDFL_ATTACHEDONLY);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void term() {
|
|
||||||
if(device.keyboard) {
|
|
||||||
device.keyboard->Unacquire();
|
|
||||||
device.keyboard->Release();
|
|
||||||
device.keyboard = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(device.mouse) {
|
|
||||||
device.mouse->Unacquire();
|
|
||||||
device.mouse->Release();
|
|
||||||
device.mouse = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(unsigned i = 0; i < Joypad::Count; i++) {
|
|
||||||
if(device.gamepad[i]) {
|
|
||||||
device.gamepad[i]->Unacquire();
|
|
||||||
device.gamepad[i]->Release();
|
|
||||||
device.gamepad[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(device.context) {
|
|
||||||
device.context->Release();
|
|
||||||
device.context = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool acquire() {
|
|
||||||
if(!device.mouse) return false;
|
|
||||||
if(acquired() == false) {
|
|
||||||
device.mouse->Unacquire();
|
|
||||||
device.mouse->SetCooperativeLevel(settings.handle, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
|
|
||||||
device.mouse->Acquire();
|
|
||||||
device.mouseacquired = true;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool unacquire() {
|
|
||||||
if(!device.mouse) return false;
|
|
||||||
if(acquired() == true) {
|
|
||||||
device.mouse->Unacquire();
|
|
||||||
device.mouse->SetCooperativeLevel(settings.handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
|
|
||||||
device.mouse->Acquire();
|
|
||||||
device.mouseacquired = false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool acquired() {
|
|
||||||
return device.mouseacquired;
|
|
||||||
}
|
|
||||||
|
|
||||||
pInputDI() {
|
|
||||||
device.context = 0;
|
|
||||||
device.keyboard = 0;
|
|
||||||
device.mouse = 0;
|
|
||||||
for(unsigned i = 0; i < Joypad::Count; i++) device.gamepad[i] = 0;
|
|
||||||
device.mouseacquired = false;
|
|
||||||
|
|
||||||
settings.handle = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
~pInputDI() { term(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
BOOL CALLBACK DI_EnumJoypadsCallback(const DIDEVICEINSTANCE* instance, void* p) {
|
|
||||||
return ((pInputDI*)p)->initJoypad(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL CALLBACK DI_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, void* p) {
|
|
||||||
return ((pInputDI*)p)->initAxis(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
DeclareInput(DI)
|
|
||||||
|
|
||||||
};
|
|
|
@ -0,0 +1,211 @@
|
||||||
|
#ifndef RUBY_INPUT_JOYPAD_DIRECTINPUT
|
||||||
|
#define RUBY_INPUT_JOYPAD_DIRECTINPUT
|
||||||
|
|
||||||
|
namespace ruby {
|
||||||
|
|
||||||
|
BOOL CALLBACK DirectInput_EnumJoypadsCallback(const DIDEVICEINSTANCE* instance, void* p);
|
||||||
|
BOOL CALLBACK DirectInput_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, void* p);
|
||||||
|
BOOL CALLBACK DirectInput_EnumJoypadEffectsCallback(const DIDEVICEOBJECTINSTANCE* instance, void* p);
|
||||||
|
|
||||||
|
struct InputJoypadDirectInput {
|
||||||
|
struct Joypad {
|
||||||
|
HID::Joypad hid;
|
||||||
|
|
||||||
|
LPDIRECTINPUTDEVICE8 device = nullptr;
|
||||||
|
LPDIRECTINPUTEFFECT effect = nullptr;
|
||||||
|
|
||||||
|
uint32_t pathID = 0;
|
||||||
|
uint16_t vendorID = 0;
|
||||||
|
uint16_t productID = 0;
|
||||||
|
bool isXInputDevice = false;
|
||||||
|
};
|
||||||
|
vector<Joypad> joypads;
|
||||||
|
|
||||||
|
uintptr_t handle = 0;
|
||||||
|
LPDIRECTINPUT8 context = nullptr;
|
||||||
|
LPDIRECTINPUTDEVICE8 device = nullptr;
|
||||||
|
bool xinputAvailable = false;
|
||||||
|
unsigned effects = 0;
|
||||||
|
|
||||||
|
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& jp : joypads) {
|
||||||
|
if(FAILED(jp.device->Poll())) jp.device->Acquire();
|
||||||
|
|
||||||
|
DIJOYSTATE2 state;
|
||||||
|
if(FAILED(jp.device->GetDeviceState(sizeof(DIJOYSTATE2), &state))) continue;
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < 4; n++) {
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Axis, 0, state.lX);
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Axis, 1, state.lY);
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Axis, 2, state.lZ);
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Axis, 3, state.lRx);
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Axis, 4, state.lRy);
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Axis, 5, state.lRz);
|
||||||
|
|
||||||
|
unsigned pov = state.rgdwPOV[n];
|
||||||
|
int16_t xaxis = 0;
|
||||||
|
int16_t yaxis = 0;
|
||||||
|
|
||||||
|
if(pov < 36000) {
|
||||||
|
if(pov >= 31500 || pov <= 4500) yaxis = -32768;
|
||||||
|
if(pov >= 4500 && pov <= 13500) xaxis = +32767;
|
||||||
|
if(pov >= 13500 && pov <= 22500) yaxis = +32767;
|
||||||
|
if(pov >= 22500 && pov <= 31500) xaxis = -32768;
|
||||||
|
}
|
||||||
|
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Hat, n * 2 + 0, xaxis);
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Hat, n * 2 + 1, yaxis);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < 128; n++) {
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Button, n, (bool)state.rgbButtons[n]);
|
||||||
|
}
|
||||||
|
|
||||||
|
devices.append(&jp.hid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rumble(uint64_t id, bool enable) {
|
||||||
|
for(auto& jp : joypads) {
|
||||||
|
if(jp.hid.id != id) continue;
|
||||||
|
if(jp.effect == nullptr) continue;
|
||||||
|
|
||||||
|
if(enable) jp.effect->Start(1, 0);
|
||||||
|
else jp.effect->Stop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool init(uintptr_t handle, LPDIRECTINPUT8 context, bool xinputAvailable) {
|
||||||
|
this->handle = handle;
|
||||||
|
this->context = context;
|
||||||
|
this->xinputAvailable = xinputAvailable;
|
||||||
|
context->EnumDevices(DI8DEVCLASS_GAMECTRL, DirectInput_EnumJoypadsCallback, (void*)this, DIEDFL_ATTACHEDONLY);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void term() {
|
||||||
|
for(auto& jp : joypads) {
|
||||||
|
jp.device->Unacquire();
|
||||||
|
if(jp.effect) jp.effect->Release();
|
||||||
|
jp.device->Release();
|
||||||
|
}
|
||||||
|
joypads.reset();
|
||||||
|
context = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool initJoypad(const DIDEVICEINSTANCE* instance) {
|
||||||
|
Joypad jp;
|
||||||
|
jp.vendorID = instance->guidProduct.Data1 >> 0;
|
||||||
|
jp.productID = instance->guidProduct.Data1 >> 16;
|
||||||
|
if(auto device = rawinput.find(jp.vendorID, jp.productID)) {
|
||||||
|
jp.pathID = crc32_calculate((const uint8_t*)device().path.data(), device().path.size());
|
||||||
|
jp.hid.id = (uint64_t)jp.pathID << 32 | jp.vendorID << 16 | jp.productID << 0;
|
||||||
|
jp.isXInputDevice = device().isXInputDevice;
|
||||||
|
} else {
|
||||||
|
//this should never occur
|
||||||
|
return DIENUM_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Microsoft has intentionally imposed artificial restrictions on XInput devices when used with DirectInput
|
||||||
|
//a) the two triggers are merged into a single axis, making uniquely distinguishing them impossible
|
||||||
|
//b) rumble support is not exposed
|
||||||
|
//thus, it's always preferred to let the XInput driver handle these joypads
|
||||||
|
//but if the driver is not available (XInput 1.3 does not ship with stock Windows XP), fall back on DirectInput
|
||||||
|
if(jp.isXInputDevice && xinputAvailable) return DIENUM_CONTINUE;
|
||||||
|
|
||||||
|
if(FAILED(context->CreateDevice(instance->guidInstance, &device, 0))) return DIENUM_CONTINUE;
|
||||||
|
jp.device = device;
|
||||||
|
device->SetDataFormat(&c_dfDIJoystick2);
|
||||||
|
device->SetCooperativeLevel((HWND)handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
|
||||||
|
|
||||||
|
effects = 0;
|
||||||
|
device->EnumObjects(DirectInput_EnumJoypadAxesCallback, (void*)this, DIDFT_ABSAXIS);
|
||||||
|
device->EnumObjects(DirectInput_EnumJoypadEffectsCallback, (void*)this, DIDFT_FFACTUATOR);
|
||||||
|
jp.hid.rumble = effects > 0;
|
||||||
|
|
||||||
|
if(jp.hid.rumble) {
|
||||||
|
//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, &jp.effect, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < 6; n++) jp.hid.axis().append({n});
|
||||||
|
for(unsigned n = 0; n < 8; n++) jp.hid.hat().append({n});
|
||||||
|
for(unsigned n = 0; n < 128; n++) jp.hid.button().append({n});
|
||||||
|
joypads.append(jp);
|
||||||
|
|
||||||
|
return DIENUM_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool initAxis(const DIDEVICEOBJECTINSTANCE* instance) {
|
||||||
|
DIPROPRANGE range;
|
||||||
|
memset(&range, 0, sizeof(DIPROPRANGE));
|
||||||
|
range.diph.dwSize = sizeof(DIPROPRANGE);
|
||||||
|
range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
||||||
|
range.diph.dwHow = DIPH_BYID;
|
||||||
|
range.diph.dwObj = instance->dwType;
|
||||||
|
range.lMin = -32768;
|
||||||
|
range.lMax = +32767;
|
||||||
|
device->SetProperty(DIPROP_RANGE, &range.diph);
|
||||||
|
return DIENUM_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool initEffect(const DIDEVICEOBJECTINSTANCE* instance) {
|
||||||
|
effects++;
|
||||||
|
return DIENUM_CONTINUE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BOOL CALLBACK DirectInput_EnumJoypadsCallback(const DIDEVICEINSTANCE* instance, void* p) {
|
||||||
|
return ((InputJoypadDirectInput*)p)->initJoypad(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CALLBACK DirectInput_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, void* p) {
|
||||||
|
return ((InputJoypadDirectInput*)p)->initAxis(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CALLBACK DirectInput_EnumJoypadEffectsCallback(const DIDEVICEOBJECTINSTANCE* instance, void* p) {
|
||||||
|
return ((InputJoypadDirectInput*)p)->initEffect(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -4,59 +4,74 @@
|
||||||
namespace ruby {
|
namespace ruby {
|
||||||
|
|
||||||
struct InputJoypadSDL {
|
struct InputJoypadSDL {
|
||||||
struct Joystick {
|
struct Joypad {
|
||||||
|
HID::Joypad hid;
|
||||||
|
|
||||||
unsigned id = 0;
|
unsigned id = 0;
|
||||||
SDL_Joystick* handle = nullptr;
|
SDL_Joystick* handle = nullptr;
|
||||||
};
|
};
|
||||||
vector<Joystick> joysticks;
|
vector<Joypad> joypads;
|
||||||
|
|
||||||
bool poll(int16_t* table) {
|
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) {
|
||||||
SDL_JoystickUpdate();
|
SDL_JoystickUpdate();
|
||||||
for(auto& js : joysticks) {
|
|
||||||
unsigned axes = min((unsigned)Joypad::Axes, SDL_JoystickNumAxes(js.handle));
|
for(auto& jp : joypads) {
|
||||||
for(unsigned axis = 0; axis < axes; axis++) {
|
for(unsigned n = 0; n < jp.hid.axis().input.size(); n++) {
|
||||||
table[joypad(js.id).axis(axis)] = (int16_t)SDL_JoystickGetAxis(js.handle, axis);
|
assign(jp.hid, HID::Joypad::GroupID::Axis, n, (int16_t)SDL_JoystickGetAxis(jp.handle, n));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned hats = min((unsigned)Joypad::Hats, SDL_JoystickNumHats(js.handle));
|
for(signed n = 0; n < (signed)jp.hid.hat().input.size() - 1; n += 2) {
|
||||||
for(unsigned hat = 0; hat < hats; hat++) {
|
uint8_t state = SDL_JoystickGetHat(jp.handle, n >> 1);
|
||||||
uint8_t state = SDL_JoystickGetHat(js.handle, hat);
|
assign(jp.hid, HID::Joypad::GroupID::Hat, n + 0, state & SDL_HAT_LEFT ? -32768 : state & SDL_HAT_RIGHT ? +32767 : 0);
|
||||||
int16_t value = 0;
|
assign(jp.hid, HID::Joypad::GroupID::Hat, n + 1, state & SDL_HAT_UP ? -32768 : state & SDL_HAT_DOWN ? +32767 : 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 n = 0; n < jp.hid.button().input.size(); n++) {
|
||||||
for(unsigned button = 0; button < Joypad::Buttons; button++) {
|
assign(jp.hid, HID::Joypad::GroupID::Button, n, (bool)SDL_JoystickGetButton(jp.handle, n));
|
||||||
table[joypad(js.id).button(button)] = (bool)SDL_JoystickGetButton(js.handle, button);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
devices.append(&jp.hid);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool init() {
|
bool init() {
|
||||||
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
|
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
|
||||||
SDL_JoystickEventState(SDL_IGNORE);
|
SDL_JoystickEventState(SDL_IGNORE);
|
||||||
|
|
||||||
unsigned joystickCount = SDL_NumJoysticks();
|
unsigned joypadCount = SDL_NumJoysticks();
|
||||||
for(unsigned id = 0; id < joystickCount; id++) {
|
for(unsigned id = 0; id < joypadCount; id++) {
|
||||||
Joystick joystick;
|
Joypad jp;
|
||||||
joystick.id = id;
|
jp.id = id;
|
||||||
joystick.handle = SDL_JoystickOpen(id);
|
jp.handle = SDL_JoystickOpen(id);
|
||||||
|
|
||||||
|
unsigned axes = SDL_JoystickNumAxes(jp.handle);
|
||||||
|
unsigned hats = SDL_JoystickNumHats(jp.handle) * 2;
|
||||||
|
unsigned buttons = 32; //there is no SDL_JoystickNumButtons()
|
||||||
|
|
||||||
|
jp.hid.id = 2 + jp.id;
|
||||||
|
for(unsigned n = 0; n < axes; n++) jp.hid.axis().append({n});
|
||||||
|
for(unsigned n = 0; n < hats; n++) jp.hid.hat().append({n});
|
||||||
|
for(unsigned n = 0; n < buttons; n++) jp.hid.button().append({n});
|
||||||
|
jp.hid.rumble = false;
|
||||||
|
|
||||||
|
joypads.append(jp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void term() {
|
void term() {
|
||||||
for(auto& js : joysticks) {
|
for(auto& jp : joypads) {
|
||||||
SDL_JoystickClose(js.handle);
|
SDL_JoystickClose(jp.handle);
|
||||||
}
|
}
|
||||||
joysticks.reset();
|
joypads.reset();
|
||||||
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
|
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,26 +10,26 @@ struct InputJoypadUdev {
|
||||||
udev_list_entry* devices = nullptr;
|
udev_list_entry* devices = nullptr;
|
||||||
udev_list_entry* item = nullptr;
|
udev_list_entry* item = nullptr;
|
||||||
|
|
||||||
struct JoystickInput {
|
struct JoypadInput {
|
||||||
signed code = 0;
|
signed code = 0;
|
||||||
unsigned id = 0;
|
unsigned id = 0;
|
||||||
int16_t value = 0;
|
int16_t value = 0;
|
||||||
input_absinfo info;
|
input_absinfo info;
|
||||||
|
|
||||||
JoystickInput() {}
|
JoypadInput() {}
|
||||||
JoystickInput(signed code) : code(code) {}
|
JoypadInput(signed code) : code(code) {}
|
||||||
JoystickInput(signed code, unsigned id) : code(code), id(id) {}
|
JoypadInput(signed code, unsigned id) : code(code), id(id) {}
|
||||||
bool operator< (const JoystickInput& source) const { return code < source.code; }
|
bool operator< (const JoypadInput& source) const { return code < source.code; }
|
||||||
bool operator==(const JoystickInput& source) const { return code == source.code; }
|
bool operator==(const JoypadInput& source) const { return code == source.code; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Joystick {
|
struct Joypad {
|
||||||
HID::Joypad hid;
|
HID::Joypad hid;
|
||||||
|
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
dev_t device = 0;
|
dev_t device = 0;
|
||||||
string deviceName;
|
string deviceName;
|
||||||
string devicePath;
|
string deviceNode;
|
||||||
|
|
||||||
uint8_t evbit[(EV_MAX + 7) / 8] = {0};
|
uint8_t evbit[(EV_MAX + 7) / 8] = {0};
|
||||||
uint8_t keybit[(KEY_MAX + 7) / 8] = {0};
|
uint8_t keybit[(KEY_MAX + 7) / 8] = {0};
|
||||||
|
@ -44,13 +44,13 @@ struct InputJoypadUdev {
|
||||||
string vendorID;
|
string vendorID;
|
||||||
string productID;
|
string productID;
|
||||||
|
|
||||||
set<JoystickInput> axes;
|
set<JoypadInput> axes;
|
||||||
set<JoystickInput> hats;
|
set<JoypadInput> hats;
|
||||||
set<JoystickInput> buttons;
|
set<JoypadInput> buttons;
|
||||||
bool rumble = false;
|
bool rumble = false;
|
||||||
unsigned effectID = 0;
|
unsigned effectID = 0;
|
||||||
};
|
};
|
||||||
vector<Joystick> joysticks;
|
vector<Joypad> joypads;
|
||||||
|
|
||||||
void assign(HID::Joypad& hid, unsigned groupID, unsigned inputID, int16_t value) {
|
void assign(HID::Joypad& hid, unsigned groupID, unsigned inputID, int16_t value) {
|
||||||
auto& group = hid.group[groupID];
|
auto& group = hid.group[groupID];
|
||||||
|
@ -60,10 +60,12 @@ struct InputJoypadUdev {
|
||||||
}
|
}
|
||||||
|
|
||||||
void poll(vector<HID::Device*>& devices) {
|
void poll(vector<HID::Device*>& devices) {
|
||||||
for(auto& js : joysticks) {
|
while(hotplugDevicesAvailable()) hotplugDevice();
|
||||||
|
|
||||||
|
for(auto& jp : joypads) {
|
||||||
input_event events[32];
|
input_event events[32];
|
||||||
signed length = 0;
|
signed length = 0;
|
||||||
while((length = read(js.fd, events, sizeof(events))) > 0) {
|
while((length = read(jp.fd, events, sizeof(events))) > 0) {
|
||||||
length /= sizeof(input_event);
|
length /= sizeof(input_event);
|
||||||
for(unsigned i = 0; i < length; i++) {
|
for(unsigned i = 0; i < length; i++) {
|
||||||
signed code = events[i].code;
|
signed code = events[i].code;
|
||||||
|
@ -71,117 +73,44 @@ struct InputJoypadUdev {
|
||||||
signed value = events[i].value;
|
signed value = events[i].value;
|
||||||
|
|
||||||
if(type == EV_ABS) {
|
if(type == EV_ABS) {
|
||||||
if(auto input = js.axes.find({code})) {
|
if(auto input = jp.axes.find({code})) {
|
||||||
signed range = input().info.maximum - input().info.minimum;
|
signed range = input().info.maximum - input().info.minimum;
|
||||||
value = (value - input().info.minimum) * 65535ll / range - 32767;
|
value = (value - input().info.minimum) * 65535ll / range - 32767;
|
||||||
assign(js.hid, HID::Joypad::GroupID::Axis, input().id, sclamp<16>(value));
|
assign(jp.hid, HID::Joypad::GroupID::Axis, input().id, sclamp<16>(value));
|
||||||
} else if(auto input = js.hats.find({code})) {
|
} else if(auto input = jp.hats.find({code})) {
|
||||||
signed range = input().info.maximum - input().info.minimum;
|
signed range = input().info.maximum - input().info.minimum;
|
||||||
value = (value - input().info.minimum) * 65535ll / range - 32767;
|
value = (value - input().info.minimum) * 65535ll / range - 32767;
|
||||||
assign(js.hid, HID::Joypad::GroupID::Hat, input().id, sclamp<16>(value));
|
assign(jp.hid, HID::Joypad::GroupID::Hat, input().id, sclamp<16>(value));
|
||||||
}
|
}
|
||||||
} else if(type == EV_KEY) {
|
} else if(type == EV_KEY) {
|
||||||
if(code >= BTN_MISC) {
|
if(code >= BTN_MISC) {
|
||||||
if(auto input = js.buttons.find({code})) {
|
if(auto input = jp.buttons.find({code})) {
|
||||||
assign(js.hid, HID::Joypad::GroupID::Button, input().id, (bool)value);
|
assign(jp.hid, HID::Joypad::GroupID::Button, input().id, (bool)value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
devices.append(&js.hid);
|
devices.append(&jp.hid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool poll(int16_t* table) {
|
bool rumble(uint64_t id, bool enable) {
|
||||||
unsigned i = 0;
|
for(auto& jp : joypads) {
|
||||||
for(auto& js : joysticks) {
|
if(jp.hid.id != id) continue;
|
||||||
input_event events[32];
|
if(jp.hid.rumble == false) continue;
|
||||||
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(uint64_t id, bool enable) {
|
|
||||||
for(auto& js : joysticks) {
|
|
||||||
if(js.hid.id != id) continue;
|
|
||||||
if(js.hid.rumble == false) continue;
|
|
||||||
|
|
||||||
input_event play;
|
input_event play;
|
||||||
memset(&play, 0, sizeof(input_event));
|
memset(&play, 0, sizeof(input_event));
|
||||||
play.type = EV_FF;
|
play.type = EV_FF;
|
||||||
play.code = js.effectID;
|
play.code = jp.effectID;
|
||||||
play.value = enable;
|
play.value = enable;
|
||||||
write(js.fd, &play, sizeof(input_event));
|
write(jp.fd, &play, sizeof(input_event));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool init() {
|
bool init() {
|
||||||
|
@ -200,10 +129,10 @@ struct InputJoypadUdev {
|
||||||
udev_enumerate_scan_devices(enumerator);
|
udev_enumerate_scan_devices(enumerator);
|
||||||
devices = udev_enumerate_get_list_entry(enumerator);
|
devices = udev_enumerate_get_list_entry(enumerator);
|
||||||
for(udev_list_entry* item = devices; item != nullptr; item = udev_list_entry_get_next(item)) {
|
for(udev_list_entry* item = devices; item != nullptr; item = udev_list_entry_get_next(item)) {
|
||||||
const char* name = udev_list_entry_get_name(item);
|
string name = udev_list_entry_get_name(item);
|
||||||
struct udev_device* device = udev_device_new_from_syspath(context, name);
|
udev_device* device = udev_device_new_from_syspath(context, name);
|
||||||
const char* deviceNode = udev_device_get_devnode(device);
|
string deviceNode = udev_device_get_devnode(device);
|
||||||
if(deviceNode) createJoystick(device, deviceNode);
|
if(deviceNode) createJoypad(device, deviceNode);
|
||||||
udev_device_unref(device);
|
udev_device_unref(device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,42 +145,66 @@ struct InputJoypadUdev {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void createJoystick(udev_device* device, const char* devicePath) {
|
bool hotplugDevicesAvailable() {
|
||||||
Joystick js;
|
pollfd fd = {0};
|
||||||
js.devicePath = devicePath;
|
fd.fd = udev_monitor_get_fd(monitor);
|
||||||
|
fd.events = POLLIN;
|
||||||
|
return (::poll(&fd, 1, 0) == 1) && (fd.revents & POLLIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hotplugDevice() {
|
||||||
|
udev_device* device = udev_monitor_receive_device(monitor);
|
||||||
|
if(device == nullptr) return;
|
||||||
|
|
||||||
|
string value = udev_device_get_property_value(device, "ID_INPUT_JOYSTICK");
|
||||||
|
string action = udev_device_get_action(device);
|
||||||
|
string deviceNode = udev_device_get_devnode(device);
|
||||||
|
if(value == "1") {
|
||||||
|
if(action == "add") {
|
||||||
|
createJoypad(device, deviceNode);
|
||||||
|
}
|
||||||
|
if(action == "remove") {
|
||||||
|
removeJoypad(device, deviceNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void createJoypad(udev_device* device, const string& deviceNode) {
|
||||||
|
Joypad jp;
|
||||||
|
jp.deviceNode = deviceNode;
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if(stat(devicePath, &st) < 0) return;
|
if(stat(deviceNode, &st) < 0) return;
|
||||||
js.device = st.st_rdev;
|
jp.device = st.st_rdev;
|
||||||
|
|
||||||
js.fd = open(devicePath, O_RDWR | O_NONBLOCK);
|
jp.fd = open(deviceNode, O_RDWR | O_NONBLOCK);
|
||||||
if(js.fd < 0) return;
|
if(jp.fd < 0) return;
|
||||||
|
|
||||||
uint8_t evbit[(EV_MAX + 7) / 8] = {0};
|
uint8_t evbit[(EV_MAX + 7) / 8] = {0};
|
||||||
uint8_t keybit[(KEY_MAX + 7) / 8] = {0};
|
uint8_t keybit[(KEY_MAX + 7) / 8] = {0};
|
||||||
uint8_t absbit[(ABS_MAX + 7) / 8] = {0};
|
uint8_t absbit[(ABS_MAX + 7) / 8] = {0};
|
||||||
|
|
||||||
ioctl(js.fd, EVIOCGBIT(0, sizeof(js.evbit)), js.evbit);
|
ioctl(jp.fd, EVIOCGBIT(0, sizeof(jp.evbit)), jp.evbit);
|
||||||
ioctl(js.fd, EVIOCGBIT(EV_KEY, sizeof(js.keybit)), js.keybit);
|
ioctl(jp.fd, EVIOCGBIT(EV_KEY, sizeof(jp.keybit)), jp.keybit);
|
||||||
ioctl(js.fd, EVIOCGBIT(EV_ABS, sizeof(js.absbit)), js.absbit);
|
ioctl(jp.fd, EVIOCGBIT(EV_ABS, sizeof(jp.absbit)), jp.absbit);
|
||||||
ioctl(js.fd, EVIOCGBIT(EV_FF, sizeof(js.ffbit)), js.ffbit);
|
ioctl(jp.fd, EVIOCGBIT(EV_FF, sizeof(jp.ffbit)), jp.ffbit);
|
||||||
ioctl(js.fd, EVIOCGEFFECTS, &js.effects);
|
ioctl(jp.fd, EVIOCGEFFECTS, &jp.effects);
|
||||||
|
|
||||||
#define testBit(buffer, bit) (buffer[(bit) >> 3] & 1 << ((bit) & 7))
|
#define testBit(buffer, bit) (buffer[(bit) >> 3] & 1 << ((bit) & 7))
|
||||||
|
|
||||||
if(testBit(js.evbit, EV_KEY)) {
|
if(testBit(jp.evbit, EV_KEY)) {
|
||||||
if(udev_device* parent = udev_device_get_parent_with_subsystem_devtype(device, "input", nullptr)) {
|
if(udev_device* parent = udev_device_get_parent_with_subsystem_devtype(device, "input", nullptr)) {
|
||||||
js.name = udev_device_get_sysattr_value(parent, "name");
|
jp.name = udev_device_get_sysattr_value(parent, "name");
|
||||||
js.vendorID = udev_device_get_sysattr_value(parent, "id/vendor");
|
jp.vendorID = udev_device_get_sysattr_value(parent, "id/vendor");
|
||||||
js.productID = udev_device_get_sysattr_value(parent, "id/product");
|
jp.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(udev_device* root = udev_device_get_parent_with_subsystem_devtype(parent, "usb", "usb_device")) {
|
||||||
if(js.vendorID == udev_device_get_sysattr_value(root, "idVendor")
|
if(jp.vendorID == udev_device_get_sysattr_value(root, "idVendor")
|
||||||
&& js.productID == udev_device_get_sysattr_value(root, "idProduct")
|
&& jp.productID == udev_device_get_sysattr_value(root, "idProduct")
|
||||||
) {
|
) {
|
||||||
js.deviceName = udev_device_get_devpath(root);
|
jp.deviceName = udev_device_get_devpath(root);
|
||||||
js.manufacturer = udev_device_get_sysattr_value(root, "manufacturer");
|
jp.manufacturer = udev_device_get_sysattr_value(root, "manufacturer");
|
||||||
js.product = udev_device_get_sysattr_value(root, "product");
|
jp.product = udev_device_get_sysattr_value(root, "product");
|
||||||
js.serial = udev_device_get_sysattr_value(root, "serial");
|
jp.serial = udev_device_get_sysattr_value(root, "serial");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -260,55 +213,65 @@ private:
|
||||||
unsigned hats = 0;
|
unsigned hats = 0;
|
||||||
unsigned buttons = 0;
|
unsigned buttons = 0;
|
||||||
for(signed i = 0; i < ABS_MISC; i++) {
|
for(signed i = 0; i < ABS_MISC; i++) {
|
||||||
if(testBit(js.absbit, i)) {
|
if(testBit(jp.absbit, i)) {
|
||||||
if(i >= ABS_HAT0X && i <= ABS_HAT3Y) {
|
if(i >= ABS_HAT0X && i <= ABS_HAT3Y) {
|
||||||
if(auto hat = js.hats.insert({i, hats++})) {
|
if(auto hat = jp.hats.insert({i, hats++})) {
|
||||||
ioctl(js.fd, EVIOCGABS(i), &hat().info);
|
ioctl(jp.fd, EVIOCGABS(i), &hat().info);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(auto axis = js.axes.insert({i, axes++})) {
|
if(auto axis = jp.axes.insert({i, axes++})) {
|
||||||
ioctl(js.fd, EVIOCGABS(i), &axis().info);
|
ioctl(jp.fd, EVIOCGABS(i), &axis().info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(signed i = BTN_JOYSTICK; i < KEY_MAX; i++) {
|
for(signed i = BTN_JOYSTICK; i < KEY_MAX; i++) {
|
||||||
if(testBit(js.keybit, i)) {
|
if(testBit(jp.keybit, i)) {
|
||||||
js.buttons.insert({i, buttons++});
|
jp.buttons.insert({i, buttons++});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(signed i = BTN_MISC; i < BTN_JOYSTICK; i++) {
|
for(signed i = BTN_MISC; i < BTN_JOYSTICK; i++) {
|
||||||
if(testBit(js.keybit, i)) {
|
if(testBit(jp.keybit, i)) {
|
||||||
js.buttons.insert({i, buttons++});
|
jp.buttons.insert({i, buttons++});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
js.rumble = js.effects >= 2 && testBit(js.ffbit, FF_RUMBLE);
|
jp.rumble = jp.effects >= 2 && testBit(jp.ffbit, FF_RUMBLE);
|
||||||
if(js.rumble) {
|
if(jp.rumble) {
|
||||||
ff_effect effect;
|
ff_effect effect;
|
||||||
memset(&effect, 0, sizeof(ff_effect));
|
memset(&effect, 0, sizeof(ff_effect));
|
||||||
effect.type = FF_RUMBLE;
|
effect.type = FF_RUMBLE;
|
||||||
effect.id = -1;
|
effect.id = -1;
|
||||||
effect.u.rumble.strong_magnitude = 65535;
|
effect.u.rumble.strong_magnitude = 65535;
|
||||||
effect.u.rumble.weak_magnitude = 65535;
|
effect.u.rumble.weak_magnitude = 65535;
|
||||||
ioctl(js.fd, EVIOCSFF, &effect);
|
ioctl(jp.fd, EVIOCSFF, &effect);
|
||||||
js.effectID = effect.id;
|
jp.effectID = effect.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
createJoystickHID(js);
|
createJoypadHID(jp);
|
||||||
joysticks.append(js);
|
joypads.append(jp);
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef testBit
|
#undef testBit
|
||||||
}
|
}
|
||||||
|
|
||||||
void createJoystickHID(Joystick& js) {
|
void createJoypadHID(Joypad& jp) {
|
||||||
uint64_t pathID = crc32_calculate((const uint8_t*)js.deviceName.data(), js.deviceName.size());
|
uint64_t pathID = crc32_calculate((const uint8_t*)jp.deviceName.data(), jp.deviceName.size());
|
||||||
js.hid.id = pathID << 32 | hex(js.vendorID) << 16 | hex(js.productID) << 0;
|
jp.hid.id = pathID << 32 | hex(jp.vendorID) << 16 | hex(jp.productID) << 0;
|
||||||
|
|
||||||
for(unsigned n = 0; n < js.axes.size(); n++) js.hid.axis.append({n});
|
for(unsigned n = 0; n < jp.axes.size(); n++) jp.hid.axis().append({n});
|
||||||
for(unsigned n = 0; n < js.hats.size(); n++) js.hid.hat.append({n});
|
for(unsigned n = 0; n < jp.hats.size(); n++) jp.hid.hat().append({n});
|
||||||
for(unsigned n = 0; n < js.buttons.size(); n++) js.hid.button.append({n});
|
for(unsigned n = 0; n < jp.buttons.size(); n++) jp.hid.button().append({n});
|
||||||
js.hid.rumble = js.rumble;
|
jp.hid.rumble = jp.rumble;
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeJoypad(udev_device* device, const string& deviceNode) {
|
||||||
|
for(unsigned n = 0; n < joypads.size(); n++) {
|
||||||
|
if(joypads[n].deviceNode == deviceNode) {
|
||||||
|
close(joypads[n].fd);
|
||||||
|
joypads.remove(n);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,103 +1,159 @@
|
||||||
#ifndef RUBY_INPUT_JOYPAD_XINPUT
|
#ifndef RUBY_INPUT_JOYPAD_XINPUT
|
||||||
#define RUBY_INPUT_JOYPAD_XINPUT
|
#define RUBY_INPUT_JOYPAD_XINPUT
|
||||||
|
|
||||||
#include <xinput.h>
|
//documented functionality
|
||||||
|
#define oXInputGetState "XInputGetState"
|
||||||
|
#define oXInputSetState "XInputSetState"
|
||||||
|
typedef DWORD WINAPI (*pXInputGetState)(DWORD dwUserIndex, XINPUT_STATE* pState);
|
||||||
|
typedef DWORD WINAPI (*pXInputSetState)(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration);
|
||||||
|
|
||||||
|
//undocumented functionality
|
||||||
|
#define oXInputGetStateEx (LPCSTR)100
|
||||||
|
#define oXInputWaitForGuideButton (LPCSTR)101
|
||||||
|
#define oXInputCancelGuideButtonWait (LPCSTR)102
|
||||||
|
#define oXInputPowerOffController (LPCSTR)103
|
||||||
|
typedef DWORD WINAPI (*pXInputGetStateEx)(DWORD dwUserIndex, XINPUT_STATE* pState);
|
||||||
|
typedef DWORD WINAPI (*pXInputWaitForGuideButton)(DWORD dwUserIndex, DWORD dwFlag, void* pUnknown);
|
||||||
|
typedef DWORD WINAPI (*pXInputCancelGuideButtonWait)(DWORD dwUserIndex);
|
||||||
|
typedef DWORD WINAPI (*pXInputPowerOffController)(DWORD dwUserIndex);
|
||||||
|
|
||||||
|
#define XINPUT_GAMEPAD_GUIDE 0x0400
|
||||||
|
|
||||||
namespace ruby {
|
namespace ruby {
|
||||||
|
|
||||||
struct InputJoypadXInput {
|
struct InputJoypadXInput {
|
||||||
HMODULE libxinput = nullptr;
|
HMODULE libxinput = nullptr;
|
||||||
DWORD WINAPI (*XInputGetState)(DWORD, XINPUT_STATE*) = nullptr;
|
pXInputGetStateEx XInputGetStateEx = nullptr;
|
||||||
DWORD WINAPI (*XInputSetState)(DWORD, XINPUT_VIBRATION*) = nullptr;
|
pXInputSetState XInputSetState = nullptr;
|
||||||
|
|
||||||
struct Joystick {
|
struct Joypad {
|
||||||
|
HID::Joypad hid;
|
||||||
unsigned id;
|
unsigned id;
|
||||||
|
|
||||||
int16_t hat = 0;
|
|
||||||
int16_t axis[6] = {0};
|
|
||||||
bool button[10] = {0};
|
|
||||||
};
|
};
|
||||||
vector<Joystick> joysticks;
|
vector<Joypad> joypads;
|
||||||
|
|
||||||
bool poll(int16_t* table) {
|
void assign(HID::Joypad& hid, unsigned groupID, unsigned inputID, int16_t value) {
|
||||||
if(!XInputGetState) return false;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
for(auto& js : joysticks) {
|
void poll(vector<HID::Device*>& devices) {
|
||||||
|
for(auto& jp : joypads) {
|
||||||
XINPUT_STATE state;
|
XINPUT_STATE state;
|
||||||
if(XInputGetState(js.id, &state) != ERROR_SUCCESS) continue;
|
if(XInputGetStateEx(jp.id, &state) != ERROR_SUCCESS) continue;
|
||||||
|
|
||||||
int16_t hat = 0;
|
//flip vertical axes so that -32768 = up, +32767 = down
|
||||||
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ) hat |= Joypad::HatUp;
|
uint16_t axisLY = 32768 + state.Gamepad.sThumbLY;
|
||||||
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ) hat |= Joypad::HatDown;
|
uint16_t axisRY = 32768 + state.Gamepad.sThumbRY;
|
||||||
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ) hat |= Joypad::HatLeft;
|
assign(jp.hid, HID::Joypad::GroupID::Axis, 0, (int16_t)state.Gamepad.sThumbLX);
|
||||||
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) hat |= Joypad::HatRight;
|
assign(jp.hid, HID::Joypad::GroupID::Axis, 1, (int16_t)(~axisLY - 32768));
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Axis, 2, (int16_t)state.Gamepad.sThumbRX);
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Axis, 3, (int16_t)(~axisRY - 32768));
|
||||||
|
|
||||||
//scale trigger ranges from (0 to 255) to (-32768 to +32767)
|
int16_t hatX = 0;
|
||||||
|
int16_t hatY = 0;
|
||||||
|
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ) hatY = -32768;
|
||||||
|
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ) hatY = +32767;
|
||||||
|
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ) hatX = -32768;
|
||||||
|
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) hatX = +32767;
|
||||||
|
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Hat, 0, hatX);
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Hat, 1, hatY);
|
||||||
|
|
||||||
|
//scale trigger ranges for up to down from (0 to 255) to (-32768 to +32767)
|
||||||
uint16_t triggerL = state.Gamepad.bLeftTrigger;
|
uint16_t triggerL = state.Gamepad.bLeftTrigger;
|
||||||
uint16_t triggerR = state.Gamepad.bRightTrigger;
|
uint16_t triggerR = state.Gamepad.bRightTrigger;
|
||||||
triggerL = triggerL << 8 | triggerL << 0;
|
triggerL = triggerL << 8 | triggerL << 0;
|
||||||
triggerR = triggerR << 8 | triggerR << 0;
|
triggerR = triggerR << 8 | triggerR << 0;
|
||||||
|
|
||||||
table[joypad(js.id).axis(0)] = (int16_t)state.Gamepad.sThumbLX;
|
assign(jp.hid, HID::Joypad::GroupID::Trigger, 0, (int16_t)(triggerL - 32768));
|
||||||
table[joypad(js.id).axis(1)] = (int16_t)state.Gamepad.sThumbLY;
|
assign(jp.hid, HID::Joypad::GroupID::Trigger, 1, (int16_t)(triggerR - 32768));
|
||||||
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;
|
assign(jp.hid, HID::Joypad::GroupID::Button, 0, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_A));
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Button, 1, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_B));
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Button, 2, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_X));
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Button, 3, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_Y));
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Button, 4, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK));
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Button, 5, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_START));
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Button, 6, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER));
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Button, 7, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER));
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Button, 8, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB));
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Button, 9, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB));
|
||||||
|
assign(jp.hid, HID::Joypad::GroupID::Button, 10, (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE));
|
||||||
|
|
||||||
|
devices.append(&jp.hid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rumble(unsigned id, bool enable) {
|
bool rumble(uint64_t id, bool enable) {
|
||||||
if(!XInputSetState) return;
|
for(auto& jp : joypads) {
|
||||||
if(id >= joysticks.size()) return;
|
if(jp.hid.id != id) continue;
|
||||||
|
|
||||||
XINPUT_VIBRATION vibration;
|
XINPUT_VIBRATION vibration;
|
||||||
memset(&vibration, 0, sizeof(XINPUT_VIBRATION));
|
memset(&vibration, 0, sizeof(XINPUT_VIBRATION));
|
||||||
vibration.wLeftMotorSpeed = enable ? 65535 : 0; //low-frequency motor (0 = off, 65535 = max)
|
vibration.wLeftMotorSpeed = enable ? 65535 : 0; //low-frequency motor (0 = off, 65535 = max)
|
||||||
vibration.wRightMotorSpeed = enable ? 65535 : 0; //high-frequency motor (0 = off, 65535 = max)
|
vibration.wRightMotorSpeed = enable ? 65535 : 0; //high-frequency motor (0 = off, 65535 = max)
|
||||||
XInputSetState(joysticks(id).id, &vibration);
|
XInputSetState(jp.id, &vibration);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool init() {
|
bool init() {
|
||||||
if(!libxinput) libxinput = LoadLibraryA("xinput1_3.dll");
|
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;
|
if(!libxinput) return false;
|
||||||
|
|
||||||
XInputGetState = (DWORD WINAPI (*)(DWORD, XINPUT_STATE*))GetProcAddress(libxinput, "XInputGetState");
|
//XInputGetStateEx is an undocumented function; but is required to get the state of the guide button
|
||||||
XInputSetState = (DWORD WINAPI (*)(DWORD, XINPUT_VIBRATION*))GetProcAddress(libxinput, "XInputSetState");
|
//if for some reason it is not available, fall back on XInputGetState, which takes the same parameters
|
||||||
|
XInputGetStateEx = (pXInputGetStateEx)GetProcAddress(libxinput, oXInputGetStateEx);
|
||||||
|
XInputSetState = (pXInputSetState)GetProcAddress(libxinput, oXInputSetState);
|
||||||
|
if(!XInputGetStateEx) XInputGetStateEx = (pXInputGetStateEx)GetProcAddress(libxinput, oXInputGetState);
|
||||||
|
if(!XInputGetStateEx || !XInputSetState) return term(), false;
|
||||||
|
|
||||||
//XInput supports a maximum of four controllers
|
//XInput supports a maximum of four controllers
|
||||||
|
//add all four to devices list now. If they are not connected, they will not show up in poll() results
|
||||||
for(unsigned id = 0; id < 4; id++) {
|
for(unsigned id = 0; id < 4; id++) {
|
||||||
XINPUT_STATE state;
|
Joypad jp;
|
||||||
if(XInputGetState(id, &state) == ERROR_SUCCESS) {
|
jp.id = id;
|
||||||
Joystick js;
|
jp.hid.id = (uint64_t)(1 + id) << 32 | 0x045e << 16 | 0x028e << 0; //Xbox 360 Player# + VendorID + ProductID
|
||||||
js.id = id;
|
|
||||||
joysticks.append(js);
|
jp.hid.axis().append({"LeftThumbX"});
|
||||||
}
|
jp.hid.axis().append({"LeftThumbY"});
|
||||||
|
jp.hid.axis().append({"RightThumbX"});
|
||||||
|
jp.hid.axis().append({"RightThumbY"});
|
||||||
|
|
||||||
|
jp.hid.hat().append({"HatX"});
|
||||||
|
jp.hid.hat().append({"HatY"});
|
||||||
|
|
||||||
|
jp.hid.trigger().append({"LeftTrigger"});
|
||||||
|
jp.hid.trigger().append({"RightTrigger"});
|
||||||
|
|
||||||
|
jp.hid.button().append({"A"});
|
||||||
|
jp.hid.button().append({"B"});
|
||||||
|
jp.hid.button().append({"X"});
|
||||||
|
jp.hid.button().append({"Y"});
|
||||||
|
jp.hid.button().append({"Back"});
|
||||||
|
jp.hid.button().append({"Start"});
|
||||||
|
jp.hid.button().append({"LeftShoulder"});
|
||||||
|
jp.hid.button().append({"RightShoulder"});
|
||||||
|
jp.hid.button().append({"LeftThumb"});
|
||||||
|
jp.hid.button().append({"RightThumb"});
|
||||||
|
jp.hid.button().append({"Guide"});
|
||||||
|
|
||||||
|
joypads.append(jp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void term() {
|
void term() {
|
||||||
if(libxinput) {
|
if(!libxinput) return;
|
||||||
FreeLibrary(libxinput);
|
|
||||||
libxinput = nullptr;
|
FreeLibrary(libxinput);
|
||||||
}
|
libxinput = nullptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,178 @@
|
||||||
|
#ifndef RUBY_INPUT_KEYBOARD_RAWINPUT
|
||||||
|
#define RUBY_INPUT_KEYBOARD_RAWINPUT
|
||||||
|
|
||||||
|
namespace ruby {
|
||||||
|
|
||||||
|
struct InputKeyboardRawInput {
|
||||||
|
struct Key {
|
||||||
|
uint16_t code;
|
||||||
|
uint16_t flag;
|
||||||
|
string name;
|
||||||
|
bool value;
|
||||||
|
};
|
||||||
|
vector<Key> keys;
|
||||||
|
|
||||||
|
struct Keyboard {
|
||||||
|
HID::Keyboard hid;
|
||||||
|
} kb;
|
||||||
|
|
||||||
|
void update(RAWINPUT* input) {
|
||||||
|
unsigned code = input->data.keyboard.MakeCode;
|
||||||
|
unsigned flag = input->data.keyboard.Flags;
|
||||||
|
|
||||||
|
for(auto& key : keys) {
|
||||||
|
if(key.code != code) continue;
|
||||||
|
key.value = (key.flag == flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assign(unsigned inputID, bool value) {
|
||||||
|
auto& group = kb.hid.group[HID::Keyboard::GroupID::Button];
|
||||||
|
if(group.input[inputID].value == value) return;
|
||||||
|
if(input.onChange) input.onChange(kb.hid, HID::Keyboard::GroupID::Button, inputID, group.input[inputID].value, value);
|
||||||
|
group.input[inputID].value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void poll(vector<HID::Device*>& devices) {
|
||||||
|
for(unsigned n = 0; n < keys.size(); n++) assign(n, keys[n].value);
|
||||||
|
devices.append(&kb.hid);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool init() {
|
||||||
|
rawinput.updateKeyboard = {&InputKeyboardRawInput::update, this};
|
||||||
|
|
||||||
|
//Pause sends 0x001d,4 + 0x0045,0; NumLock sends only 0x0045,0
|
||||||
|
//pressing Pause will falsely trigger NumLock
|
||||||
|
//further, pause sends its key release even while button is held down
|
||||||
|
//because of this, we cannot map either reliably
|
||||||
|
|
||||||
|
keys.append({0x0001, 0, "Escape"});
|
||||||
|
keys.append({0x003b, 0, "F1"});
|
||||||
|
keys.append({0x003c, 0, "F2"});
|
||||||
|
keys.append({0x003d, 0, "F3"});
|
||||||
|
keys.append({0x003e, 0, "F4"});
|
||||||
|
keys.append({0x003f, 0, "F5"});
|
||||||
|
keys.append({0x0040, 0, "F6"});
|
||||||
|
keys.append({0x0041, 0, "F7"});
|
||||||
|
keys.append({0x0042, 0, "F8"});
|
||||||
|
keys.append({0x0043, 0, "F9"});
|
||||||
|
keys.append({0x0044, 0, "F10"});
|
||||||
|
keys.append({0x0057, 0, "F11"});
|
||||||
|
keys.append({0x0058, 0, "F12"});
|
||||||
|
|
||||||
|
keys.append({0x0037, 2, "PrintScreen"});
|
||||||
|
keys.append({0x0046, 0, "ScrollLock"});
|
||||||
|
//keys.append({0x001d, 4, "Pause"});
|
||||||
|
keys.append({0x0029, 0, "Tilde"});
|
||||||
|
|
||||||
|
keys.append({0x0002, 0, "Num1"});
|
||||||
|
keys.append({0x0003, 0, "Num2"});
|
||||||
|
keys.append({0x0004, 0, "Num3"});
|
||||||
|
keys.append({0x0005, 0, "Num4"});
|
||||||
|
keys.append({0x0006, 0, "Num5"});
|
||||||
|
keys.append({0x0007, 0, "Num6"});
|
||||||
|
keys.append({0x0008, 0, "Num7"});
|
||||||
|
keys.append({0x0009, 0, "Num8"});
|
||||||
|
keys.append({0x000a, 0, "Num9"});
|
||||||
|
keys.append({0x000b, 0, "Num0"});
|
||||||
|
|
||||||
|
keys.append({0x000c, 0, "Dash"});
|
||||||
|
keys.append({0x000d, 0, "Equal"});
|
||||||
|
keys.append({0x000e, 0, "Backspace"});
|
||||||
|
|
||||||
|
keys.append({0x0052, 2, "Insert"});
|
||||||
|
keys.append({0x0053, 2, "Delete"});
|
||||||
|
keys.append({0x0047, 2, "Home"});
|
||||||
|
keys.append({0x004f, 2, "End"});
|
||||||
|
keys.append({0x0049, 2, "PageUp"});
|
||||||
|
keys.append({0x0051, 2, "PageDown"});
|
||||||
|
|
||||||
|
keys.append({0x001e, 0, "A"});
|
||||||
|
keys.append({0x0030, 0, "B"});
|
||||||
|
keys.append({0x002e, 0, "C"});
|
||||||
|
keys.append({0x0020, 0, "D"});
|
||||||
|
keys.append({0x0012, 0, "E"});
|
||||||
|
keys.append({0x0021, 0, "F"});
|
||||||
|
keys.append({0x0022, 0, "G"});
|
||||||
|
keys.append({0x0023, 0, "H"});
|
||||||
|
keys.append({0x0017, 0, "I"});
|
||||||
|
keys.append({0x0024, 0, "J"});
|
||||||
|
keys.append({0x0025, 0, "K"});
|
||||||
|
keys.append({0x0026, 0, "L"});
|
||||||
|
keys.append({0x0032, 0, "M"});
|
||||||
|
keys.append({0x0031, 0, "N"});
|
||||||
|
keys.append({0x0018, 0, "O"});
|
||||||
|
keys.append({0x0019, 0, "P"});
|
||||||
|
keys.append({0x0010, 0, "Q"});
|
||||||
|
keys.append({0x0013, 0, "R"});
|
||||||
|
keys.append({0x001f, 0, "S"});
|
||||||
|
keys.append({0x0014, 0, "T"});
|
||||||
|
keys.append({0x0016, 0, "U"});
|
||||||
|
keys.append({0x002f, 0, "V"});
|
||||||
|
keys.append({0x0011, 0, "W"});
|
||||||
|
keys.append({0x002d, 0, "X"});
|
||||||
|
keys.append({0x0015, 0, "Y"});
|
||||||
|
keys.append({0x002c, 0, "Z"});
|
||||||
|
|
||||||
|
keys.append({0x001a, 0, "LeftBracket"});
|
||||||
|
keys.append({0x001b, 0, "RightBracket"});
|
||||||
|
keys.append({0x002b, 0, "Backslash"});
|
||||||
|
keys.append({0x0027, 0, "Semicolon"});
|
||||||
|
keys.append({0x0028, 0, "Apostrophe"});
|
||||||
|
keys.append({0x0033, 0, "Comma"});
|
||||||
|
keys.append({0x0034, 0, "Period"});
|
||||||
|
keys.append({0x0035, 0, "Slash"});
|
||||||
|
|
||||||
|
keys.append({0x004f, 0, "Keypad1"});
|
||||||
|
keys.append({0x0050, 0, "Keypad2"});
|
||||||
|
keys.append({0x0051, 0, "Keypad3"});
|
||||||
|
keys.append({0x004b, 0, "Keypad4"});
|
||||||
|
keys.append({0x004c, 0, "Keypad5"});
|
||||||
|
keys.append({0x004d, 0, "Keypad6"});
|
||||||
|
keys.append({0x0047, 0, "Keypad7"});
|
||||||
|
keys.append({0x0048, 0, "Keypad8"});
|
||||||
|
keys.append({0x0049, 0, "Keypad9"});
|
||||||
|
keys.append({0x0052, 0, "Keypad0"});
|
||||||
|
|
||||||
|
keys.append({0x0053, 0, "Point"});
|
||||||
|
keys.append({0x001c, 2, "Enter"});
|
||||||
|
keys.append({0x004e, 0, "Add"});
|
||||||
|
keys.append({0x004a, 0, "Subtract"});
|
||||||
|
keys.append({0x0037, 0, "Multiply"});
|
||||||
|
keys.append({0x0035, 2, "Divide"});
|
||||||
|
|
||||||
|
//keys.append({0x0045, 0, "NumLock"});
|
||||||
|
keys.append({0x003a, 0, "CapsLock"});
|
||||||
|
|
||||||
|
keys.append({0x0048, 2, "Up"});
|
||||||
|
keys.append({0x0050, 2, "Down"});
|
||||||
|
keys.append({0x004b, 2, "Left"});
|
||||||
|
keys.append({0x004d, 2, "Right"});
|
||||||
|
|
||||||
|
keys.append({0x000f, 0, "Tab"});
|
||||||
|
keys.append({0x001c, 0, "Return"});
|
||||||
|
keys.append({0x0039, 0, "Spacebar"});
|
||||||
|
|
||||||
|
keys.append({0x002a, 0, "LeftShift"});
|
||||||
|
keys.append({0x0036, 0, "RightShift"});
|
||||||
|
keys.append({0x001d, 0, "LeftControl"});
|
||||||
|
keys.append({0x001d, 2, "RightControl"});
|
||||||
|
keys.append({0x0038, 0, "LeftAlt"});
|
||||||
|
keys.append({0x0038, 2, "RightAlt"});
|
||||||
|
keys.append({0x005b, 2, "LeftSuper"});
|
||||||
|
keys.append({0x005c, 2, "RightSuper"});
|
||||||
|
keys.append({0x005d, 2, "Menu"});
|
||||||
|
|
||||||
|
kb.hid.id = 1;
|
||||||
|
for(auto& key : keys) kb.hid.button().append({key.name});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void term() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -7,7 +7,6 @@ struct InputKeyboardXlib {
|
||||||
HID::Keyboard hid;
|
HID::Keyboard hid;
|
||||||
|
|
||||||
Display* display = nullptr;
|
Display* display = nullptr;
|
||||||
uint8_t scancode[256] = {0};
|
|
||||||
|
|
||||||
struct Key {
|
struct Key {
|
||||||
string name;
|
string name;
|
||||||
|
@ -16,23 +15,6 @@ struct InputKeyboardXlib {
|
||||||
};
|
};
|
||||||
vector<Key> keys;
|
vector<Key> keys;
|
||||||
|
|
||||||
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,
|
|
||||||
Limit,
|
|
||||||
};
|
|
||||||
|
|
||||||
void assign(unsigned inputID, bool value) {
|
void assign(unsigned inputID, bool value) {
|
||||||
auto& group = hid.group[HID::Keyboard::GroupID::Button];
|
auto& group = hid.group[HID::Keyboard::GroupID::Button];
|
||||||
if(group.input[inputID].value == value) return;
|
if(group.input[inputID].value == value) return;
|
||||||
|
@ -52,129 +34,6 @@ struct InputKeyboardXlib {
|
||||||
devices.append(&hid);
|
devices.append(&hid);
|
||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
bool init() {
|
||||||
display = XOpenDisplay(0);
|
display = XOpenDisplay(0);
|
||||||
|
|
||||||
|
@ -295,127 +154,10 @@ struct InputKeyboardXlib {
|
||||||
hid.id = 1;
|
hid.id = 1;
|
||||||
|
|
||||||
for(unsigned n = 0; n < keys.size(); n++) {
|
for(unsigned n = 0; n < keys.size(); n++) {
|
||||||
hid.button.append(keys[n].name);
|
hid.button().append(keys[n].name);
|
||||||
keys[n].keycode = XKeysymToKeycode(display, keys[n].keysym);
|
keys[n].keycode = XKeysymToKeycode(display, keys[n].keysym);
|
||||||
}
|
}
|
||||||
|
|
||||||
#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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
#ifndef RUBY_INPUT_MOUSE_RAWINPUT
|
||||||
|
#define RUBY_INPUT_MOUSE_RAWINPUT
|
||||||
|
|
||||||
|
namespace ruby {
|
||||||
|
|
||||||
|
struct InputMouseRawInput {
|
||||||
|
uintptr_t handle = 0;
|
||||||
|
bool mouseAcquired = false;
|
||||||
|
|
||||||
|
struct Mouse {
|
||||||
|
HID::Mouse hid;
|
||||||
|
|
||||||
|
signed relativeX = 0;
|
||||||
|
signed relativeY = 0;
|
||||||
|
signed relativeZ = 0;
|
||||||
|
bool buttons[5] = {0};
|
||||||
|
} ms;
|
||||||
|
|
||||||
|
bool acquire() {
|
||||||
|
if(mouseAcquired == false) {
|
||||||
|
mouseAcquired = true;
|
||||||
|
ShowCursor(false);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unacquire() {
|
||||||
|
if(mouseAcquired == true) {
|
||||||
|
mouseAcquired = false;
|
||||||
|
ReleaseCapture();
|
||||||
|
ClipCursor(NULL);
|
||||||
|
ShowCursor(true);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool acquired() {
|
||||||
|
if(mouseAcquired == true) {
|
||||||
|
SetFocus((HWND)handle);
|
||||||
|
SetCapture((HWND)handle);
|
||||||
|
RECT rc;
|
||||||
|
GetWindowRect((HWND)handle, &rc);
|
||||||
|
ClipCursor(&rc);
|
||||||
|
}
|
||||||
|
return GetCapture() == (HWND)handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(RAWINPUT* input) {
|
||||||
|
if((input->data.mouse.usFlags & 1) == MOUSE_MOVE_RELATIVE) {
|
||||||
|
ms.relativeX += input->data.mouse.lLastX;
|
||||||
|
ms.relativeY += input->data.mouse.lLastY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_WHEEL) {
|
||||||
|
ms.relativeZ += (int16_t)input->data.mouse.usButtonData;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) ms.buttons[0] = 1;
|
||||||
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP ) ms.buttons[0] = 0;
|
||||||
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) ms.buttons[1] = 1;
|
||||||
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP ) ms.buttons[1] = 0;
|
||||||
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) ms.buttons[2] = 1;
|
||||||
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP ) ms.buttons[2] = 0;
|
||||||
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) ms.buttons[3] = 1;
|
||||||
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP ) ms.buttons[3] = 0;
|
||||||
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) ms.buttons[4] = 1;
|
||||||
|
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP ) ms.buttons[4] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void assign(unsigned groupID, unsigned inputID, int16_t value) {
|
||||||
|
auto& group = ms.hid.group[groupID];
|
||||||
|
if(group.input[inputID].value == value) return;
|
||||||
|
if(input.onChange) input.onChange(ms.hid, groupID, inputID, group.input[inputID].value, value);
|
||||||
|
group.input[inputID].value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void poll(vector<HID::Device*>& devices) {
|
||||||
|
assign(HID::Mouse::GroupID::Axis, 0, ms.relativeX);
|
||||||
|
assign(HID::Mouse::GroupID::Axis, 1, ms.relativeY);
|
||||||
|
assign(HID::Mouse::GroupID::Axis, 2, ms.relativeZ);
|
||||||
|
|
||||||
|
//keys are intentionally reordered below:
|
||||||
|
//in ruby, button order is {left, middle, right, up, down}
|
||||||
|
assign(HID::Mouse::GroupID::Button, 0, ms.buttons[0]);
|
||||||
|
assign(HID::Mouse::GroupID::Button, 2, ms.buttons[1]);
|
||||||
|
assign(HID::Mouse::GroupID::Button, 1, ms.buttons[2]);
|
||||||
|
assign(HID::Mouse::GroupID::Button, 4, ms.buttons[3]);
|
||||||
|
assign(HID::Mouse::GroupID::Button, 3, ms.buttons[4]);
|
||||||
|
|
||||||
|
ms.relativeX = 0;
|
||||||
|
ms.relativeY = 0;
|
||||||
|
ms.relativeZ = 0;
|
||||||
|
|
||||||
|
devices.append(&ms.hid);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool init(uintptr_t handle) {
|
||||||
|
this->handle = handle;
|
||||||
|
|
||||||
|
ms.hid.id = 2;
|
||||||
|
|
||||||
|
ms.hid.axis().append({"X"});
|
||||||
|
ms.hid.axis().append({"Y"});
|
||||||
|
ms.hid.axis().append({"Z"});
|
||||||
|
|
||||||
|
ms.hid.button().append({"Left"});
|
||||||
|
ms.hid.button().append({"Middle"});
|
||||||
|
ms.hid.button().append({"Right"});
|
||||||
|
ms.hid.button().append({"Up"});
|
||||||
|
ms.hid.button().append({"Down"});
|
||||||
|
|
||||||
|
rawinput.updateMouse = {&InputMouseRawInput::update, this};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void term() {
|
||||||
|
unacquire();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -81,7 +81,7 @@ struct InputMouseXlib {
|
||||||
assign(HID::Mouse::GroupID::Axis, 0, (int16_t)(rootXReturn - screenWidth / 2));
|
assign(HID::Mouse::GroupID::Axis, 0, (int16_t)(rootXReturn - screenWidth / 2));
|
||||||
assign(HID::Mouse::GroupID::Axis, 1, (int16_t)(rootYReturn - screenHeight / 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(hid.axis().input[0].value != 0 || hid.axis().input[1].value != 0) {
|
||||||
//if mouse moved, re-center mouse for next poll
|
//if mouse moved, re-center mouse for next poll
|
||||||
XWarpPointer(display, None, rootWindow, 0, 0, 0, 0, screenWidth / 2, screenHeight / 2);
|
XWarpPointer(display, None, rootWindow, 0, 0, 0, 0, screenWidth / 2, screenHeight / 2);
|
||||||
}
|
}
|
||||||
|
@ -102,45 +102,6 @@ struct InputMouseXlib {
|
||||||
devices.append(&hid);
|
devices.append(&hid);
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
bool init(uintptr_t handle) {
|
||||||
this->handle = handle;
|
this->handle = handle;
|
||||||
display = XOpenDisplay(0);
|
display = XOpenDisplay(0);
|
||||||
|
@ -151,6 +112,7 @@ struct InputMouseXlib {
|
||||||
screenWidth = attributes.width;
|
screenWidth = attributes.width;
|
||||||
screenHeight = attributes.height;
|
screenHeight = attributes.height;
|
||||||
|
|
||||||
|
//Xlib: because XShowCursor(display, false) would just be too easy
|
||||||
//create invisible cursor for use when mouse is acquired
|
//create invisible cursor for use when mouse is acquired
|
||||||
Pixmap pixmap;
|
Pixmap pixmap;
|
||||||
XColor black, unused;
|
XColor black, unused;
|
||||||
|
@ -168,14 +130,14 @@ struct InputMouseXlib {
|
||||||
|
|
||||||
hid.id = 2;
|
hid.id = 2;
|
||||||
|
|
||||||
hid.axis.append({"X"});
|
hid.axis().append({"X"});
|
||||||
hid.axis.append({"Y"});
|
hid.axis().append({"Y"});
|
||||||
|
|
||||||
hid.button.append({"Left"});
|
hid.button().append({"Left"});
|
||||||
hid.button.append({"Middle"});
|
hid.button().append({"Middle"});
|
||||||
hid.button.append({"Right"});
|
hid.button().append({"Right"});
|
||||||
hid.button.append({"Up"});
|
hid.button().append({"Up"});
|
||||||
hid.button.append({"Down"});
|
hid.button().append({"Down"});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,765 +0,0 @@
|
||||||
//RawInput driver
|
|
||||||
//author: byuu
|
|
||||||
|
|
||||||
//this driver utilizes RawInput (WM_INPUT) to capture keyboard and mouse input.
|
|
||||||
//although this requires WinXP or newer, it is the only way to uniquely identify
|
|
||||||
//and independently map multiple keyboards and mice. DirectInput merges all
|
|
||||||
//keyboards and mice into one device per.
|
|
||||||
//
|
|
||||||
//as WM_INPUT lacks specific RAWINPUT structures for gamepads, giving only raw
|
|
||||||
//data, and because DirectInput supports up to 16 joypads, DirectInput is used
|
|
||||||
//for joypad mapping.
|
|
||||||
//
|
|
||||||
//further, Xbox 360 controllers are explicitly detected and supported through
|
|
||||||
//XInput. this is because under DirectInput, the LT / RT (trigger) buttons are
|
|
||||||
//merged into a single Z-axis -- making it impossible to detect both buttons
|
|
||||||
//being pressed at the same time. with XInput, the state of both trigger
|
|
||||||
//buttons can be read independently.
|
|
||||||
//
|
|
||||||
//so in essence, this is actually more of a hybrid driver.
|
|
||||||
|
|
||||||
#define DIRECTINPUT_VERSION 0x0800
|
|
||||||
#include <dinput.h>
|
|
||||||
|
|
||||||
#include "joypad/xinput.cpp"
|
|
||||||
|
|
||||||
namespace ruby {
|
|
||||||
|
|
||||||
static DWORD WINAPI RawInputThreadProc(void*);
|
|
||||||
static LRESULT CALLBACK RawInputWindowProc(HWND, UINT, WPARAM, LPARAM);
|
|
||||||
|
|
||||||
struct RawInput {
|
|
||||||
HANDLE mutex;
|
|
||||||
HWND hwnd;
|
|
||||||
bool initialized;
|
|
||||||
bool ready;
|
|
||||||
|
|
||||||
struct Device {
|
|
||||||
HANDLE handle;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Keyboard : Device {
|
|
||||||
bool state[nall::Keyboard::Size];
|
|
||||||
|
|
||||||
void update(RAWINPUT* input) {
|
|
||||||
unsigned code = input->data.keyboard.MakeCode;
|
|
||||||
unsigned flags = input->data.keyboard.Flags;
|
|
||||||
|
|
||||||
#define map(id, flag, name) if(code == id) state[name] = (bool)(flags == flag);
|
|
||||||
map(0x0001, 0, nall::Keyboard::Escape)
|
|
||||||
map(0x003b, 0, nall::Keyboard::F1)
|
|
||||||
map(0x003c, 0, nall::Keyboard::F2)
|
|
||||||
map(0x003d, 0, nall::Keyboard::F3)
|
|
||||||
map(0x003e, 0, nall::Keyboard::F4)
|
|
||||||
map(0x003f, 0, nall::Keyboard::F5)
|
|
||||||
map(0x0040, 0, nall::Keyboard::F6)
|
|
||||||
map(0x0041, 0, nall::Keyboard::F7)
|
|
||||||
map(0x0042, 0, nall::Keyboard::F8)
|
|
||||||
map(0x0043, 0, nall::Keyboard::F9)
|
|
||||||
map(0x0044, 0, nall::Keyboard::F10)
|
|
||||||
map(0x0057, 0, nall::Keyboard::F11)
|
|
||||||
map(0x0058, 0, nall::Keyboard::F12)
|
|
||||||
|
|
||||||
map(0x0037, 2, nall::Keyboard::PrintScreen)
|
|
||||||
map(0x0046, 0, nall::Keyboard::ScrollLock)
|
|
||||||
map(0x001d, 4, nall::Keyboard::Pause)
|
|
||||||
map(0x0029, 0, nall::Keyboard::Tilde)
|
|
||||||
|
|
||||||
map(0x0002, 0, nall::Keyboard::Num1)
|
|
||||||
map(0x0003, 0, nall::Keyboard::Num2)
|
|
||||||
map(0x0004, 0, nall::Keyboard::Num3)
|
|
||||||
map(0x0005, 0, nall::Keyboard::Num4)
|
|
||||||
map(0x0006, 0, nall::Keyboard::Num5)
|
|
||||||
map(0x0007, 0, nall::Keyboard::Num6)
|
|
||||||
map(0x0008, 0, nall::Keyboard::Num7)
|
|
||||||
map(0x0009, 0, nall::Keyboard::Num8)
|
|
||||||
map(0x000a, 0, nall::Keyboard::Num9)
|
|
||||||
map(0x000b, 0, nall::Keyboard::Num0)
|
|
||||||
|
|
||||||
map(0x000c, 0, nall::Keyboard::Dash)
|
|
||||||
map(0x000d, 0, nall::Keyboard::Equal)
|
|
||||||
map(0x000e, 0, nall::Keyboard::Backspace)
|
|
||||||
|
|
||||||
map(0x0052, 2, nall::Keyboard::Insert)
|
|
||||||
map(0x0053, 2, nall::Keyboard::Delete)
|
|
||||||
map(0x0047, 2, nall::Keyboard::Home)
|
|
||||||
map(0x004f, 2, nall::Keyboard::End)
|
|
||||||
map(0x0049, 2, nall::Keyboard::PageUp)
|
|
||||||
map(0x0051, 2, nall::Keyboard::PageDown)
|
|
||||||
|
|
||||||
map(0x001e, 0, nall::Keyboard::A)
|
|
||||||
map(0x0030, 0, nall::Keyboard::B)
|
|
||||||
map(0x002e, 0, nall::Keyboard::C)
|
|
||||||
map(0x0020, 0, nall::Keyboard::D)
|
|
||||||
map(0x0012, 0, nall::Keyboard::E)
|
|
||||||
map(0x0021, 0, nall::Keyboard::F)
|
|
||||||
map(0x0022, 0, nall::Keyboard::G)
|
|
||||||
map(0x0023, 0, nall::Keyboard::H)
|
|
||||||
map(0x0017, 0, nall::Keyboard::I)
|
|
||||||
map(0x0024, 0, nall::Keyboard::J)
|
|
||||||
map(0x0025, 0, nall::Keyboard::K)
|
|
||||||
map(0x0026, 0, nall::Keyboard::L)
|
|
||||||
map(0x0032, 0, nall::Keyboard::M)
|
|
||||||
map(0x0031, 0, nall::Keyboard::N)
|
|
||||||
map(0x0018, 0, nall::Keyboard::O)
|
|
||||||
map(0x0019, 0, nall::Keyboard::P)
|
|
||||||
map(0x0010, 0, nall::Keyboard::Q)
|
|
||||||
map(0x0013, 0, nall::Keyboard::R)
|
|
||||||
map(0x001f, 0, nall::Keyboard::S)
|
|
||||||
map(0x0014, 0, nall::Keyboard::T)
|
|
||||||
map(0x0016, 0, nall::Keyboard::U)
|
|
||||||
map(0x002f, 0, nall::Keyboard::V)
|
|
||||||
map(0x0011, 0, nall::Keyboard::W)
|
|
||||||
map(0x002d, 0, nall::Keyboard::X)
|
|
||||||
map(0x0015, 0, nall::Keyboard::Y)
|
|
||||||
map(0x002c, 0, nall::Keyboard::Z)
|
|
||||||
|
|
||||||
map(0x001a, 0, nall::Keyboard::LeftBracket)
|
|
||||||
map(0x001b, 0, nall::Keyboard::RightBracket)
|
|
||||||
map(0x002b, 0, nall::Keyboard::Backslash)
|
|
||||||
map(0x0027, 0, nall::Keyboard::Semicolon)
|
|
||||||
map(0x0028, 0, nall::Keyboard::Apostrophe)
|
|
||||||
map(0x0033, 0, nall::Keyboard::Comma)
|
|
||||||
map(0x0034, 0, nall::Keyboard::Period)
|
|
||||||
map(0x0035, 0, nall::Keyboard::Slash)
|
|
||||||
|
|
||||||
map(0x004f, 0, nall::Keyboard::Keypad1)
|
|
||||||
map(0x0050, 0, nall::Keyboard::Keypad2)
|
|
||||||
map(0x0051, 0, nall::Keyboard::Keypad3)
|
|
||||||
map(0x004b, 0, nall::Keyboard::Keypad4)
|
|
||||||
map(0x004c, 0, nall::Keyboard::Keypad5)
|
|
||||||
map(0x004d, 0, nall::Keyboard::Keypad6)
|
|
||||||
map(0x0047, 0, nall::Keyboard::Keypad7)
|
|
||||||
map(0x0048, 0, nall::Keyboard::Keypad8)
|
|
||||||
map(0x0049, 0, nall::Keyboard::Keypad9)
|
|
||||||
map(0x0052, 0, nall::Keyboard::Keypad0)
|
|
||||||
|
|
||||||
map(0x0053, 0, nall::Keyboard::Point)
|
|
||||||
map(0x001c, 2, nall::Keyboard::Enter)
|
|
||||||
map(0x004e, 0, nall::Keyboard::Add)
|
|
||||||
map(0x004a, 0, nall::Keyboard::Subtract)
|
|
||||||
map(0x0037, 0, nall::Keyboard::Multiply)
|
|
||||||
map(0x0035, 2, nall::Keyboard::Divide)
|
|
||||||
|
|
||||||
map(0x0045, 0, nall::Keyboard::NumLock)
|
|
||||||
map(0x003a, 0, nall::Keyboard::CapsLock)
|
|
||||||
|
|
||||||
//Pause signals 0x1d:4 + 0x45:0, whereas NumLock signals only 0x45:0.
|
|
||||||
//this makes it impractical to detect both Pause+NumLock independently.
|
|
||||||
//workaround: always detect Pause; detect NumLock only when Pause is released.
|
|
||||||
if(state[nall::Keyboard::Pause]) state[nall::Keyboard::NumLock] = false;
|
|
||||||
|
|
||||||
map(0x0048, 2, nall::Keyboard::Up)
|
|
||||||
map(0x0050, 2, nall::Keyboard::Down)
|
|
||||||
map(0x004b, 2, nall::Keyboard::Left)
|
|
||||||
map(0x004d, 2, nall::Keyboard::Right)
|
|
||||||
|
|
||||||
map(0x000f, 0, nall::Keyboard::Tab)
|
|
||||||
map(0x001c, 0, nall::Keyboard::Return)
|
|
||||||
map(0x0039, 0, nall::Keyboard::Spacebar)
|
|
||||||
map(0x005d, 2, nall::Keyboard::Menu)
|
|
||||||
|
|
||||||
//merge left and right modifiers to one ID
|
|
||||||
if(code == 0x002a && flags == 0) state[nall::Keyboard::Shift] = 1; //left shift
|
|
||||||
if(code == 0x002a && flags == 1) state[nall::Keyboard::Shift] = 0;
|
|
||||||
if(code == 0x0036 && flags == 0) state[nall::Keyboard::Shift] = 1; //right shift
|
|
||||||
if(code == 0x0036 && flags == 1) state[nall::Keyboard::Shift] = 0;
|
|
||||||
|
|
||||||
if(code == 0x001d && flags == 0) state[nall::Keyboard::Control] = 1; //left control
|
|
||||||
if(code == 0x001d && flags == 1) state[nall::Keyboard::Control] = 0;
|
|
||||||
if(code == 0x001d && flags == 2) state[nall::Keyboard::Control] = 1; //right control
|
|
||||||
if(code == 0x001d && flags == 3) state[nall::Keyboard::Control] = 0;
|
|
||||||
|
|
||||||
if(code == 0x0038 && flags == 0) state[nall::Keyboard::Alt] = 1; //left alt
|
|
||||||
if(code == 0x0038 && flags == 1) state[nall::Keyboard::Alt] = 0;
|
|
||||||
if(code == 0x0038 && flags == 2) state[nall::Keyboard::Alt] = 1; //right alt
|
|
||||||
if(code == 0x0038 && flags == 3) state[nall::Keyboard::Alt] = 0;
|
|
||||||
|
|
||||||
if(code == 0x005b && flags == 2) state[nall::Keyboard::Super] = 1; //left super
|
|
||||||
if(code == 0x005b && flags == 3) state[nall::Keyboard::Super] = 0;
|
|
||||||
if(code == 0x005c && flags == 2) state[nall::Keyboard::Super] = 1; //right super
|
|
||||||
if(code == 0x005c && flags == 3) state[nall::Keyboard::Super] = 0;
|
|
||||||
#undef map
|
|
||||||
}
|
|
||||||
|
|
||||||
Keyboard() {
|
|
||||||
for(unsigned i = 0; i < nall::Keyboard::Size; i++) state[i] = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Mouse : Device {
|
|
||||||
signed xDistance;
|
|
||||||
signed yDistance;
|
|
||||||
signed zDistance;
|
|
||||||
unsigned buttonState;
|
|
||||||
|
|
||||||
void sync() {
|
|
||||||
xDistance = 0;
|
|
||||||
yDistance = 0;
|
|
||||||
zDistance = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void update(RAWINPUT* input) {
|
|
||||||
if((input->data.mouse.usFlags & 1) == MOUSE_MOVE_RELATIVE) {
|
|
||||||
xDistance += input->data.mouse.lLastX;
|
|
||||||
yDistance += input->data.mouse.lLastY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) buttonState |= 1 << 0;
|
|
||||||
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP ) buttonState &=~ 1 << 0;
|
|
||||||
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) buttonState |= 1 << 2; //swap middle and right buttons,
|
|
||||||
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP ) buttonState &=~ 1 << 2; //for consistency with Linux:
|
|
||||||
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) buttonState |= 1 << 1; //left = 0, middle = 1, right = 2
|
|
||||||
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP ) buttonState &=~ 1 << 1;
|
|
||||||
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) buttonState |= 1 << 3;
|
|
||||||
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP ) buttonState &=~ 1 << 3;
|
|
||||||
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) buttonState |= 1 << 4;
|
|
||||||
if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP ) buttonState &=~ 1 << 4;
|
|
||||||
|
|
||||||
if(input->data.mouse.usButtonFlags & RI_MOUSE_WHEEL) {
|
|
||||||
zDistance += (int16_t)input->data.mouse.usButtonData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Mouse() {
|
|
||||||
xDistance = yDistance = zDistance = 0;
|
|
||||||
buttonState = 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//keep track of gamepads for the sole purpose of distinguishing XInput devices
|
|
||||||
//from all other devices. this is necessary, as DirectInput does not provide
|
|
||||||
//a way to retrieve the necessary RIDI_DEVICENAME string.
|
|
||||||
struct Gamepad : Device {
|
|
||||||
bool isXInputDevice;
|
|
||||||
uint16_t vendorId;
|
|
||||||
uint16_t productId;
|
|
||||||
};
|
|
||||||
|
|
||||||
vector<Keyboard> lkeyboard;
|
|
||||||
vector<Mouse> lmouse;
|
|
||||||
vector<Gamepad> lgamepad;
|
|
||||||
|
|
||||||
LRESULT window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
|
||||||
if(msg == WM_INPUT) {
|
|
||||||
unsigned size = 0;
|
|
||||||
GetRawInputData((HRAWINPUT)lparam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
|
|
||||||
RAWINPUT *input = new RAWINPUT[size];
|
|
||||||
GetRawInputData((HRAWINPUT)lparam, RID_INPUT, input, &size, sizeof(RAWINPUTHEADER));
|
|
||||||
WaitForSingleObject(mutex, INFINITE);
|
|
||||||
|
|
||||||
if(input->header.dwType == RIM_TYPEKEYBOARD) {
|
|
||||||
for(unsigned i = 0; i < lkeyboard.size(); i++) {
|
|
||||||
if(input->header.hDevice == lkeyboard(i).handle) {
|
|
||||||
lkeyboard(i).update(input);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if(input->header.dwType == RIM_TYPEMOUSE) {
|
|
||||||
for(unsigned i = 0; i < lmouse.size(); i++) {
|
|
||||||
if(input->header.hDevice == lmouse(i).handle) {
|
|
||||||
lmouse(i).update(input);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ReleaseMutex(mutex);
|
|
||||||
//allow propogation of WM_INPUT message
|
|
||||||
LRESULT result = DefRawInputProc(&input, size, sizeof(RAWINPUTHEADER));
|
|
||||||
delete[] input;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DefWindowProc(hwnd, msg, wparam, lparam);
|
|
||||||
}
|
|
||||||
|
|
||||||
//this is used to sort device IDs
|
|
||||||
struct DevicePool {
|
|
||||||
HANDLE handle;
|
|
||||||
wchar_t name[4096];
|
|
||||||
bool operator<(const DevicePool &pool) const { return wcscmp(name, pool.name) < 0; }
|
|
||||||
};
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
//create an invisible window to act as a sink, capturing all WM_INPUT messages
|
|
||||||
WNDCLASS wc;
|
|
||||||
wc.cbClsExtra = 0;
|
|
||||||
wc.cbWndExtra = 0;
|
|
||||||
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
|
|
||||||
wc.hCursor = LoadCursor(0, IDC_ARROW);
|
|
||||||
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
|
|
||||||
wc.hInstance = GetModuleHandle(0);
|
|
||||||
wc.lpfnWndProc = RawInputWindowProc;
|
|
||||||
wc.lpszClassName = L"RawInputClass";
|
|
||||||
wc.lpszMenuName = 0;
|
|
||||||
wc.style = CS_VREDRAW | CS_HREDRAW;
|
|
||||||
RegisterClass(&wc);
|
|
||||||
|
|
||||||
hwnd = CreateWindow(L"RawInputClass", L"RawInputClass", WS_POPUP,
|
|
||||||
0, 0, 64, 64, 0, 0, GetModuleHandle(0), 0);
|
|
||||||
|
|
||||||
//enumerate all HID devices
|
|
||||||
unsigned devices = 0;
|
|
||||||
GetRawInputDeviceList(NULL, &devices, sizeof(RAWINPUTDEVICELIST));
|
|
||||||
RAWINPUTDEVICELIST *list = new RAWINPUTDEVICELIST[devices];
|
|
||||||
GetRawInputDeviceList(list, &devices, sizeof(RAWINPUTDEVICELIST));
|
|
||||||
|
|
||||||
//sort all devices by name. this has two important properties:
|
|
||||||
//1) it consistently orders peripherals, so mapped IDs remain constant
|
|
||||||
//2) it sorts the virtual keyboard and mouse to the bottom of the list
|
|
||||||
// (real devices start with \\?\HID#, virtual with \\?\Root#)
|
|
||||||
DevicePool pool[devices];
|
|
||||||
for(unsigned i = 0; i < devices; i++) {
|
|
||||||
pool[i].handle = list[i].hDevice;
|
|
||||||
unsigned size = sizeof(pool[i].name) - 1;
|
|
||||||
GetRawInputDeviceInfo(list[i].hDevice, RIDI_DEVICENAME, &pool[i].name, &size);
|
|
||||||
}
|
|
||||||
nall::sort(pool, devices);
|
|
||||||
delete[] list;
|
|
||||||
|
|
||||||
for(unsigned i = 0; i < devices; i++) {
|
|
||||||
RID_DEVICE_INFO info;
|
|
||||||
info.cbSize = sizeof(RID_DEVICE_INFO);
|
|
||||||
|
|
||||||
unsigned size = info.cbSize;
|
|
||||||
GetRawInputDeviceInfo(pool[i].handle, RIDI_DEVICEINFO, &info, &size);
|
|
||||||
|
|
||||||
if(info.dwType == RIM_TYPEKEYBOARD) {
|
|
||||||
unsigned n = lkeyboard.size();
|
|
||||||
lkeyboard(n).handle = pool[i].handle;
|
|
||||||
} else if(info.dwType == RIM_TYPEMOUSE) {
|
|
||||||
unsigned n = lmouse.size();
|
|
||||||
lmouse(n).handle = pool[i].handle;
|
|
||||||
} else if(info.dwType == RIM_TYPEHID) {
|
|
||||||
//if this is a gamepad or joystick device ...
|
|
||||||
if(info.hid.usUsagePage == 1 && (info.hid.usUsage == 4 || info.hid.usUsage == 5)) {
|
|
||||||
//... then cache device information for later use
|
|
||||||
unsigned n = lgamepad.size();
|
|
||||||
lgamepad(n).handle = pool[i].handle;
|
|
||||||
lgamepad(n).vendorId = (uint16_t)info.hid.dwVendorId;
|
|
||||||
lgamepad(n).productId = (uint16_t)info.hid.dwProductId;
|
|
||||||
|
|
||||||
//per MSDN: XInput devices have "IG_" in their device strings,
|
|
||||||
//which is how they should be identified.
|
|
||||||
string p = (const char*)utf8_t(pool[i].name);
|
|
||||||
if(auto position = strpos(p, "IG_")) {
|
|
||||||
lgamepad(n).isXInputDevice = true;
|
|
||||||
} else {
|
|
||||||
lgamepad(n).isXInputDevice = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RAWINPUTDEVICE device[2];
|
|
||||||
//capture all keyboard input
|
|
||||||
device[0].usUsagePage = 1;
|
|
||||||
device[0].usUsage = 6;
|
|
||||||
device[0].dwFlags = RIDEV_INPUTSINK;
|
|
||||||
device[0].hwndTarget = hwnd;
|
|
||||||
//capture all mouse input
|
|
||||||
device[1].usUsagePage = 1;
|
|
||||||
device[1].usUsage = 2;
|
|
||||||
device[1].dwFlags = RIDEV_INPUTSINK;
|
|
||||||
device[1].hwndTarget = hwnd;
|
|
||||||
RegisterRawInputDevices(device, 2, sizeof(RAWINPUTDEVICE));
|
|
||||||
|
|
||||||
WaitForSingleObject(mutex, INFINITE);
|
|
||||||
ready = true;
|
|
||||||
ReleaseMutex(mutex);
|
|
||||||
|
|
||||||
while(true) {
|
|
||||||
MSG msg;
|
|
||||||
GetMessage(&msg, hwnd, 0, 0);
|
|
||||||
TranslateMessage(&msg);
|
|
||||||
DispatchMessage(&msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
RawInput() : initialized(false), ready(false) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static RawInput rawinput;
|
|
||||||
|
|
||||||
DWORD WINAPI RawInputThreadProc(void*) {
|
|
||||||
return rawinput.main();
|
|
||||||
}
|
|
||||||
|
|
||||||
LRESULT CALLBACK RawInputWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
|
||||||
return rawinput.window_proc(hwnd, msg, wparam, lparam);
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL CALLBACK DirectInput_EnumJoypadsCallback(const DIDEVICEINSTANCE*, void*);
|
|
||||||
static BOOL CALLBACK DirectInput_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE*, void*);
|
|
||||||
static BOOL CALLBACK DirectInput_EnumJoypadFeedbacksCallback(const DIDEVICEOBJECTINSTANCE*, void*);
|
|
||||||
|
|
||||||
struct DirectInput {
|
|
||||||
HWND handle;
|
|
||||||
LPDIRECTINPUT8 context;
|
|
||||||
struct Gamepad {
|
|
||||||
LPDIRECTINPUTDEVICE8 handle;
|
|
||||||
|
|
||||||
int16_t hat[4];
|
|
||||||
int16_t axis[6];
|
|
||||||
bool button[128];
|
|
||||||
LPDIRECTINPUTEFFECT effect = nullptr;
|
|
||||||
|
|
||||||
void poll(DIJOYSTATE2& state) {
|
|
||||||
//POV hats
|
|
||||||
for(unsigned n = 0; n < 4; n++) {
|
|
||||||
hat[n] = Joypad::HatCenter;
|
|
||||||
|
|
||||||
//POV value is in clockwise-hundredth degree units
|
|
||||||
unsigned pov = state.rgdwPOV[n];
|
|
||||||
|
|
||||||
//some drivers report a centered POV hat as -1U, others as 65535U.
|
|
||||||
//>= 36000 will match both, as well as invalid ranges.
|
|
||||||
if(pov >= 36000) continue;
|
|
||||||
|
|
||||||
if(pov >= 31500 || pov <= 4500) hat[n] |= Joypad::HatUp;
|
|
||||||
if(pov >= 4500 && pov <= 13500) hat[n] |= Joypad::HatRight;
|
|
||||||
if(pov >= 13500 && pov <= 22500) hat[n] |= Joypad::HatDown;
|
|
||||||
if(pov >= 22500 && pov <= 31500) hat[n] |= Joypad::HatLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
//axes
|
|
||||||
axis[0] = state.lX;
|
|
||||||
axis[1] = state.lY;
|
|
||||||
axis[2] = state.lZ;
|
|
||||||
axis[3] = state.lRx;
|
|
||||||
axis[4] = state.lRy;
|
|
||||||
axis[5] = state.lRz;
|
|
||||||
|
|
||||||
//buttons
|
|
||||||
for(unsigned n = 0; n < 128; n++) {
|
|
||||||
button[n] = (bool)state.rgbButtons[n];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Gamepad() {
|
|
||||||
handle = 0;
|
|
||||||
for(unsigned n = 0; n < 4; n++) hat[n] = Joypad::HatCenter;
|
|
||||||
for(unsigned n = 0; n < 6; n++) axis[n] = 0;
|
|
||||||
for(unsigned n = 0; n < 128; n++) button[n] = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
vector<Gamepad> lgamepad;
|
|
||||||
|
|
||||||
void poll() {
|
|
||||||
for(unsigned i = 0; i < lgamepad.size(); i++) {
|
|
||||||
if(FAILED(lgamepad(i).handle->Poll())) {
|
|
||||||
lgamepad(i).handle->Acquire();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
DIJOYSTATE2 state;
|
|
||||||
lgamepad(i).handle->GetDeviceState(sizeof(DIJOYSTATE2), &state);
|
|
||||||
lgamepad(i).poll(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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++) {
|
|
||||||
uint32_t guid = MAKELONG(rawinput.lgamepad(i).vendorId, rawinput.lgamepad(i).productId);
|
|
||||||
if(guid == instance->guidProduct.Data1) {
|
|
||||||
if(rawinput.lgamepad(i).isXInputDevice == true) {
|
|
||||||
return DIENUM_CONTINUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(FAILED(context->CreateDevice(instance->guidInstance, &device, 0))) {
|
|
||||||
return DIENUM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 initAxis(const DIDEVICEOBJECTINSTANCE* instance) {
|
|
||||||
DIPROPRANGE range;
|
|
||||||
range.diph.dwSize = sizeof(DIPROPRANGE);
|
|
||||||
range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
|
||||||
range.diph.dwHow = DIPH_BYID;
|
|
||||||
range.diph.dwObj = instance->dwType;
|
|
||||||
range.lMin = -32768;
|
|
||||||
range.lMax = +32767;
|
|
||||||
device->SetProperty(DIPROP_RANGE, &range.diph);
|
|
||||||
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);
|
|
||||||
context->EnumDevices(DI8DEVCLASS_GAMECTRL, DirectInput_EnumJoypadsCallback, (void*)this, DIEDFL_ATTACHEDONLY);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
LPDIRECTINPUTDEVICE8 device;
|
|
||||||
unsigned forceFeedbackAxes;
|
|
||||||
};
|
|
||||||
|
|
||||||
BOOL CALLBACK DirectInput_EnumJoypadsCallback(const DIDEVICEINSTANCE* instance, void* p) {
|
|
||||||
return ((DirectInput*)p)->initJoypad(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL CALLBACK DirectInput_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, void* p) {
|
|
||||||
return ((DirectInput*)p)->initAxis(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 Settings {
|
|
||||||
HWND handle;
|
|
||||||
} 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 = (HWND)any_cast<uintptr_t>(value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool acquire() {
|
|
||||||
acquire_mouse = true;
|
|
||||||
if(cursor_visible == true) {
|
|
||||||
ShowCursor(cursor_visible = false);
|
|
||||||
}
|
|
||||||
return acquired();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool unacquire() {
|
|
||||||
acquire_mouse = false;
|
|
||||||
ReleaseCapture();
|
|
||||||
ClipCursor(NULL);
|
|
||||||
if(cursor_visible == false) {
|
|
||||||
ShowCursor(cursor_visible = true);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool acquired() {
|
|
||||||
if(acquire_mouse == true) {
|
|
||||||
SetFocus(settings.handle);
|
|
||||||
SetCapture(settings.handle);
|
|
||||||
RECT rc;
|
|
||||||
GetWindowRect(settings.handle, &rc);
|
|
||||||
ClipCursor(&rc);
|
|
||||||
}
|
|
||||||
return GetCapture() == settings.handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool poll(int16_t* table) {
|
|
||||||
memset(table, 0, Scancode::Limit * sizeof(int16_t));
|
|
||||||
|
|
||||||
WaitForSingleObject(rawinput.mutex, INFINITE);
|
|
||||||
|
|
||||||
//=========
|
|
||||||
//Keyboards
|
|
||||||
//=========
|
|
||||||
for(unsigned i = 0; i < min(rawinput.lkeyboard.size(), (unsigned)Keyboard::Count); i++) {
|
|
||||||
for(unsigned n = 0; n < nall::Keyboard::Size; n++) {
|
|
||||||
//using keyboard(0)|= instead of keyboard(i)= merges all keyboards to KB0
|
|
||||||
//this is done to favor ease of mapping over flexibility (eg share laptop+USB keyboard mapping)
|
|
||||||
table[keyboard(0).key(n)] |= rawinput.lkeyboard(i).state[n];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//====
|
|
||||||
//Mice
|
|
||||||
//====
|
|
||||||
for(unsigned i = 0; i < min(rawinput.lmouse.size(), (unsigned)Mouse::Count); i++) {
|
|
||||||
table[mouse(i).axis(0)] = rawinput.lmouse(i).xDistance;
|
|
||||||
table[mouse(i).axis(1)] = rawinput.lmouse(i).yDistance;
|
|
||||||
table[mouse(i).axis(2)] = rawinput.lmouse(i).zDistance;
|
|
||||||
|
|
||||||
for(unsigned n = 0; n < min(5U, (unsigned)Mouse::Buttons); n++) {
|
|
||||||
table[mouse(i).button(n)] = (bool)(rawinput.lmouse(i).buttonState & (1 << n));
|
|
||||||
}
|
|
||||||
|
|
||||||
rawinput.lmouse(i).sync();
|
|
||||||
}
|
|
||||||
|
|
||||||
ReleaseMutex(rawinput.mutex);
|
|
||||||
|
|
||||||
//==================
|
|
||||||
//XInput controllers
|
|
||||||
//==================
|
|
||||||
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;
|
|
||||||
|
|
||||||
for(unsigned hat = 0; hat < min(4U, (unsigned)Joypad::Hats); hat++) {
|
|
||||||
table[joypad(joy).hat(hat)] = dinput.lgamepad(i).hat[hat];
|
|
||||||
}
|
|
||||||
|
|
||||||
for(unsigned axis = 0; axis < min(6U, (unsigned)Joypad::Axes); axis++) {
|
|
||||||
table[joypad(joy).axis(axis)] = dinput.lgamepad(i).axis[axis];
|
|
||||||
}
|
|
||||||
|
|
||||||
for(unsigned button = 0; button < min(128U, (unsigned)Joypad::Buttons); button++) {
|
|
||||||
table[joypad(joy).button(button)] = dinput.lgamepad(i).button[button];
|
|
||||||
}
|
|
||||||
|
|
||||||
joy++;
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
rawinput.initialized = true;
|
|
||||||
rawinput.mutex = CreateMutex(NULL, FALSE, NULL);
|
|
||||||
CreateThread(NULL, 0, RawInputThreadProc, 0, 0, NULL);
|
|
||||||
|
|
||||||
//RawInput device calibration needs to finish before initializing DirectInput;
|
|
||||||
//as it needs device GUIDs to distinguish XInput devices from ordinary joypads.
|
|
||||||
bool ready = false;
|
|
||||||
do {
|
|
||||||
Sleep(10);
|
|
||||||
WaitForSingleObject(rawinput.mutex, INFINITE);
|
|
||||||
ready = rawinput.ready;
|
|
||||||
ReleaseMutex(rawinput.mutex);
|
|
||||||
} while(ready == false);
|
|
||||||
}
|
|
||||||
|
|
||||||
xinput.init();
|
|
||||||
dinput.init(settings.handle);
|
|
||||||
|
|
||||||
acquire_mouse = false;
|
|
||||||
cursor_visible = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void term() {
|
|
||||||
unacquire();
|
|
||||||
xinput.term();
|
|
||||||
dinput.term();
|
|
||||||
}
|
|
||||||
|
|
||||||
pInputRaw() {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
DeclareInput(Raw)
|
|
||||||
|
|
||||||
};
|
|
|
@ -51,14 +51,16 @@ struct pInputSDL {
|
||||||
return xlibMouse.acquired();
|
return xlibMouse.acquired();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool poll(int16_t* table) {
|
vector<HID::Device*> poll() {
|
||||||
xlibKeyboard.poll(table);
|
vector<HID::Device*> devices;
|
||||||
xlibMouse.poll(table);
|
xlibKeyboard.poll(devices);
|
||||||
sdl.poll(table);
|
xlibMouse.poll(devices);
|
||||||
return true;
|
sdl.poll(devices);
|
||||||
|
return devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rumble(unsigned id, bool enable) {
|
bool rumble(uint64_t id, bool enable) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool init() {
|
bool init() {
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
#ifndef RUBY_INPUT_SHARED_RAWINPUT
|
||||||
|
#define RUBY_INPUT_SHARED_RAWINPUT
|
||||||
|
|
||||||
|
namespace ruby {
|
||||||
|
|
||||||
|
LRESULT CALLBACK RawInputWindowProc(HWND, UINT, WPARAM, LPARAM);
|
||||||
|
|
||||||
|
struct RawInput {
|
||||||
|
HANDLE mutex;
|
||||||
|
HWND hwnd;
|
||||||
|
bool ready = false;
|
||||||
|
bool initialized = false;
|
||||||
|
function<void (RAWINPUT*)> updateKeyboard;
|
||||||
|
function<void (RAWINPUT*)> updateMouse;
|
||||||
|
|
||||||
|
struct Device {
|
||||||
|
HANDLE handle;
|
||||||
|
string path;
|
||||||
|
enum class Type : unsigned { Keyboard, Mouse, Joypad } type;
|
||||||
|
uint16_t vendorID = 0;
|
||||||
|
uint16_t productID = 0;
|
||||||
|
bool isXInputDevice = false;
|
||||||
|
};
|
||||||
|
vector<Device> devices;
|
||||||
|
|
||||||
|
optional<Device&> find(uint16_t vendorID, uint16_t productID) {
|
||||||
|
for(auto& device : devices) {
|
||||||
|
if(device.vendorID == vendorID && device.productID == productID) return {true, device};
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scanDevices() {
|
||||||
|
devices.reset();
|
||||||
|
|
||||||
|
unsigned deviceCount = 0;
|
||||||
|
GetRawInputDeviceList(NULL, &deviceCount, sizeof(RAWINPUTDEVICELIST));
|
||||||
|
RAWINPUTDEVICELIST* list = new RAWINPUTDEVICELIST[deviceCount];
|
||||||
|
GetRawInputDeviceList(list, &deviceCount, sizeof(RAWINPUTDEVICELIST));
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < deviceCount; n++) {
|
||||||
|
wchar_t path[4096];
|
||||||
|
unsigned size = sizeof(path) - 1;
|
||||||
|
GetRawInputDeviceInfo(list[n].hDevice, RIDI_DEVICENAME, &path, &size);
|
||||||
|
|
||||||
|
RID_DEVICE_INFO info;
|
||||||
|
info.cbSize = size = sizeof(RID_DEVICE_INFO);
|
||||||
|
GetRawInputDeviceInfo(list[n].hDevice, RIDI_DEVICEINFO, &info, &size);
|
||||||
|
|
||||||
|
Device device;
|
||||||
|
device.path = (const char*)utf8_t(path);
|
||||||
|
device.handle = list[n].hDevice;
|
||||||
|
|
||||||
|
if(info.dwType == RIM_TYPEKEYBOARD) {
|
||||||
|
device.type = Device::Type::Keyboard;
|
||||||
|
device.vendorID = 0;
|
||||||
|
device.productID = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(info.dwType == RIM_TYPEMOUSE) {
|
||||||
|
device.type = Device::Type::Mouse;
|
||||||
|
device.vendorID = 0;
|
||||||
|
device.productID = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(info.dwType == RIM_TYPEHID) {
|
||||||
|
//verify that this is a joypad device
|
||||||
|
if(info.hid.usUsagePage != 1 || (info.hid.usUsage != 4 && info.hid.usUsage != 5)) continue;
|
||||||
|
|
||||||
|
device.type = Device::Type::Joypad;
|
||||||
|
device.vendorID = info.hid.dwVendorId;
|
||||||
|
device.productID = info.hid.dwProductId;
|
||||||
|
if(device.path.find("IG_")) device.isXInputDevice = true; //"IG_" is only found inside XInput device paths
|
||||||
|
}
|
||||||
|
|
||||||
|
devices.append(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] list;
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||||
|
if(msg != WM_INPUT) return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||||
|
|
||||||
|
unsigned size = 0;
|
||||||
|
GetRawInputData((HRAWINPUT)lparam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
|
||||||
|
RAWINPUT* input = new RAWINPUT[size];
|
||||||
|
GetRawInputData((HRAWINPUT)lparam, RID_INPUT, input, &size, sizeof(RAWINPUTHEADER));
|
||||||
|
WaitForSingleObject(mutex, INFINITE);
|
||||||
|
|
||||||
|
if(input->header.dwType == RIM_TYPEKEYBOARD) {
|
||||||
|
if(updateKeyboard) updateKeyboard(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(input->header.dwType == RIM_TYPEMOUSE) {
|
||||||
|
if(updateMouse) updateMouse(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseMutex(mutex);
|
||||||
|
LRESULT result = DefRawInputProc(&input, size, sizeof(RAWINPUTHEADER));
|
||||||
|
delete[] input;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
WNDCLASS wc;
|
||||||
|
wc.cbClsExtra = 0;
|
||||||
|
wc.cbWndExtra = 0;
|
||||||
|
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
|
||||||
|
wc.hCursor = LoadCursor(0, IDC_ARROW);
|
||||||
|
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
|
||||||
|
wc.hInstance = GetModuleHandle(0);
|
||||||
|
wc.lpfnWndProc = RawInputWindowProc;
|
||||||
|
wc.lpszClassName = L"RawInputClass";
|
||||||
|
wc.lpszMenuName = 0;
|
||||||
|
wc.style = CS_VREDRAW | CS_HREDRAW;
|
||||||
|
RegisterClass(&wc);
|
||||||
|
|
||||||
|
hwnd = CreateWindow(L"RawInputClass", L"RawInputClass", WS_POPUP, 0, 0, 64, 64, 0, 0, GetModuleHandle(0), 0);
|
||||||
|
|
||||||
|
scanDevices();
|
||||||
|
|
||||||
|
RAWINPUTDEVICE device[2];
|
||||||
|
//capture all keyboard input
|
||||||
|
device[0].usUsagePage = 1;
|
||||||
|
device[0].usUsage = 6;
|
||||||
|
device[0].dwFlags = RIDEV_INPUTSINK;
|
||||||
|
device[0].hwndTarget = hwnd;
|
||||||
|
//capture all mouse input
|
||||||
|
device[1].usUsagePage = 1;
|
||||||
|
device[1].usUsage = 2;
|
||||||
|
device[1].dwFlags = RIDEV_INPUTSINK;
|
||||||
|
device[1].hwndTarget = hwnd;
|
||||||
|
RegisterRawInputDevices(device, 2, sizeof(RAWINPUTDEVICE));
|
||||||
|
|
||||||
|
WaitForSingleObject(mutex, INFINITE);
|
||||||
|
ready = true;
|
||||||
|
ReleaseMutex(mutex);
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
MSG msg;
|
||||||
|
GetMessage(&msg, hwnd, 0, 0);
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessage(&msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static RawInput rawinput;
|
||||||
|
|
||||||
|
DWORD WINAPI RawInputThreadProc(void*) {
|
||||||
|
rawinput.main();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CALLBACK RawInputWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||||
|
return rawinput.windowProc(hwnd, msg, wparam, lparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -65,15 +65,8 @@ struct pInputUdev {
|
||||||
return devices;
|
return devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool poll(int16_t* table) {
|
bool rumble(uint64_t id, bool enable) {
|
||||||
xlibKeyboard.poll(table);
|
return udev.rumble(id, enable);
|
||||||
xlibMouse.poll(table);
|
|
||||||
udev.poll(table);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void rumble(uint64_t id, bool enable) {
|
|
||||||
udev.rumble(id, enable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool init() {
|
bool init() {
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
#include <xinput.h>
|
||||||
|
#define DIRECTINPUT_VERSION 0x0800
|
||||||
|
#include <dinput.h>
|
||||||
|
|
||||||
|
#include "shared/rawinput.cpp"
|
||||||
|
#include "keyboard/rawinput.cpp"
|
||||||
|
#include "mouse/rawinput.cpp"
|
||||||
|
#include "joypad/xinput.cpp"
|
||||||
|
#include "joypad/directinput.cpp"
|
||||||
|
|
||||||
|
namespace ruby {
|
||||||
|
|
||||||
|
struct pInputWindows {
|
||||||
|
InputKeyboardRawInput rawinputKeyboard;
|
||||||
|
InputMouseRawInput rawinputMouse;
|
||||||
|
InputJoypadXInput xinput;
|
||||||
|
InputJoypadDirectInput directinput;
|
||||||
|
|
||||||
|
LPDIRECTINPUT8 directinputContext = nullptr;
|
||||||
|
|
||||||
|
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 rawinputMouse.acquire();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unacquire() {
|
||||||
|
return rawinputMouse.unacquire();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool acquired() {
|
||||||
|
return rawinputMouse.acquired();
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<HID::Device*> poll() {
|
||||||
|
vector<HID::Device*> devices;
|
||||||
|
rawinputKeyboard.poll(devices);
|
||||||
|
rawinputMouse.poll(devices);
|
||||||
|
xinput.poll(devices);
|
||||||
|
directinput.poll(devices);
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rumble(uint64_t id, bool enable) {
|
||||||
|
if(xinput.rumble(id, enable)) return true;
|
||||||
|
if(directinput.rumble(id, enable)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool init() {
|
||||||
|
if(rawinput.initialized == false) {
|
||||||
|
rawinput.initialized = true;
|
||||||
|
rawinput.mutex = CreateMutex(NULL, FALSE, NULL);
|
||||||
|
CreateThread(NULL, 0, RawInputThreadProc, 0, 0, NULL);
|
||||||
|
|
||||||
|
do {
|
||||||
|
Sleep(1);
|
||||||
|
WaitForSingleObject(rawinput.mutex, INFINITE);
|
||||||
|
ReleaseMutex(rawinput.mutex);
|
||||||
|
} while(rawinput.ready == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectInput8Create(GetModuleHandle(0), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&directinputContext, 0);
|
||||||
|
if(directinputContext == nullptr) return false;
|
||||||
|
|
||||||
|
if(rawinputKeyboard.init() == false) return false;
|
||||||
|
if(rawinputMouse.init(settings.handle) == false) return false;
|
||||||
|
bool xinputAvailable = xinput.init();
|
||||||
|
if(directinput.init(settings.handle, directinputContext, xinputAvailable) == false) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void term() {
|
||||||
|
rawinputKeyboard.term();
|
||||||
|
rawinputMouse.term();
|
||||||
|
xinput.term();
|
||||||
|
directinput.term();
|
||||||
|
|
||||||
|
if(directinputContext) { directinputContext->Release(); directinputContext = nullptr; }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
DeclareInput(Windows)
|
||||||
|
|
||||||
|
}
|
|
@ -49,13 +49,15 @@ struct pInputXlib {
|
||||||
return xlibMouse.acquired();
|
return xlibMouse.acquired();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool poll(int16_t* table) {
|
vector<HID::Device*> poll() {
|
||||||
xlibKeyboard.poll(table);
|
vector<HID::Device*> devices;
|
||||||
xlibMouse.poll(table);
|
xlibKeyboard.poll(devices);
|
||||||
return true;
|
xlibMouse.poll(devices);
|
||||||
|
return devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rumble(unsigned id, bool enable) {
|
bool rumble(uint64_t id, bool enable) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool init() {
|
bool init() {
|
||||||
|
|
|
@ -375,12 +375,8 @@ void InputInterface::driver(const char* driver) {
|
||||||
|
|
||||||
if(0);
|
if(0);
|
||||||
|
|
||||||
#ifdef INPUT_DIRECTINPUT
|
#ifdef INPUT_WINDOWS
|
||||||
else if(!strcmp(driver, "DirectInput")) p = new InputDI();
|
else if(!strcmp(driver, "Windows")) p = new InputWindows();
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef INPUT_RAWINPUT
|
|
||||||
else if(!strcmp(driver, "RawInput")) p = new InputRaw();
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef INPUT_CARBON
|
#ifdef INPUT_CARBON
|
||||||
|
@ -403,10 +399,8 @@ void InputInterface::driver(const char* driver) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* InputInterface::optimalDriver() {
|
const char* InputInterface::optimalDriver() {
|
||||||
#if defined(INPUT_RAWINPUT)
|
#if defined(INPUT_WINDOWS)
|
||||||
return "RawInput";
|
return "Windows";
|
||||||
#elif defined(INPUT_DIRECTINPUT)
|
|
||||||
return "DirectInput";
|
|
||||||
|
|
||||||
#elif defined(INPUT_CARBON)
|
#elif defined(INPUT_CARBON)
|
||||||
return "Carbon";
|
return "Carbon";
|
||||||
|
@ -424,10 +418,8 @@ const char* InputInterface::optimalDriver() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* InputInterface::safestDriver() {
|
const char* InputInterface::safestDriver() {
|
||||||
#if defined(INPUT_RAWINPUT)
|
#if defined(INPUT_WINDOWS)
|
||||||
return "RawInput";
|
return "Windows";
|
||||||
#elif defined(INPUT_DIRECTINPUT)
|
|
||||||
return "DirectInput";
|
|
||||||
|
|
||||||
#elif defined(INPUT_CARBON)
|
#elif defined(INPUT_CARBON)
|
||||||
return "Carbon";
|
return "Carbon";
|
||||||
|
@ -449,12 +441,8 @@ const char* InputInterface::availableDrivers() {
|
||||||
|
|
||||||
//Windows
|
//Windows
|
||||||
|
|
||||||
#if defined(INPUT_RAWINPUT)
|
#if defined(INPUT_WINDOWS)
|
||||||
"RawInput;"
|
"Windows;"
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(INPUT_DIRECTINPUT)
|
|
||||||
"DirectInput;"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//OS X
|
//OS X
|
||||||
|
@ -500,8 +488,7 @@ bool InputInterface::acquire() { return p ? p->acquire() : false; }
|
||||||
bool InputInterface::unacquire() { return p ? p->unacquire() : false; }
|
bool InputInterface::unacquire() { return p ? p->unacquire() : false; }
|
||||||
bool InputInterface::acquired() { return p ? p->acquired() : false; }
|
bool InputInterface::acquired() { return p ? p->acquired() : false; }
|
||||||
vector<HID::Device*> InputInterface::poll() { return p ? p->poll() : vector<HID::Device*>(); }
|
vector<HID::Device*> InputInterface::poll() { return p ? p->poll() : vector<HID::Device*>(); }
|
||||||
bool InputInterface::poll(int16_t* table) { return p ? p->poll(table) : false; }
|
bool InputInterface::rumble(uint64_t id, bool enable) { return p ? p->rumble(id, enable) : false; }
|
||||||
void InputInterface::rumble(uint64_t id, bool enable) { if(p) return p->rumble(id, enable); }
|
|
||||||
InputInterface::InputInterface() : p(nullptr) {}
|
InputInterface::InputInterface() : p(nullptr) {}
|
||||||
InputInterface::~InputInterface() { term(); }
|
InputInterface::~InputInterface() { term(); }
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#define RUBY_H
|
#define RUBY_H
|
||||||
|
|
||||||
#include <nall/nall.hpp>
|
#include <nall/nall.hpp>
|
||||||
#include <nall/input.hpp>
|
|
||||||
|
|
||||||
namespace ruby {
|
namespace ruby {
|
||||||
|
|
||||||
|
@ -81,8 +80,7 @@ struct InputInterface {
|
||||||
bool acquired();
|
bool acquired();
|
||||||
|
|
||||||
nall::vector<nall::HID::Device*> poll();
|
nall::vector<nall::HID::Device*> poll();
|
||||||
bool poll(int16_t* table);
|
bool rumble(uint64_t id, bool enable);
|
||||||
void rumble(uint64_t id, bool enable);
|
|
||||||
|
|
||||||
InputInterface();
|
InputInterface();
|
||||||
~InputInterface();
|
~InputInterface();
|
||||||
|
|
|
@ -66,8 +66,8 @@ struct pVideoGLX : OpenGL {
|
||||||
if(depth > DefaultDepth(display, screen)) return false;
|
if(depth > DefaultDepth(display, screen)) return false;
|
||||||
|
|
||||||
switch(depth) {
|
switch(depth) {
|
||||||
case 24: format = GL_RGBA8; inputFormat = GL_UNSIGNED_INT_8_8_8_8_REV; break;
|
case 24: format = GL_RGBA8; break;
|
||||||
case 30: format = GL_RGB10_A2; inputFormat = GL_UNSIGNED_INT_2_10_10_10_REV; break;
|
case 30: format = GL_RGB10_A2; break;
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,6 @@ void OpenGL::shader(const char* pathname) {
|
||||||
for(auto& program : programs) program.release();
|
for(auto& program : programs) program.release();
|
||||||
programs.reset();
|
programs.reset();
|
||||||
|
|
||||||
for(auto& frame : frames) glDeleteTextures(1, &frame.texture);
|
|
||||||
frames.reset();
|
|
||||||
|
|
||||||
settings.reset();
|
settings.reset();
|
||||||
|
|
||||||
format = GL_RGBA8;
|
format = GL_RGBA8;
|
||||||
|
@ -13,6 +10,7 @@ void OpenGL::shader(const char* pathname) {
|
||||||
absoluteWidth = 0, absoluteHeight = 0;
|
absoluteWidth = 0, absoluteHeight = 0;
|
||||||
relativeWidth = 0, relativeHeight = 0;
|
relativeWidth = 0, relativeHeight = 0;
|
||||||
|
|
||||||
|
unsigned historySize = 0;
|
||||||
if(pathname) {
|
if(pathname) {
|
||||||
auto document = Markup::Document(file::read({pathname, "manifest.bml"}));
|
auto document = Markup::Document(file::read({pathname, "manifest.bml"}));
|
||||||
|
|
||||||
|
@ -20,27 +18,51 @@ void OpenGL::shader(const char* pathname) {
|
||||||
settings.insert({node.name, node.text()});
|
settings.insert({node.name, node.text()});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(auto& node : document["input"]) {
|
||||||
|
if(node.name == "history") historySize = node.decimal();
|
||||||
|
if(node.name == "format") format = glrFormat(node.text());
|
||||||
|
if(node.name == "filter") filter = glrFilter(node.text());
|
||||||
|
if(node.name == "wrap") wrap = glrWrap(node.text());
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto& node : document["output"]) {
|
||||||
|
string text = node.text();
|
||||||
|
if(node.name == "width") {
|
||||||
|
if(text.endsWith("%")) relativeWidth = real(text.rtrim<1>("%")) / 100.0;
|
||||||
|
else absoluteWidth = decimal(text);
|
||||||
|
}
|
||||||
|
if(node.name == "height") {
|
||||||
|
if(text.endsWith("%")) relativeHeight = real(text.rtrim<1>("%")) / 100.0;
|
||||||
|
else absoluteHeight = decimal(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for(auto& node : document.find("program")) {
|
for(auto& node : document.find("program")) {
|
||||||
unsigned n = programs.size();
|
unsigned n = programs.size();
|
||||||
programs(n).bind(this, node, pathname);
|
programs(n).bind(this, node, pathname);
|
||||||
}
|
}
|
||||||
|
|
||||||
bind(document, pathname);
|
|
||||||
OpenGLProgram::bind(this, document["output"], pathname);
|
|
||||||
} else {
|
|
||||||
//no shader; assign default values
|
|
||||||
history.length = 0;
|
|
||||||
history.format = GL_RGBA8;
|
|
||||||
history.filter = GL_LINEAR;
|
|
||||||
history.wrap = GL_CLAMP_TO_BORDER;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//changing shaders may change input format, which requires the input texture to be recreated
|
||||||
|
if(texture) { glDeleteTextures(1, &texture); texture = 0; }
|
||||||
|
glGenTextures(1, &texture);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, getFormat(), getType(), buffer);
|
||||||
|
allocateHistory(historySize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGL::bind(const Markup::Node& node, const string& pathname) {
|
void OpenGL::allocateHistory(unsigned size) {
|
||||||
history.length = node["history/frames"].decimal();
|
for(auto& frame : history) glDeleteTextures(1, &frame.texture);
|
||||||
if(node["history/format"].exists()) history.format = glrFormat(node["history/format"].text());
|
history.reset();
|
||||||
if(node["history/filter"].exists()) history.filter = glrFilter(node["history/filter"].text());
|
while(size--) {
|
||||||
if(node["history/wrap"].exists()) history.wrap = glrWrap(node["history/wrap"].text());
|
OpenGLTexture frame;
|
||||||
|
frame.filter = filter;
|
||||||
|
frame.wrap = wrap;
|
||||||
|
glGenTextures(1, &frame.texture);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, frame.texture);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, format, frame.width = width, frame.height = height, 0, getFormat(), getType(), buffer);
|
||||||
|
history.append(frame);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGL::lock(uint32_t*& data, unsigned& pitch) {
|
bool OpenGL::lock(uint32_t*& data, unsigned& pitch) {
|
||||||
|
@ -64,20 +86,9 @@ void OpenGL::clear() {
|
||||||
void OpenGL::refresh() {
|
void OpenGL::refresh() {
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
//frame[] must always contain max# of previous frames: allocate them now, so first few frames can use them
|
|
||||||
while(frames.size() < history.length) {
|
|
||||||
OpenGLTexture frame;
|
|
||||||
glGenTextures(1, &frame.texture);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, frame.texture);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, frame.format = history.format, frame.width = width, frame.height = height, 0, GL_BGRA, inputFormat, buffer);
|
|
||||||
frame.filter = history.filter;
|
|
||||||
frame.wrap = history.wrap;
|
|
||||||
frames.append(frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
glBindTexture(GL_TEXTURE_2D, texture);
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_BGRA, inputFormat, buffer);
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, getFormat(), getType(), buffer);
|
||||||
|
|
||||||
struct Source {
|
struct Source {
|
||||||
GLuint texture;
|
GLuint texture;
|
||||||
|
@ -85,31 +96,29 @@ void OpenGL::refresh() {
|
||||||
GLuint filter, wrap;
|
GLuint filter, wrap;
|
||||||
};
|
};
|
||||||
vector<Source> sources;
|
vector<Source> sources;
|
||||||
|
sources.prepend({texture, width, height, filter, wrap});
|
||||||
unsigned sourceWidth = width, sourceHeight = height;
|
|
||||||
sources.prepend({texture, sourceWidth, sourceHeight, filter, wrap});
|
|
||||||
|
|
||||||
for(auto& p : programs) {
|
for(auto& p : programs) {
|
||||||
unsigned targetWidth = p.absoluteWidth ? p.absoluteWidth : outputWidth;
|
unsigned targetWidth = p.absoluteWidth ? p.absoluteWidth : outputWidth;
|
||||||
unsigned targetHeight = p.absoluteHeight ? p.absoluteHeight : outputHeight;
|
unsigned targetHeight = p.absoluteHeight ? p.absoluteHeight : outputHeight;
|
||||||
if(p.relativeWidth) targetWidth = sourceWidth * p.relativeWidth;
|
if(p.relativeWidth) targetWidth = sources[0].width * p.relativeWidth;
|
||||||
if(p.relativeHeight) targetHeight = sourceHeight * p.relativeHeight;
|
if(p.relativeHeight) targetHeight = sources[0].height * p.relativeHeight;
|
||||||
|
|
||||||
p.size(targetWidth, targetHeight);
|
p.size(targetWidth, targetHeight);
|
||||||
glUseProgram(p.program);
|
glUseProgram(p.program);
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, p.framebuffer);
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, p.framebuffer);
|
||||||
|
|
||||||
glrUniform1i("phase", p.phase);
|
glrUniform1i("phase", p.phase);
|
||||||
glrUniform1i("frameLength", frames.size());
|
glrUniform1i("historyLength", history.size());
|
||||||
glrUniform1i("sourceLength", sources.size());
|
glrUniform1i("sourceLength", sources.size());
|
||||||
glrUniform1i("pixmapLength", p.pixmaps.size());
|
glrUniform1i("pixmapLength", p.pixmaps.size());
|
||||||
glrUniform4f("targetSize", targetWidth, targetHeight, 1.0 / targetWidth, 1.0 / targetHeight);
|
glrUniform4f("targetSize", targetWidth, targetHeight, 1.0 / targetWidth, 1.0 / targetHeight);
|
||||||
glrUniform4f("outputSize", outputWidth, outputHeight, 1.0 / outputWidth, 1.0 / outputHeight);
|
glrUniform4f("outputSize", outputWidth, outputHeight, 1.0 / outputWidth, 1.0 / outputHeight);
|
||||||
|
|
||||||
unsigned aid = 0;
|
unsigned aid = 0;
|
||||||
for(auto& frame : frames) {
|
for(auto& frame : history) {
|
||||||
glrUniform1i({"frame[", aid, "]"}, aid);
|
glrUniform1i({"history[", aid, "]"}, aid);
|
||||||
glrUniform4f({"frameSize[", aid, "]"}, frame.width, frame.height, 1.0 / frame.width, 1.0 / frame.height);
|
glrUniform4f({"historySize[", aid, "]"}, frame.width, frame.height, 1.0 / frame.width, 1.0 / frame.height);
|
||||||
glActiveTexture(GL_TEXTURE0 + (aid++));
|
glActiveTexture(GL_TEXTURE0 + (aid++));
|
||||||
glBindTexture(GL_TEXTURE_2D, frame.texture);
|
glBindTexture(GL_TEXTURE_2D, frame.texture);
|
||||||
glrParameters(frame.filter, frame.wrap);
|
glrParameters(frame.filter, frame.wrap);
|
||||||
|
@ -134,19 +143,18 @@ void OpenGL::refresh() {
|
||||||
}
|
}
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glrParameters(p.filter, p.wrap);
|
glrParameters(sources[0].filter, sources[0].wrap);
|
||||||
p.render(sourceWidth, sourceHeight, targetWidth, targetHeight);
|
p.render(sources[0].width, sources[0].height, targetWidth, targetHeight);
|
||||||
glBindTexture(GL_TEXTURE_2D, p.texture);
|
glBindTexture(GL_TEXTURE_2D, p.texture);
|
||||||
|
|
||||||
p.phase = (p.phase + 1) % p.modulo;
|
p.phase = (p.phase + 1) % p.modulo;
|
||||||
sourceWidth = p.width, sourceHeight = p.height;
|
sources.prepend({p.texture, p.width, p.height, p.filter, p.wrap});
|
||||||
sources.prepend({p.texture, sourceWidth, sourceHeight, p.filter, p.wrap});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned targetWidth = absoluteWidth ? absoluteWidth : outputWidth;
|
unsigned targetWidth = absoluteWidth ? absoluteWidth : outputWidth;
|
||||||
unsigned targetHeight = absoluteHeight ? absoluteHeight : outputHeight;
|
unsigned targetHeight = absoluteHeight ? absoluteHeight : outputHeight;
|
||||||
if(relativeWidth) targetWidth = sourceWidth * relativeWidth;
|
if(relativeWidth) targetWidth = sources[0].width * relativeWidth;
|
||||||
if(relativeHeight) targetHeight = sourceHeight * relativeHeight;
|
if(relativeHeight) targetHeight = sources[0].height * relativeHeight;
|
||||||
|
|
||||||
glUseProgram(program);
|
glUseProgram(program);
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
@ -155,20 +163,20 @@ void OpenGL::refresh() {
|
||||||
glrUniform4f("targetSize", targetWidth, targetHeight, 1.0 / targetWidth, 1.0 / targetHeight);
|
glrUniform4f("targetSize", targetWidth, targetHeight, 1.0 / targetWidth, 1.0 / targetHeight);
|
||||||
glrUniform4f("outputSize", outputWidth, outputHeight, 1.0 / outputWidth, 1.0 / outputHeight);
|
glrUniform4f("outputSize", outputWidth, outputHeight, 1.0 / outputWidth, 1.0 / outputHeight);
|
||||||
|
|
||||||
glrParameters(filter, wrap);
|
glrParameters(sources[0].filter, sources[0].wrap);
|
||||||
render(sourceWidth, sourceHeight, outputWidth, outputHeight);
|
render(sources[0].width, sources[0].height, outputWidth, outputHeight);
|
||||||
|
|
||||||
if(frames.size() > 0) {
|
if(history.size() > 0) {
|
||||||
OpenGLTexture frame = frames.take();
|
OpenGLTexture frame = history.takeLast();
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, frame.texture);
|
glBindTexture(GL_TEXTURE_2D, frame.texture);
|
||||||
if(width == frame.width && height == frame.height) {
|
if(width == frame.width && height == frame.height) {
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_BGRA, inputFormat, buffer);
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, getFormat(), getType(), buffer);
|
||||||
} else {
|
} else {
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, frame.format, frame.width = width, frame.height = height, 0, GL_BGRA, inputFormat, buffer);
|
glTexImage2D(GL_TEXTURE_2D, 0, format, frame.width = width, frame.height = height, 0, getFormat(), getType(), buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
frames.prepend(frame);
|
history.prepend(frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,9 @@ struct OpenGLTexture {
|
||||||
GLuint format = GL_RGBA8;
|
GLuint format = GL_RGBA8;
|
||||||
GLuint filter = GL_LINEAR;
|
GLuint filter = GL_LINEAR;
|
||||||
GLuint wrap = GL_CLAMP_TO_BORDER;
|
GLuint wrap = GL_CLAMP_TO_BORDER;
|
||||||
|
|
||||||
|
GLuint getFormat() const;
|
||||||
|
GLuint getType() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct OpenGLSurface : OpenGLTexture {
|
struct OpenGLSurface : OpenGLTexture {
|
||||||
|
@ -47,7 +50,6 @@ struct OpenGLSurface : OpenGLTexture {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct OpenGLProgram : OpenGLSurface {
|
struct OpenGLProgram : OpenGLSurface {
|
||||||
//configuration
|
|
||||||
unsigned phase = 0; //frame counter
|
unsigned phase = 0; //frame counter
|
||||||
unsigned modulo = 0; //frame counter modulus
|
unsigned modulo = 0; //frame counter modulus
|
||||||
unsigned absoluteWidth = 0;
|
unsigned absoluteWidth = 0;
|
||||||
|
@ -63,18 +65,9 @@ struct OpenGLProgram : OpenGLSurface {
|
||||||
|
|
||||||
struct OpenGL : OpenGLProgram {
|
struct OpenGL : OpenGLProgram {
|
||||||
vector<OpenGLProgram> programs;
|
vector<OpenGLProgram> programs;
|
||||||
vector<OpenGLTexture> frames;
|
vector<OpenGLTexture> history;
|
||||||
struct History {
|
|
||||||
unsigned length = 0;
|
|
||||||
GLuint format = GL_RGBA8;
|
|
||||||
GLuint filter = GL_LINEAR;
|
|
||||||
GLuint wrap = GL_CLAMP_TO_BORDER;
|
|
||||||
} history;
|
|
||||||
|
|
||||||
GLuint inputFormat = GL_UNSIGNED_INT_8_8_8_8_REV;
|
|
||||||
unsigned outputWidth = 0;
|
unsigned outputWidth = 0;
|
||||||
unsigned outputHeight = 0;
|
unsigned outputHeight = 0;
|
||||||
|
|
||||||
struct Setting {
|
struct Setting {
|
||||||
string name;
|
string name;
|
||||||
string value;
|
string value;
|
||||||
|
@ -87,7 +80,7 @@ struct OpenGL : OpenGLProgram {
|
||||||
set<Setting> settings;
|
set<Setting> settings;
|
||||||
|
|
||||||
void shader(const char* pathname);
|
void shader(const char* pathname);
|
||||||
void bind(const Markup::Node& node, const string& pathname);
|
void allocateHistory(unsigned size);
|
||||||
bool lock(uint32_t*& data, unsigned& pitch);
|
bool lock(uint32_t*& data, unsigned& pitch);
|
||||||
void clear();
|
void clear();
|
||||||
void refresh();
|
void refresh();
|
||||||
|
@ -95,6 +88,7 @@ struct OpenGL : OpenGLProgram {
|
||||||
void term();
|
void term();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#include "texture.hpp"
|
||||||
#include "surface.hpp"
|
#include "surface.hpp"
|
||||||
#include "program.hpp"
|
#include "program.hpp"
|
||||||
#include "main.hpp"
|
#include "main.hpp"
|
||||||
|
|
|
@ -9,7 +9,6 @@ void OpenGLProgram::bind(OpenGL* instance, const Markup::Node& node, const strin
|
||||||
if(h.endsWith("%")) relativeHeight = real(h.rtrim<1>("%")) / 100.0;
|
if(h.endsWith("%")) relativeHeight = real(h.rtrim<1>("%")) / 100.0;
|
||||||
else absoluteHeight = decimal(h);
|
else absoluteHeight = decimal(h);
|
||||||
|
|
||||||
if(node.name != "program") return;
|
|
||||||
format = glrFormat(node["format"].text());
|
format = glrFormat(node["format"].text());
|
||||||
|
|
||||||
program = glCreateProgram();
|
program = glCreateProgram();
|
||||||
|
@ -62,8 +61,8 @@ void OpenGLProgram::bind(OpenGL* instance, const Markup::Node& node, const strin
|
||||||
unsigned w = glrSize(image.width), h = glrSize(image.height);
|
unsigned w = glrSize(image.width), h = glrSize(image.height);
|
||||||
uint32_t* buffer = new uint32_t[w * h]();
|
uint32_t* buffer = new uint32_t[w * h]();
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
glBindTexture(GL_TEXTURE_2D, texture);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, pixmaps(n).format, w, h, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
|
glTexImage2D(GL_TEXTURE_2D, 0, pixmaps(n).format, w, h, 0, pixmaps(n).getFormat(), pixmaps(n).getType(), buffer);
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image.width, image.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, image.data);
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image.width, image.height, getFormat(), getType(), image.data);
|
||||||
delete[] buffer;
|
delete[] buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ void OpenGLSurface::size(unsigned w, unsigned h) {
|
||||||
buffer = new uint32_t[w * h]();
|
buffer = new uint32_t[w * h]();
|
||||||
glGenTextures(1, &texture);
|
glGenTextures(1, &texture);
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
glBindTexture(GL_TEXTURE_2D, texture);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
|
glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, getFormat(), getType(), buffer);
|
||||||
|
|
||||||
if(framebuffer) {
|
if(framebuffer) {
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
GLuint OpenGLTexture::getFormat() const {
|
||||||
|
if(format == GL_R32I) return GL_RED_INTEGER;
|
||||||
|
if(format == GL_R32UI) return GL_RED_INTEGER;
|
||||||
|
return GL_BGRA;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint OpenGLTexture::getType() const {
|
||||||
|
if(format == GL_R32I) return GL_UNSIGNED_INT;
|
||||||
|
if(format == GL_R32UI) return GL_UNSIGNED_INT;
|
||||||
|
if(format == GL_RGB10_A2) return GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||||
|
return GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||||
|
}
|
|
@ -4,6 +4,8 @@ static unsigned glrSize(unsigned size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static GLuint glrFormat(const string& format) {
|
static GLuint glrFormat(const string& format) {
|
||||||
|
if(format == "r32i" ) return GL_R32I;
|
||||||
|
if(format == "r32ui" ) return GL_R32UI;
|
||||||
if(format == "rgba8" ) return GL_RGBA8;
|
if(format == "rgba8" ) return GL_RGBA8;
|
||||||
if(format == "rgb10a2") return GL_RGB10_A2;
|
if(format == "rgb10a2") return GL_RGB10_A2;
|
||||||
if(format == "rgba12" ) return GL_RGBA12;
|
if(format == "rgba12" ) return GL_RGBA12;
|
||||||
|
|
|
@ -4,33 +4,45 @@
|
||||||
namespace ruby {
|
namespace ruby {
|
||||||
|
|
||||||
struct pVideoXShm {
|
struct pVideoXShm {
|
||||||
struct {
|
struct Device {
|
||||||
Display* display;
|
Display* display = nullptr;
|
||||||
int screen;
|
int screen;
|
||||||
Visual *visual;
|
|
||||||
int depth;
|
int depth;
|
||||||
|
Visual* visual = nullptr;
|
||||||
Window window;
|
Window window;
|
||||||
|
|
||||||
XShmSegmentInfo shmInfo;
|
XShmSegmentInfo shmInfo;
|
||||||
XImage* image;
|
XImage* image = nullptr;
|
||||||
uint32_t* buffer;
|
uint32_t* buffer = nullptr;
|
||||||
unsigned width, height;
|
unsigned width, height;
|
||||||
} device;
|
} device;
|
||||||
|
|
||||||
struct {
|
struct Settings {
|
||||||
uintptr_t handle;
|
uintptr_t handle;
|
||||||
|
unsigned depth = 24;
|
||||||
|
|
||||||
uint32_t* buffer;
|
uint32_t* buffer = nullptr;
|
||||||
unsigned width, height;
|
unsigned width, height;
|
||||||
} settings;
|
} settings;
|
||||||
|
|
||||||
|
struct Color {
|
||||||
|
unsigned depth;
|
||||||
|
unsigned shift;
|
||||||
|
|
||||||
|
unsigned idepth;
|
||||||
|
unsigned ishift;
|
||||||
|
} red, green, blue;
|
||||||
|
|
||||||
bool cap(const string& name) {
|
bool cap(const string& name) {
|
||||||
if(name == Video::Handle) return true;
|
if(name == Video::Handle) return true;
|
||||||
|
if(name == Video::Depth) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
any get(const string& name) {
|
any get(const string& name) {
|
||||||
if(name == Video::Handle) return settings.handle;
|
if(name == Video::Handle) return settings.handle;
|
||||||
|
if(name == Video::Depth) return settings.depth;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool set(const string& name, const any& value) {
|
bool set(const string& name, const any& value) {
|
||||||
|
@ -38,6 +50,9 @@ struct pVideoXShm {
|
||||||
settings.handle = any_cast<uintptr_t>(value);
|
settings.handle = any_cast<uintptr_t>(value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if(name == Video::Depth) {
|
||||||
|
return setDepth(any_cast<unsigned>(value));
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +81,7 @@ struct pVideoXShm {
|
||||||
if(settings.buffer == nullptr) return;
|
if(settings.buffer == nullptr) return;
|
||||||
size();
|
size();
|
||||||
|
|
||||||
float xRatio = (float)settings.width / (float)device.width;
|
float xRatio = (float)settings.width / (float)device.width;
|
||||||
float yRatio = (float)settings.height / (float)device.height;
|
float yRatio = (float)settings.height / (float)device.height;
|
||||||
float yStep = 0;
|
float yStep = 0;
|
||||||
for(unsigned y = 0; y < device.height; y++) {
|
for(unsigned y = 0; y < device.height; y++) {
|
||||||
|
@ -77,7 +92,12 @@ struct pVideoXShm {
|
||||||
for(unsigned x = 0; x < device.width; x++) {
|
for(unsigned x = 0; x < device.width; x++) {
|
||||||
uint32_t color = sp[(unsigned)xStep];
|
uint32_t color = sp[(unsigned)xStep];
|
||||||
xStep += xRatio;
|
xStep += xRatio;
|
||||||
*dp++ = ((color >> 20) & 0x000003ff) | ((color) & 0x000ffc00) | ((color << 20) & 0x3ff00000);
|
unsigned r = (color >> red.ishift ) & ((1 << red.idepth ) - 1);
|
||||||
|
unsigned g = (color >> green.ishift) & ((1 << green.idepth) - 1);
|
||||||
|
unsigned b = (color >> blue.ishift ) & ((1 << blue.idepth ) - 1);
|
||||||
|
*dp++ = image::normalize(r, red.idepth, red.depth ) << red.shift
|
||||||
|
| image::normalize(g, green.idepth, green.depth) << green.shift
|
||||||
|
| image::normalize(b, blue.idepth, blue.depth ) << blue.shift;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,15 +113,36 @@ struct pVideoXShm {
|
||||||
bool init() {
|
bool init() {
|
||||||
device.display = XOpenDisplay(0);
|
device.display = XOpenDisplay(0);
|
||||||
device.screen = DefaultScreen(device.display);
|
device.screen = DefaultScreen(device.display);
|
||||||
device.visual = DefaultVisual(device.display, device.screen);
|
|
||||||
device.depth = DefaultDepth(device.display, device.screen);
|
|
||||||
|
|
||||||
XSetWindowAttributes attributes;
|
XWindowAttributes getAttributes;
|
||||||
attributes.border_pixel = 0;
|
XGetWindowAttributes(device.display, (Window)settings.handle, &getAttributes);
|
||||||
|
device.depth = getAttributes.depth;
|
||||||
|
device.visual = getAttributes.visual;
|
||||||
|
unsigned visualID = XVisualIDFromVisual(device.visual);
|
||||||
|
|
||||||
|
XVisualInfo visualTemplate = {0};
|
||||||
|
visualTemplate.screen = device.screen;
|
||||||
|
visualTemplate.depth = device.depth;
|
||||||
|
int visualsMatched = 0;
|
||||||
|
XVisualInfo* visualList = XGetVisualInfo(device.display, VisualScreenMask | VisualDepthMask, &visualTemplate, &visualsMatched);
|
||||||
|
for(unsigned n = 0; n < visualsMatched; n++) {
|
||||||
|
auto& v = visualList[n];
|
||||||
|
if(v.visualid == visualID) {
|
||||||
|
red.depth = bit::count(v.red_mask), red.shift = bit::first(v.red_mask);
|
||||||
|
green.depth = bit::count(v.green_mask), green.shift = bit::first(v.green_mask);
|
||||||
|
blue.depth = bit::count(v.blue_mask), blue.shift = bit::first(v.blue_mask);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XFree(visualList);
|
||||||
|
setDepth(settings.depth);
|
||||||
|
|
||||||
|
XSetWindowAttributes setAttributes = {0};
|
||||||
|
setAttributes.border_pixel = 0;
|
||||||
device.window = XCreateWindow(device.display, (Window)settings.handle,
|
device.window = XCreateWindow(device.display, (Window)settings.handle,
|
||||||
0, 0, 256, 256,
|
0, 0, 256, 256, 0,
|
||||||
0, device.depth, InputOutput, device.visual,
|
getAttributes.depth, InputOutput, getAttributes.visual,
|
||||||
CWBorderPixel, &attributes
|
CWBorderPixel, &setAttributes
|
||||||
);
|
);
|
||||||
XSetWindowBackground(device.display, device.window, 0);
|
XSetWindowBackground(device.display, device.window, 0);
|
||||||
XMapWindow(device.display, device.window);
|
XMapWindow(device.display, device.window);
|
||||||
|
@ -118,12 +159,8 @@ struct pVideoXShm {
|
||||||
|
|
||||||
void term() {
|
void term() {
|
||||||
free();
|
free();
|
||||||
}
|
|
||||||
|
|
||||||
pVideoXShm() {
|
if(device.display) { XCloseDisplay(device.display); device.display = nullptr; }
|
||||||
device.buffer = nullptr;
|
|
||||||
|
|
||||||
settings.buffer = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~pVideoXShm() {
|
~pVideoXShm() {
|
||||||
|
@ -131,6 +168,26 @@ struct pVideoXShm {
|
||||||
}
|
}
|
||||||
|
|
||||||
//internal:
|
//internal:
|
||||||
|
bool setDepth(unsigned depth) {
|
||||||
|
if(depth == 24) {
|
||||||
|
settings.depth = 24;
|
||||||
|
red.idepth = 8, red.ishift = 16;
|
||||||
|
green.idepth = 8, green.ishift = 8;
|
||||||
|
blue.idepth = 8, blue.ishift = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(depth == 30) {
|
||||||
|
settings.depth = 30;
|
||||||
|
red.idepth = 10, red.ishift = 20;
|
||||||
|
green.idepth = 10, green.ishift = 10;
|
||||||
|
blue.idepth = 10, blue.ishift = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool size() {
|
bool size() {
|
||||||
XWindowAttributes windowAttributes;
|
XWindowAttributes windowAttributes;
|
||||||
XGetWindowAttributes(device.display, settings.handle, &windowAttributes);
|
XGetWindowAttributes(device.display, settings.handle, &windowAttributes);
|
||||||
|
|
|
@ -17,7 +17,7 @@ ui_objects += $(if $(call streq,$(platform),windows),resource)
|
||||||
ifeq ($(platform),windows)
|
ifeq ($(platform),windows)
|
||||||
ruby := video.direct3d video.wgl video.directdraw video.gdi
|
ruby := video.direct3d video.wgl video.directdraw video.gdi
|
||||||
ruby += audio.directsound audio.xaudio2
|
ruby += audio.directsound audio.xaudio2
|
||||||
ruby += input.rawinput input.directinput
|
ruby += input.windows
|
||||||
else ifeq ($(platform),macosx)
|
else ifeq ($(platform),macosx)
|
||||||
ruby := video.cgl
|
ruby := video.cgl
|
||||||
ruby += audio.openal
|
ruby += audio.openal
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include <nall/config.hpp>
|
#include <nall/config.hpp>
|
||||||
#include <nall/directory.hpp>
|
#include <nall/directory.hpp>
|
||||||
#include <nall/dsp.hpp>
|
#include <nall/dsp.hpp>
|
||||||
#include <nall/input.hpp>
|
#include <nall/hid.hpp>
|
||||||
#include <nall/invoke.hpp>
|
#include <nall/invoke.hpp>
|
||||||
#include <nall/map.hpp>
|
#include <nall/map.hpp>
|
||||||
#include <nall/stream/file.hpp>
|
#include <nall/stream/file.hpp>
|
||||||
|
|
Loading…
Reference in New Issue