mirror of https://github.com/bsnes-emu/bsnes.git
Update to v093r11 release.
byuu says: Changelog: - GBA: SOUND_CTL_H is readable, fixes sound effects in Mario&Luigi Superstar Saga [Cydrak] (note: game is still unplayable due to other bugs) - phoenix/Windows: workaround for Win32 API ListView bug, fixes slot loading behavior - ruby: added udev driver for Linux with rumble support, and added rumble support to existing RawInput driver for XInput and DirectInput - ethos: added new "Rumble" mapping to GBA input assignment, use it to tell higan which controller to rumble (clear it to disable rumble) - GBA: Game Boy Player rumble is now fully emulated - core: added new normalized raw-color palette mode for Display Emulation shaders The way rumble was added to ethos was somewhat hackish. The support doesn't really exist in nall. I need to redesign the entire input system, but that's not a change I want to make so close to a release.
This commit is contained in:
parent
84fab07756
commit
73be2e729c
|
@ -3,7 +3,7 @@
|
|||
|
||||
namespace Emulator {
|
||||
static const char Name[] = "higan";
|
||||
static const char Version[] = "093.10";
|
||||
static const char Version[] = "093.11";
|
||||
static const char Author[] = "byuu";
|
||||
static const char License[] = "GPLv3";
|
||||
static const char Website[] = "http://byuu.org/";
|
||||
|
@ -27,6 +27,7 @@ namespace Emulator {
|
|||
#include <nall/file.hpp>
|
||||
#include <nall/function.hpp>
|
||||
#include <nall/http.hpp>
|
||||
#include <nall/image.hpp>
|
||||
#include <nall/invoke.hpp>
|
||||
#include <nall/priority-queue.hpp>
|
||||
#include <nall/property.hpp>
|
||||
|
|
|
@ -31,7 +31,7 @@ struct Interface {
|
|||
string name;
|
||||
struct Input {
|
||||
unsigned id;
|
||||
unsigned type; //0 = digital, 1 = analog (relative), 2 = analog (absolute)
|
||||
unsigned type; //0 = digital, 1 = analog (relative), 2 = analog (absolute), 3 = rumble
|
||||
string name;
|
||||
unsigned guid;
|
||||
};
|
||||
|
@ -50,10 +50,11 @@ struct Interface {
|
|||
virtual void loadRequest(unsigned, string, string) {}
|
||||
virtual void loadRequest(unsigned, string) {}
|
||||
virtual void saveRequest(unsigned, string) {}
|
||||
virtual uint32_t videoColor(unsigned, uint16_t, uint16_t, uint16_t) { return 0u; }
|
||||
virtual uint32_t videoColor(unsigned, uint16_t, uint16_t, uint16_t, uint16_t) { return 0u; }
|
||||
virtual void videoRefresh(const uint32_t*, const uint32_t*, unsigned, unsigned, unsigned) {}
|
||||
virtual void audioSample(int16_t, int16_t) {}
|
||||
virtual int16_t inputPoll(unsigned, unsigned, unsigned) { return 0; }
|
||||
virtual void inputRumble(unsigned, unsigned, unsigned, bool) {}
|
||||
virtual unsigned dipSettings(const Markup::Node&) { return 0; }
|
||||
virtual string path(unsigned) { return ""; }
|
||||
virtual string server() { return ""; }
|
||||
|
@ -65,10 +66,11 @@ struct Interface {
|
|||
void loadRequest(unsigned id, string name, string type) { return bind->loadRequest(id, name, type); }
|
||||
void loadRequest(unsigned id, string path) { return bind->loadRequest(id, path); }
|
||||
void saveRequest(unsigned id, string path) { return bind->saveRequest(id, path); }
|
||||
uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue) { return bind->videoColor(source, red, green, blue); }
|
||||
uint32_t videoColor(unsigned source, uint16_t alpha, uint16_t red, uint16_t green, uint16_t blue) { return bind->videoColor(source, alpha, red, green, blue); }
|
||||
void videoRefresh(const uint32_t* palette, const uint32_t* data, unsigned pitch, unsigned width, unsigned height) { return bind->videoRefresh(palette, data, pitch, width, height); }
|
||||
void audioSample(int16_t lsample, int16_t rsample) { return bind->audioSample(lsample, rsample); }
|
||||
int16_t inputPoll(unsigned port, unsigned device, unsigned input) { return bind->inputPoll(port, device, input); }
|
||||
void inputRumble(unsigned port, unsigned device, unsigned input, bool enable) { return bind->inputRumble(port, device, input, enable); }
|
||||
unsigned dipSettings(const Markup::Node& node) { return bind->dipSettings(node); }
|
||||
string path(unsigned group) { return bind->path(group); }
|
||||
string server() { return bind->server(); }
|
||||
|
@ -107,7 +109,7 @@ struct Interface {
|
|||
virtual void cheatSet(const lstring& = lstring{}) {}
|
||||
|
||||
//utility functions
|
||||
enum class PaletteMode : unsigned { None, Standard, Emulation };
|
||||
enum class PaletteMode : unsigned { Literal, Channel, Standard, Emulation };
|
||||
virtual void paletteUpdate(PaletteMode mode) {}
|
||||
|
||||
//debugger functions
|
||||
|
|
|
@ -7,11 +7,21 @@ namespace Famicom {
|
|||
Video video;
|
||||
|
||||
void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
|
||||
for(unsigned n = 0; n < (1 << 9); n++) {
|
||||
switch(mode) {
|
||||
case Emulator::Interface::PaletteMode::None: palette[n] = n; break;
|
||||
case Emulator::Interface::PaletteMode::Standard: palette[n] = generate_color(n, 2.0, 0.0, 1.0, 1.0, 2.2); break;
|
||||
case Emulator::Interface::PaletteMode::Emulation: palette[n] = generate_color(n, 2.0, 0.0, 1.0, 1.0, 1.8); break;
|
||||
for(unsigned color = 0; color < (1 << 9); color++) {
|
||||
if(mode == Emulator::Interface::PaletteMode::Literal) {
|
||||
palette[color] = color;
|
||||
} else if(mode == Emulator::Interface::PaletteMode::Channel) {
|
||||
unsigned emphasis = (color >> 6) & 7;
|
||||
unsigned luma = (color >> 4) & 3;
|
||||
unsigned chroma = (color >> 0) & 15;
|
||||
emphasis = image::normalize(emphasis, 3, 16);
|
||||
luma = image::normalize(luma, 2, 16);
|
||||
chroma = image::normalize(chroma, 4, 16);
|
||||
palette[color] = interface->videoColor(color, 0, emphasis, luma, chroma);
|
||||
} else if(mode == Emulator::Interface::PaletteMode::Standard) {
|
||||
palette[color] = generate_color(color, 2.0, 0.0, 1.0, 1.0, 2.2);
|
||||
} else if(mode == Emulator::Interface::PaletteMode::Emulation) {
|
||||
palette[color] = generate_color(color, 2.0, 0.0, 1.0, 1.0, 1.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +79,7 @@ uint32_t Video::generate_color(
|
|||
unsigned g = 65535.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q);
|
||||
unsigned b = 65535.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q);
|
||||
|
||||
return interface->videoColor(n, uclamp<16>(r), uclamp<16>(g), uclamp<16>(b));
|
||||
return interface->videoColor(n, 0, uclamp<16>(r), uclamp<16>(g), uclamp<16>(b));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,18 +21,28 @@ Video::~Video() {
|
|||
}
|
||||
|
||||
unsigned Video::palette_dmg(unsigned color) const {
|
||||
if(mode == Emulator::Interface::PaletteMode::None) return color;
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Standard) {
|
||||
unsigned L = (3 - color) * 21845;
|
||||
return interface->videoColor(color, L, L, L);
|
||||
if(mode == Emulator::Interface::PaletteMode::Literal) {
|
||||
return color;
|
||||
}
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Channel) {
|
||||
unsigned L = image::normalize(color, 2, 16);
|
||||
return interface->videoColor(color, 0, 0, 0, L);
|
||||
}
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Standard) {
|
||||
unsigned L = image::normalize(3 - color, 2, 16);
|
||||
return interface->videoColor(color, 0, L, L, L);
|
||||
}
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Emulation) {
|
||||
unsigned R = monochrome[color][0];
|
||||
unsigned G = monochrome[color][1];
|
||||
unsigned B = monochrome[color][2];
|
||||
return interface->videoColor(color, 0, R, G, B);
|
||||
}
|
||||
|
||||
return interface->videoColor(color, R, G, B);
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned Video::palette_sgb(unsigned color) const {
|
||||
|
@ -40,19 +50,29 @@ unsigned Video::palette_sgb(unsigned color) const {
|
|||
}
|
||||
|
||||
unsigned Video::palette_cgb(unsigned color) const {
|
||||
if(mode == Emulator::Interface::PaletteMode::None) return color;
|
||||
if(mode == Emulator::Interface::PaletteMode::Literal) {
|
||||
return color;
|
||||
}
|
||||
|
||||
unsigned r = (color >> 0) & 31;
|
||||
unsigned g = (color >> 5) & 31;
|
||||
unsigned b = (color >> 10) & 31;
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Standard) {
|
||||
unsigned R = (r << 11) | (r << 6) | (r << 1) | (r >> 4);
|
||||
unsigned G = (g << 11) | (g << 6) | (g << 1) | (g >> 4);
|
||||
unsigned B = (b << 11) | (b << 6) | (b << 1) | (b >> 4);
|
||||
return interface->videoColor(color, R, G, B);
|
||||
if(mode == Emulator::Interface::PaletteMode::Channel) {
|
||||
r = image::normalize(r, 5, 16);
|
||||
g = image::normalize(g, 5, 16);
|
||||
b = image::normalize(b, 5, 16);
|
||||
return interface->videoColor(color, 0, r, g, b);
|
||||
}
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Standard) {
|
||||
r = image::normalize(r, 5, 16);
|
||||
g = image::normalize(g, 5, 16);
|
||||
b = image::normalize(b, 5, 16);
|
||||
return interface->videoColor(color, 0, r, g, b);
|
||||
}
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Emulation) {
|
||||
unsigned R = (r * 26 + g * 4 + b * 2);
|
||||
unsigned G = ( g * 24 + b * 8);
|
||||
unsigned B = (r * 6 + g * 4 + b * 22);
|
||||
|
@ -65,7 +85,10 @@ unsigned Video::palette_cgb(unsigned color) const {
|
|||
G = G << 6 | G >> 4;
|
||||
B = B << 6 | B >> 4;
|
||||
|
||||
return interface->videoColor(color, R, G, B);
|
||||
return interface->videoColor(color, 0, R, G, B);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define DMG_PALETTE_GREEN
|
||||
|
|
|
@ -45,6 +45,13 @@ uint8 APU::read(uint32 addr) {
|
|||
case 0x04000080: return sequencer.read(0);
|
||||
case 0x04000081: return sequencer.read(1);
|
||||
|
||||
//SOUND_CNT_H
|
||||
case 0x04000082:
|
||||
return (fifo[1].volume << 3) | (fifo[0].volume << 2) | (sequencer.volume << 0);
|
||||
case 0x04000083:
|
||||
return (fifo[1].timer << 6) | (fifo[1].lenable << 5) | (fifo[1].renable << 4)
|
||||
| (fifo[0].timer << 2) | (fifo[0].lenable << 1) | (fifo[0].renable << 0);
|
||||
|
||||
//NR52
|
||||
case 0x04000084: return sequencer.read(2);
|
||||
case 0x04000085: return 0u;
|
||||
|
|
|
@ -129,17 +129,18 @@ Interface::Interface() {
|
|||
|
||||
{
|
||||
Device device{0, ID::Device, "Controller"};
|
||||
device.input.append({0, 0, "A" });
|
||||
device.input.append({1, 0, "B" });
|
||||
device.input.append({2, 0, "Select"});
|
||||
device.input.append({3, 0, "Start" });
|
||||
device.input.append({4, 0, "Right" });
|
||||
device.input.append({5, 0, "Left" });
|
||||
device.input.append({6, 0, "Up" });
|
||||
device.input.append({7, 0, "Down" });
|
||||
device.input.append({8, 0, "R" });
|
||||
device.input.append({9, 0, "L" });
|
||||
device.order = {6, 7, 5, 4, 1, 0, 9, 8, 2, 3};
|
||||
device.input.append({ 0, 0, "A" });
|
||||
device.input.append({ 1, 0, "B" });
|
||||
device.input.append({ 2, 0, "Select"});
|
||||
device.input.append({ 3, 0, "Start" });
|
||||
device.input.append({ 4, 0, "Right" });
|
||||
device.input.append({ 5, 0, "Left" });
|
||||
device.input.append({ 6, 0, "Up" });
|
||||
device.input.append({ 7, 0, "Down" });
|
||||
device.input.append({ 8, 0, "R" });
|
||||
device.input.append({ 9, 0, "L" });
|
||||
device.input.append({10, 3, "Rumble"});
|
||||
device.order = {6, 7, 5, 4, 1, 0, 9, 8, 2, 3, 10};
|
||||
this->device.append(device);
|
||||
}
|
||||
|
||||
|
|
|
@ -54,11 +54,6 @@ void Player::frame() {
|
|||
}
|
||||
cpu.regs.irq.flag.serial = true;
|
||||
}
|
||||
|
||||
if(status.rumble) {
|
||||
//todo: support actual gamepad rumble; for now, color screen red during rumble
|
||||
for(unsigned n = 0; n < 240 * 160; n++) ppu.output[n] &= 0x001f;
|
||||
}
|
||||
}
|
||||
|
||||
optional<uint16> Player::keyinput() {
|
||||
|
@ -87,6 +82,7 @@ void Player::write(uint8 byte, uint2 addr) {
|
|||
|
||||
if(addr == 3 && status.packet == 15) {
|
||||
status.rumble = (status.recv & 0xff) == 0x26; //on = 0x26, off = 0x04
|
||||
interface->inputRumble(0, 0, 10, status.rumble);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ Video video;
|
|||
|
||||
void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
|
||||
for(unsigned color = 0; color < (1 << 15); color++) {
|
||||
if(mode == Emulator::Interface::PaletteMode::None) {
|
||||
if(mode == Emulator::Interface::PaletteMode::Literal) {
|
||||
palette[color] = color;
|
||||
continue;
|
||||
}
|
||||
|
@ -15,11 +15,23 @@ void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
|
|||
unsigned G = (color >> 5) & 31;
|
||||
unsigned R = (color >> 0) & 31;
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Channel) {
|
||||
R = image::normalize(R, 5, 16);
|
||||
G = image::normalize(G, 5, 16);
|
||||
B = image::normalize(B, 5, 16);
|
||||
palette[color] = interface->videoColor(color, 0, R, G, B);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Standard) {
|
||||
R = R << 11 | R << 6 | R << 1 | R >> 4;
|
||||
G = G << 11 | G << 6 | G << 1 | G >> 4;
|
||||
B = B << 11 | B << 6 | B << 1 | B >> 4;
|
||||
} else {
|
||||
R = image::normalize(R, 5, 16);
|
||||
G = image::normalize(G, 5, 16);
|
||||
B = image::normalize(B, 5, 16);
|
||||
palette[color] = interface->videoColor(color, 0, R, G, B);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Emulation) {
|
||||
R = curve[R];
|
||||
G = curve[G];
|
||||
B = curve[B];
|
||||
|
@ -52,12 +64,14 @@ void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
|
|||
G = (((4 * Gr + 2 * Gg + Gb) * 160) >> 14) + 32;
|
||||
B = (((4 * Br + 2 * Bg + Bb) * 160) >> 14) + 32;
|
||||
|
||||
R = R << 8 | R;
|
||||
G = G << 8 | G;
|
||||
B = B << 8 | B;
|
||||
R = image::normalize(R, 8, 16);
|
||||
G = image::normalize(G, 8, 16);
|
||||
B = image::normalize(B, 8, 16);
|
||||
palette[color] = interface->videoColor(color, 0, R, G, B);
|
||||
continue;
|
||||
}
|
||||
|
||||
palette[color] = interface->videoColor(color, R, G, B);
|
||||
palette[color] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ namespace Math {
|
|||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
extern "C" int _fileno(FILE*);
|
||||
__declspec(dllimport) int _fileno(FILE*);
|
||||
|
||||
inline int access(const char* path, int amode) { return _waccess(nall::utf16_t(path), amode); }
|
||||
inline int fileno(FILE* stream) { return _fileno(stream); }
|
||||
|
|
|
@ -2,6 +2,7 @@ namespace phoenix {
|
|||
|
||||
static bool Application_keyboardProc(HWND, UINT, WPARAM, LPARAM);
|
||||
static void Application_processDialogMessage(MSG&);
|
||||
static void Application_processMessageQueue();
|
||||
static LRESULT CALLBACK Application_windowProc(HWND, UINT, WPARAM, LPARAM);
|
||||
|
||||
void pApplication::run() {
|
||||
|
@ -38,13 +39,24 @@ void Application_processDialogMessage(MSG& msg) {
|
|||
|| msg.message == WM_SYSKEYDOWN || msg.message == WM_SYSKEYUP) {
|
||||
if(Application_keyboardProc(msg.hwnd, msg.message, msg.wParam, msg.lParam)) {
|
||||
DispatchMessage(&msg);
|
||||
return;
|
||||
return Application_processMessageQueue();
|
||||
}
|
||||
}
|
||||
|
||||
if(!IsDialogMessage(GetForegroundWindow(), &msg)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
Application_processMessageQueue();
|
||||
}
|
||||
}
|
||||
|
||||
static void Application_processMessageQueue() {
|
||||
while(!messageQueue.empty()) {
|
||||
Message message = messageQueue.takeFirst();
|
||||
if(message.type == Message::Type::ListView_OnActivate) {
|
||||
ListView* listView = (ListView*)message.object;
|
||||
if(listView->onActivate) listView->onActivate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,9 +146,7 @@ static bool Application_keyboardProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM
|
|||
if(dynamic_cast<ListView*>(object)) {
|
||||
ListView& listView = (ListView&)*object;
|
||||
if(wparam == VK_RETURN) {
|
||||
if(listView.state.text.size() && listView.selected()) {
|
||||
if(listView.onActivate) listView.onActivate();
|
||||
}
|
||||
if(listView.selected()) return true; //returning true generates LVN_ITEMACTIVATE message
|
||||
}
|
||||
} else if(dynamic_cast<LineEdit*>(object)) {
|
||||
LineEdit& lineEdit = (LineEdit&)*object;
|
||||
|
|
|
@ -2,6 +2,16 @@ namespace phoenix {
|
|||
|
||||
typedef LRESULT CALLBACK (*WindowProc)(HWND, UINT, WPARAM, LPARAM);
|
||||
|
||||
struct Message {
|
||||
enum class Type : unsigned {
|
||||
ListView_OnActivate,
|
||||
};
|
||||
Type type;
|
||||
Object* object;
|
||||
};
|
||||
|
||||
static vector<Message> messageQueue;
|
||||
|
||||
struct pApplication {
|
||||
static void run();
|
||||
static bool pendingEvents();
|
||||
|
|
|
@ -255,14 +255,7 @@ static LRESULT CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT
|
|||
} else if(!GetParentWidget((Sizable*)object) && window.p.brush) {
|
||||
SetBkColor((HDC)wparam, window.p.brushColor);
|
||||
return (INT_PTR)window.p.brush;
|
||||
}/* else {
|
||||
//this will repaint the background properly, but the foreground isn't always rendered after ...
|
||||
RECT rc;
|
||||
GetClientRect((HWND)lparam, &rc);
|
||||
DrawThemeParentBackground((HWND)lparam, (HDC)wparam, &rc);
|
||||
SetBkMode((HDC)wparam, TRANSPARENT);
|
||||
return (INT_PTR)GetStockBrush(HOLLOW_BRUSH);
|
||||
}*/
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -212,7 +212,9 @@ void pListView::buildImageList() {
|
|||
void pListView::onActivate(LPARAM lparam) {
|
||||
LPNMLISTVIEW nmlistview = (LPNMLISTVIEW)lparam;
|
||||
if(listView.state.text.empty() || !listView.state.selected) return;
|
||||
if(listView.onActivate) listView.onActivate();
|
||||
//LVN_ITEMACTIVATE is not re-entrant until DispatchMessage() completes
|
||||
//if(listView.onActivate) listView.onActivate();
|
||||
messageQueue.append({Message::Type::ListView_OnActivate, (Object*)&listView});
|
||||
}
|
||||
|
||||
void pListView::onChange(LPARAM lparam) {
|
||||
|
@ -228,6 +230,8 @@ void pListView::onChange(LPARAM lparam) {
|
|||
}
|
||||
} else if((nmlistview->uOldState & LVIS_FOCUSED) && !(nmlistview->uNewState & LVIS_FOCUSED)) {
|
||||
lostFocus = true;
|
||||
listView.state.selected = false;
|
||||
listView.state.selection = 0;
|
||||
} else if(!(nmlistview->uOldState & LVIS_SELECTED) && (nmlistview->uNewState & LVIS_SELECTED)) {
|
||||
lostFocus = false;
|
||||
listView.state.selected = true;
|
||||
|
@ -238,6 +242,10 @@ void pListView::onChange(LPARAM lparam) {
|
|||
listView.state.selected = false;
|
||||
listView.state.selection = 0;
|
||||
if(!locked && listView.onChange) listView.onChange();
|
||||
} else if(listView.selected() && ListView_GetSelectedCount(hwnd) == 0) {
|
||||
listView.state.selected = false;
|
||||
listView.state.selection = 0;
|
||||
if(!locked && listView.onChange) listView.onChange();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ 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 .sdl,$(ruby)),`sdl-config --libs`)
|
||||
|
||||
|
|
|
@ -143,6 +143,7 @@ using namespace nall;
|
|||
bool acquired() { return p.acquired(); } \
|
||||
\
|
||||
bool poll(int16_t* table) { return p.poll(table); } \
|
||||
void rumble(unsigned id, bool enable) { return p.rumble(id, enable); } \
|
||||
bool init() { return p.init(); } \
|
||||
void term() { p.term(); } \
|
||||
\
|
||||
|
@ -153,6 +154,10 @@ using namespace nall;
|
|||
pInput##Name& p; \
|
||||
};
|
||||
|
||||
#ifdef INPUT_CARBON
|
||||
#include <ruby/input/carbon.cpp>
|
||||
#endif
|
||||
|
||||
#ifdef INPUT_DIRECTINPUT
|
||||
#include <ruby/input/directinput.cpp>
|
||||
#endif
|
||||
|
@ -161,14 +166,14 @@ using namespace nall;
|
|||
#include <ruby/input/rawinput.cpp>
|
||||
#endif
|
||||
|
||||
#ifdef INPUT_CARBON
|
||||
#include <ruby/input/carbon.cpp>
|
||||
#endif
|
||||
|
||||
#ifdef INPUT_SDL
|
||||
#include <ruby/input/sdl.cpp>
|
||||
#endif
|
||||
|
||||
#ifdef INPUT_UDEV
|
||||
#include <ruby/input/udev.cpp>
|
||||
#endif
|
||||
|
||||
#ifdef INPUT_X
|
||||
#include <ruby/input/x.cpp>
|
||||
#endif
|
||||
|
|
|
@ -3,6 +3,7 @@ struct Input {
|
|||
static const char* KeyboardSupport;
|
||||
static const char* MouseSupport;
|
||||
static const char* JoypadSupport;
|
||||
static const char* JoypadRumbleSupport;
|
||||
|
||||
virtual bool cap(const nall::string& name) { return false; }
|
||||
virtual nall::any get(const nall::string& name) { return false; }
|
||||
|
@ -13,6 +14,7 @@ struct Input {
|
|||
virtual bool acquired() { return false; }
|
||||
|
||||
virtual bool poll(int16_t* table) { return false; }
|
||||
virtual void rumble(unsigned id, bool enable) {}
|
||||
virtual bool init() { return true; }
|
||||
virtual void term() {}
|
||||
|
||||
|
|
|
@ -143,6 +143,9 @@ struct pInputCarbon {
|
|||
return true;
|
||||
}
|
||||
|
||||
void rumble(unsigned id, bool enable) {
|
||||
}
|
||||
|
||||
bool init() {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -251,7 +251,10 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool init_joypad(const DIDEVICEINSTANCE* instance) {
|
||||
void rumble(unsigned id, bool enable) {
|
||||
}
|
||||
|
||||
bool initJoypad(const DIDEVICEINSTANCE* instance) {
|
||||
unsigned n;
|
||||
for(n = 0; n < Joypad::Count; n++) { if(!device.gamepad[n]) break; }
|
||||
if(n >= Joypad::Count) return DIENUM_STOP;
|
||||
|
@ -267,7 +270,7 @@ public:
|
|||
return DIENUM_CONTINUE;
|
||||
}
|
||||
|
||||
bool init_axis(const DIDEVICEOBJECTINSTANCE* instance) {
|
||||
bool initAxis(const DIDEVICEOBJECTINSTANCE* instance) {
|
||||
signed n;
|
||||
for(n = Joypad::Count - 1; n >= 0; n--) { if(device.gamepad[n]) break; }
|
||||
if(n < 0) return DIENUM_STOP;
|
||||
|
@ -375,11 +378,11 @@ public:
|
|||
};
|
||||
|
||||
BOOL CALLBACK DI_EnumJoypadsCallback(const DIDEVICEINSTANCE* instance, void* p) {
|
||||
return ((pInputDI*)p)->init_joypad(instance);
|
||||
return ((pInputDI*)p)->initJoypad(instance);
|
||||
}
|
||||
|
||||
BOOL CALLBACK DI_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, void* p) {
|
||||
return ((pInputDI*)p)->init_axis(instance);
|
||||
return ((pInputDI*)p)->initAxis(instance);
|
||||
}
|
||||
|
||||
DeclareInput(DI)
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
namespace ruby {
|
||||
|
||||
struct InputJoypadUdev {
|
||||
udev* context = nullptr;
|
||||
udev_monitor* monitor = nullptr;
|
||||
udev_enumerate* enumerator = nullptr;
|
||||
udev_list_entry* devices = nullptr;
|
||||
udev_list_entry* item = nullptr;
|
||||
|
||||
struct JoystickInput {
|
||||
signed code = 0;
|
||||
unsigned id = 0;
|
||||
int16_t value = 0;
|
||||
input_absinfo info;
|
||||
|
||||
JoystickInput() {}
|
||||
JoystickInput(signed code) : code(code) {}
|
||||
JoystickInput(signed code, unsigned id) : code(code), id(id) {}
|
||||
bool operator< (const JoystickInput& source) const { return code < source.code; }
|
||||
bool operator==(const JoystickInput& source) const { return code == source.code; }
|
||||
};
|
||||
|
||||
struct Joystick {
|
||||
string path;
|
||||
dev_t device = 0;
|
||||
int fd = -1;
|
||||
uint8_t evbit[(EV_MAX + 7) / 8] = {0};
|
||||
uint8_t keybit[(KEY_MAX + 7) / 8] = {0};
|
||||
uint8_t absbit[(ABS_MAX + 7) / 8] = {0};
|
||||
uint8_t ffbit[(FF_MAX + 7) / 8] = {0};
|
||||
unsigned effects = 0;
|
||||
|
||||
string name;
|
||||
string manufacturer;
|
||||
string product;
|
||||
string serial;
|
||||
string vendorID;
|
||||
string productID;
|
||||
|
||||
set<JoystickInput> axes;
|
||||
set<JoystickInput> hats;
|
||||
set<JoystickInput> buttons;
|
||||
bool rumble = false;
|
||||
unsigned effectID = 0;
|
||||
};
|
||||
vector<Joystick> joysticks;
|
||||
|
||||
bool poll(int16_t* table) {
|
||||
unsigned i = 0;
|
||||
for(auto& js : joysticks) {
|
||||
input_event events[32];
|
||||
signed length = 0;
|
||||
while((length = read(js.fd, events, sizeof(events))) > 0) {
|
||||
length /= sizeof(input_event);
|
||||
for(unsigned i = 0; i < length; i++) {
|
||||
signed code = events[i].code;
|
||||
signed type = events[i].type;
|
||||
signed value = events[i].value;
|
||||
|
||||
if(type == EV_ABS) {
|
||||
if(auto input = js.axes.find({code})) {
|
||||
signed range = input().info.maximum - input().info.minimum;
|
||||
signed axis = (value - input().info.minimum) * 65535ll / range - 32767;
|
||||
if(axis > +32767) axis = +32767;
|
||||
if(axis < -32768) axis = -32768;
|
||||
input().value = axis;
|
||||
}
|
||||
if(auto input = js.hats.find({code})) {
|
||||
input().value = value;
|
||||
}
|
||||
}
|
||||
|
||||
if(type == EV_KEY) {
|
||||
if(code >= BTN_MISC) {
|
||||
if(auto input = js.buttons.find({code})) {
|
||||
input().value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(auto input : js.axes) {
|
||||
table[joypad(i).axis(input.id)] = input.value;
|
||||
}
|
||||
|
||||
for(unsigned id = 0; id < (js.hats.size() + 1) / 2; id++) {
|
||||
table[joypad(i).hat(id)] = 0;
|
||||
}
|
||||
|
||||
for(auto input : js.hats) {
|
||||
unsigned hat = 0;
|
||||
if(input.code == ABS_HAT0X || input.code == ABS_HAT0Y) hat = 0;
|
||||
if(input.code == ABS_HAT1X || input.code == ABS_HAT1Y) hat = 1;
|
||||
if(input.code == ABS_HAT2X || input.code == ABS_HAT2Y) hat = 2;
|
||||
if(input.code == ABS_HAT3X || input.code == ABS_HAT3Y) hat = 3;
|
||||
|
||||
bool orientation = 0;
|
||||
if(input.code == ABS_HAT0X || input.code == ABS_HAT1X || input.code == ABS_HAT2X || input.code == ABS_HAT3X) orientation = 0;
|
||||
if(input.code == ABS_HAT0Y || input.code == ABS_HAT1Y || input.code == ABS_HAT2Y || input.code == ABS_HAT3Y) orientation = 1;
|
||||
|
||||
signed value = 0;
|
||||
if(orientation == 0) {
|
||||
if(input.value < 0) value |= Joypad::HatLeft;
|
||||
if(input.value > 0) value |= Joypad::HatRight;
|
||||
} else {
|
||||
if(input.value < 0) value |= Joypad::HatUp;
|
||||
if(input.value > 0) value |= Joypad::HatDown;
|
||||
}
|
||||
|
||||
table[joypad(i).hat(hat)] |= value;
|
||||
}
|
||||
|
||||
for(auto input : js.buttons) {
|
||||
table[joypad(i).button(input.id)] = input.value;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void rumble(unsigned id, bool enable) {
|
||||
if(id >= joysticks.size()) return;
|
||||
|
||||
Joystick& js = joysticks[id];
|
||||
if(js.rumble == false) return;
|
||||
|
||||
input_event play;
|
||||
memset(&play, 0, sizeof(input_event));
|
||||
play.type = EV_FF;
|
||||
play.code = js.effectID;
|
||||
play.value = enable;
|
||||
write(js.fd, &play, sizeof(input_event));
|
||||
}
|
||||
|
||||
bool init() {
|
||||
context = udev_new();
|
||||
if(context == nullptr) return false;
|
||||
|
||||
monitor = udev_monitor_new_from_netlink(context, "udev");
|
||||
if(monitor) {
|
||||
udev_monitor_filter_add_match_subsystem_devtype(monitor, "input", nullptr);
|
||||
udev_monitor_enable_receiving(monitor);
|
||||
}
|
||||
|
||||
enumerator = udev_enumerate_new(context);
|
||||
if(enumerator) {
|
||||
udev_enumerate_add_match_property(enumerator, "ID_INPUT_JOYSTICK", "1");
|
||||
udev_enumerate_scan_devices(enumerator);
|
||||
devices = udev_enumerate_get_list_entry(enumerator);
|
||||
for(udev_list_entry* item = devices; item != nullptr; item = udev_list_entry_get_next(item)) {
|
||||
const char* name = udev_list_entry_get_name(item);
|
||||
struct udev_device* device = udev_device_new_from_syspath(context, name);
|
||||
const char* deviceNode = udev_device_get_devnode(device);
|
||||
if(deviceNode) createJoystick(device, deviceNode);
|
||||
udev_device_unref(device);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void term() {
|
||||
if(enumerator) { udev_enumerate_unref(enumerator); enumerator = nullptr; }
|
||||
}
|
||||
|
||||
private:
|
||||
void createJoystick(udev_device* device, const char* path) {
|
||||
Joystick js;
|
||||
js.path = path;
|
||||
|
||||
struct stat st;
|
||||
if(stat(path, &st) < 0) return;
|
||||
js.device = st.st_rdev;
|
||||
|
||||
js.fd = open(path, O_RDWR | O_NONBLOCK);
|
||||
if(js.fd < 0) return;
|
||||
|
||||
uint8_t evbit[(EV_MAX + 7) / 8] = {0};
|
||||
uint8_t keybit[(KEY_MAX + 7) / 8] = {0};
|
||||
uint8_t absbit[(ABS_MAX + 7) / 8] = {0};
|
||||
|
||||
ioctl(js.fd, EVIOCGBIT(0, sizeof(js.evbit)), js.evbit);
|
||||
ioctl(js.fd, EVIOCGBIT(EV_KEY, sizeof(js.keybit)), js.keybit);
|
||||
ioctl(js.fd, EVIOCGBIT(EV_ABS, sizeof(js.absbit)), js.absbit);
|
||||
ioctl(js.fd, EVIOCGBIT(EV_FF, sizeof(js.ffbit)), js.ffbit);
|
||||
ioctl(js.fd, EVIOCGEFFECTS, &js.effects);
|
||||
|
||||
#define testBit(buffer, bit) (buffer[(bit) >> 3] & 1 << ((bit) & 7))
|
||||
|
||||
if(testBit(js.evbit, EV_KEY)) {
|
||||
if(udev_device* parent = udev_device_get_parent_with_subsystem_devtype(device, "input", nullptr)) {
|
||||
js.name = udev_device_get_sysattr_value(parent, "name");
|
||||
js.vendorID = udev_device_get_sysattr_value(parent, "id/vendor");
|
||||
js.productID = udev_device_get_sysattr_value(parent, "id/product");
|
||||
if(udev_device* root = udev_device_get_parent_with_subsystem_devtype(parent, "usb", "usb_device")) {
|
||||
if(js.vendorID == udev_device_get_sysattr_value(root, "idVendor")
|
||||
&& js.productID == udev_device_get_sysattr_value(root, "idProduct")
|
||||
) {
|
||||
js.manufacturer = udev_device_get_sysattr_value(root, "manufacturer");
|
||||
js.product = udev_device_get_sysattr_value(root, "product");
|
||||
js.serial = udev_device_get_sysattr_value(root, "serial");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned axes = 0;
|
||||
unsigned hats = 0;
|
||||
unsigned buttons = 0;
|
||||
for(signed i = 0; i < ABS_MISC; i++) {
|
||||
if(testBit(js.absbit, i)) {
|
||||
if(i >= ABS_HAT0X && i <= ABS_HAT3Y) {
|
||||
if(auto hat = js.hats.insert({i, hats++})) {
|
||||
ioctl(js.fd, EVIOCGABS(i), &hat().info);
|
||||
}
|
||||
} else {
|
||||
if(auto axis = js.axes.insert({i, axes++})) {
|
||||
ioctl(js.fd, EVIOCGABS(i), &axis().info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(signed i = BTN_JOYSTICK; i < KEY_MAX; i++) {
|
||||
if(testBit(js.keybit, i)) {
|
||||
js.buttons.insert({i, buttons++});
|
||||
}
|
||||
}
|
||||
for(signed i = BTN_MISC; i < BTN_JOYSTICK; i++) {
|
||||
if(testBit(js.keybit, i)) {
|
||||
js.buttons.insert({i, buttons++});
|
||||
}
|
||||
}
|
||||
js.rumble = js.effects >= 2 && testBit(js.ffbit, FF_RUMBLE);
|
||||
if(js.rumble) {
|
||||
ff_effect effect;
|
||||
memset(&effect, 0, sizeof(ff_effect));
|
||||
effect.type = FF_RUMBLE;
|
||||
effect.id = -1;
|
||||
effect.u.rumble.strong_magnitude = 65535;
|
||||
effect.u.rumble.weak_magnitude = 65535;
|
||||
ioctl(js.fd, EVIOCSFF, &effect);
|
||||
js.effectID = effect.id;
|
||||
}
|
||||
|
||||
joysticks.append(js);
|
||||
}
|
||||
|
||||
#undef testBit
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
#include <xinput.h>
|
||||
|
||||
namespace ruby {
|
||||
|
||||
struct InputJoypadXInput {
|
||||
HMODULE libxinput = nullptr;
|
||||
DWORD WINAPI (*XInputGetState)(DWORD, XINPUT_STATE*) = nullptr;
|
||||
DWORD WINAPI (*XInputSetState)(DWORD, XINPUT_VIBRATION*) = nullptr;
|
||||
|
||||
struct Joystick {
|
||||
unsigned id;
|
||||
|
||||
int16_t hat = 0;
|
||||
int16_t axis[6] = {0};
|
||||
bool button[10] = {0};
|
||||
};
|
||||
vector<Joystick> joysticks;
|
||||
|
||||
bool poll(int16_t* table) {
|
||||
if(!XInputGetState) return false;
|
||||
|
||||
for(auto& js : joysticks) {
|
||||
XINPUT_STATE state;
|
||||
if(XInputGetState(js.id, &state) != ERROR_SUCCESS) continue;
|
||||
|
||||
int16_t hat = 0;
|
||||
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ) hat |= Joypad::HatUp;
|
||||
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ) hat |= Joypad::HatDown;
|
||||
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ) hat |= Joypad::HatLeft;
|
||||
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) hat |= Joypad::HatRight;
|
||||
|
||||
//scale trigger ranges from (0 to 255) to (-32768 to +32767)
|
||||
uint16_t triggerL = state.Gamepad.bLeftTrigger;
|
||||
uint16_t triggerR = state.Gamepad.bRightTrigger;
|
||||
triggerL = triggerL << 8 | triggerL << 0;
|
||||
triggerR = triggerR << 8 | triggerR << 0;
|
||||
|
||||
table[joypad(js.id).axis(0)] = (int16_t)state.Gamepad.sThumbLX;
|
||||
table[joypad(js.id).axis(1)] = (int16_t)state.Gamepad.sThumbLY;
|
||||
table[joypad(js.id).axis(2)] = (int16_t)state.Gamepad.sThumbRX;
|
||||
table[joypad(js.id).axis(3)] = (int16_t)state.Gamepad.sThumbRY;
|
||||
table[joypad(js.id).axis(4)] = (int16_t)((~triggerL) - 32768);
|
||||
table[joypad(js.id).axis(5)] = (int16_t)((~triggerR) - 32768);
|
||||
table[joypad(js.id).hat(0)] = (int16_t)hat;
|
||||
table[joypad(js.id).button(0)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_A);
|
||||
table[joypad(js.id).button(1)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_B);
|
||||
table[joypad(js.id).button(2)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_X);
|
||||
table[joypad(js.id).button(3)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_Y);
|
||||
table[joypad(js.id).button(4)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK);
|
||||
table[joypad(js.id).button(5)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_START);
|
||||
table[joypad(js.id).button(6)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER);
|
||||
table[joypad(js.id).button(7)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER);
|
||||
table[joypad(js.id).button(8)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB);
|
||||
table[joypad(js.id).button(9)] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void rumble(unsigned id, bool enable) {
|
||||
if(!XInputSetState) return;
|
||||
if(id >= joysticks.size()) return;
|
||||
|
||||
XINPUT_VIBRATION vibration;
|
||||
memset(&vibration, 0, sizeof(XINPUT_VIBRATION));
|
||||
vibration.wLeftMotorSpeed = enable ? 65535 : 0; //low-frequency motor (0 = off, 65535 = max)
|
||||
vibration.wRightMotorSpeed = enable ? 65535 : 0; //high-frequency motor (0 = off, 65535 = max)
|
||||
XInputSetState(joysticks(id).id, &vibration);
|
||||
}
|
||||
|
||||
bool init() {
|
||||
if(!libxinput) libxinput = LoadLibraryA("xinput1_3.dll");
|
||||
if(!libxinput) libxinput = LoadLibraryA("xinput1_2.dll");
|
||||
if(!libxinput) libxinput = LoadLibraryA("xinput1_1.dll");
|
||||
if(!libxinput) return false;
|
||||
|
||||
XInputGetState = (DWORD WINAPI (*)(DWORD, XINPUT_STATE*))GetProcAddress(libxinput, "XInputGetState");
|
||||
XInputSetState = (DWORD WINAPI (*)(DWORD, XINPUT_VIBRATION*))GetProcAddress(libxinput, "XInputSetState");
|
||||
|
||||
//XInput supports a maximum of four controllers
|
||||
for(unsigned id = 0; id < 4; id++) {
|
||||
XINPUT_STATE state;
|
||||
if(XInputGetState(id, &state) == ERROR_SUCCESS) {
|
||||
Joystick js;
|
||||
js.id = id;
|
||||
joysticks.append(js);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void term() {
|
||||
if(libxinput) {
|
||||
FreeLibrary(libxinput);
|
||||
libxinput = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,277 @@
|
|||
namespace ruby {
|
||||
|
||||
struct InputKeyboardXlib {
|
||||
Display* display = nullptr;
|
||||
uint8_t scancode[256] = {0};
|
||||
|
||||
enum XScancode : unsigned {
|
||||
Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
|
||||
ScrollLock, Pause, Tilde,
|
||||
Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Num0,
|
||||
Dash, Equal, Backspace,
|
||||
Insert, Delete, Home, End, PageUp, PageDown,
|
||||
A, B, C, D, E, F, G, H, I, J, K, L, M,
|
||||
N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
|
||||
LeftBracket, RightBracket, Backslash, Semicolon, Apostrophe, Comma, Period, Slash,
|
||||
Keypad1, Keypad2, Keypad3, Keypad4, Keypad5, Keypad6, Keypad7, Keypad8, Keypad9, Keypad0,
|
||||
Point, Enter, Add, Subtract, Multiply, Divide,
|
||||
Up, Down, Left, Right,
|
||||
Tab, Return, Spacebar, Menu,
|
||||
LeftShift, RightShift, LeftControl, RightControl, LeftAlt, RightAlt, LeftSuper, RightSuper,
|
||||
};
|
||||
|
||||
bool poll(int16_t* table) {
|
||||
char state[32];
|
||||
XQueryKeymap(display, state);
|
||||
|
||||
#define key(id) table[keyboard(0)[id]]
|
||||
#define pressed(id) (bool)(state[scancode[id] >> 3] & (1 << (scancode[id] & 7)))
|
||||
|
||||
key(Keyboard::Escape) = pressed(Escape);
|
||||
|
||||
key(Keyboard::F1) = pressed(F1);
|
||||
key(Keyboard::F2) = pressed(F2);
|
||||
key(Keyboard::F3) = pressed(F3);
|
||||
key(Keyboard::F4) = pressed(F4);
|
||||
key(Keyboard::F5) = pressed(F5);
|
||||
key(Keyboard::F6) = pressed(F6);
|
||||
key(Keyboard::F7) = pressed(F7);
|
||||
key(Keyboard::F8) = pressed(F8);
|
||||
key(Keyboard::F9) = pressed(F9);
|
||||
key(Keyboard::F10) = pressed(F10);
|
||||
key(Keyboard::F11) = pressed(F11);
|
||||
key(Keyboard::F12) = pressed(F12);
|
||||
|
||||
key(Keyboard::ScrollLock) = pressed(ScrollLock);
|
||||
key(Keyboard::Pause) = pressed(Pause);
|
||||
key(Keyboard::Tilde) = pressed(Tilde);
|
||||
|
||||
key(Keyboard::Num1) = pressed(Num1);
|
||||
key(Keyboard::Num2) = pressed(Num2);
|
||||
key(Keyboard::Num3) = pressed(Num3);
|
||||
key(Keyboard::Num4) = pressed(Num4);
|
||||
key(Keyboard::Num5) = pressed(Num5);
|
||||
key(Keyboard::Num6) = pressed(Num6);
|
||||
key(Keyboard::Num7) = pressed(Num7);
|
||||
key(Keyboard::Num8) = pressed(Num8);
|
||||
key(Keyboard::Num9) = pressed(Num9);
|
||||
key(Keyboard::Num0) = pressed(Num0);
|
||||
|
||||
key(Keyboard::Dash) = pressed(Dash);
|
||||
key(Keyboard::Equal) = pressed(Equal);
|
||||
key(Keyboard::Backspace) = pressed(Backspace);
|
||||
|
||||
key(Keyboard::Insert) = pressed(Insert);
|
||||
key(Keyboard::Delete) = pressed(Delete);
|
||||
key(Keyboard::Home) = pressed(Home);
|
||||
key(Keyboard::End) = pressed(End);
|
||||
key(Keyboard::PageUp) = pressed(PageUp);
|
||||
key(Keyboard::PageDown) = pressed(PageDown);
|
||||
|
||||
key(Keyboard::A) = pressed(A);
|
||||
key(Keyboard::B) = pressed(B);
|
||||
key(Keyboard::C) = pressed(C);
|
||||
key(Keyboard::D) = pressed(D);
|
||||
key(Keyboard::E) = pressed(E);
|
||||
key(Keyboard::F) = pressed(F);
|
||||
key(Keyboard::G) = pressed(G);
|
||||
key(Keyboard::H) = pressed(H);
|
||||
key(Keyboard::I) = pressed(I);
|
||||
key(Keyboard::J) = pressed(J);
|
||||
key(Keyboard::K) = pressed(K);
|
||||
key(Keyboard::L) = pressed(L);
|
||||
key(Keyboard::M) = pressed(M);
|
||||
key(Keyboard::N) = pressed(N);
|
||||
key(Keyboard::O) = pressed(O);
|
||||
key(Keyboard::P) = pressed(P);
|
||||
key(Keyboard::Q) = pressed(Q);
|
||||
key(Keyboard::R) = pressed(R);
|
||||
key(Keyboard::S) = pressed(S);
|
||||
key(Keyboard::T) = pressed(T);
|
||||
key(Keyboard::U) = pressed(U);
|
||||
key(Keyboard::V) = pressed(V);
|
||||
key(Keyboard::W) = pressed(W);
|
||||
key(Keyboard::X) = pressed(X);
|
||||
key(Keyboard::Y) = pressed(Y);
|
||||
key(Keyboard::Z) = pressed(Z);
|
||||
|
||||
key(Keyboard::LeftBracket) = pressed(LeftBracket);
|
||||
key(Keyboard::RightBracket) = pressed(RightBracket);
|
||||
key(Keyboard::Backslash) = pressed(Backslash);
|
||||
key(Keyboard::Semicolon) = pressed(Semicolon);
|
||||
key(Keyboard::Apostrophe) = pressed(Apostrophe);
|
||||
key(Keyboard::Comma) = pressed(Comma);
|
||||
key(Keyboard::Period) = pressed(Period);
|
||||
key(Keyboard::Slash) = pressed(Slash);
|
||||
|
||||
key(Keyboard::Keypad1) = pressed(Keypad1);
|
||||
key(Keyboard::Keypad2) = pressed(Keypad2);
|
||||
key(Keyboard::Keypad3) = pressed(Keypad3);
|
||||
key(Keyboard::Keypad4) = pressed(Keypad4);
|
||||
key(Keyboard::Keypad5) = pressed(Keypad5);
|
||||
key(Keyboard::Keypad6) = pressed(Keypad6);
|
||||
key(Keyboard::Keypad7) = pressed(Keypad7);
|
||||
key(Keyboard::Keypad8) = pressed(Keypad8);
|
||||
key(Keyboard::Keypad9) = pressed(Keypad9);
|
||||
key(Keyboard::Keypad0) = pressed(Keypad0);
|
||||
|
||||
key(Keyboard::Point) = pressed(Point);
|
||||
key(Keyboard::Enter) = pressed(Enter);
|
||||
key(Keyboard::Add) = pressed(Add);
|
||||
key(Keyboard::Subtract) = pressed(Subtract);
|
||||
key(Keyboard::Multiply) = pressed(Multiply);
|
||||
key(Keyboard::Divide) = pressed(Divide);
|
||||
|
||||
key(Keyboard::Up) = pressed(Up);
|
||||
key(Keyboard::Down) = pressed(Down);
|
||||
key(Keyboard::Left) = pressed(Left);
|
||||
key(Keyboard::Right) = pressed(Right);
|
||||
|
||||
key(Keyboard::Tab) = pressed(Tab);
|
||||
key(Keyboard::Return) = pressed(Return);
|
||||
key(Keyboard::Spacebar) = pressed(Spacebar);
|
||||
key(Keyboard::Menu) = pressed(Menu);
|
||||
|
||||
key(Keyboard::Shift) = pressed(LeftShift) || pressed(RightShift);
|
||||
key(Keyboard::Control) = pressed(LeftControl) || pressed(RightControl);
|
||||
key(Keyboard::Alt) = pressed(LeftAlt) || pressed(RightAlt);
|
||||
key(Keyboard::Super) = pressed(LeftSuper) || pressed(RightSuper);
|
||||
|
||||
#undef key
|
||||
#undef pressed
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool init() {
|
||||
display = XOpenDisplay(0);
|
||||
|
||||
#define assign(x, y) scancode[x] = XKeysymToKeycode(display, y)
|
||||
assign(Escape, XK_Escape);
|
||||
|
||||
assign(F1, XK_F1);
|
||||
assign(F2, XK_F2);
|
||||
assign(F3, XK_F3);
|
||||
assign(F4, XK_F4);
|
||||
assign(F5, XK_F5);
|
||||
assign(F6, XK_F6);
|
||||
assign(F7, XK_F7);
|
||||
assign(F8, XK_F8);
|
||||
assign(F9, XK_F9);
|
||||
assign(F10, XK_F10);
|
||||
assign(F11, XK_F11);
|
||||
assign(F12, XK_F12);
|
||||
|
||||
assign(ScrollLock, XK_Scroll_Lock);
|
||||
assign(Pause, XK_Pause);
|
||||
|
||||
assign(Tilde, XK_asciitilde);
|
||||
|
||||
assign(Num0, XK_0);
|
||||
assign(Num1, XK_1);
|
||||
assign(Num2, XK_2);
|
||||
assign(Num3, XK_3);
|
||||
assign(Num4, XK_4);
|
||||
assign(Num5, XK_5);
|
||||
assign(Num6, XK_6);
|
||||
assign(Num7, XK_7);
|
||||
assign(Num8, XK_8);
|
||||
assign(Num9, XK_9);
|
||||
|
||||
assign(Dash, XK_minus);
|
||||
assign(Equal, XK_equal);
|
||||
assign(Backspace, XK_BackSpace);
|
||||
|
||||
assign(Insert, XK_Insert);
|
||||
assign(Delete, XK_Delete);
|
||||
assign(Home, XK_Home);
|
||||
assign(End, XK_End);
|
||||
assign(PageUp, XK_Prior);
|
||||
assign(PageDown, XK_Next);
|
||||
|
||||
assign(A, XK_A);
|
||||
assign(B, XK_B);
|
||||
assign(C, XK_C);
|
||||
assign(D, XK_D);
|
||||
assign(E, XK_E);
|
||||
assign(F, XK_F);
|
||||
assign(G, XK_G);
|
||||
assign(H, XK_H);
|
||||
assign(I, XK_I);
|
||||
assign(J, XK_J);
|
||||
assign(K, XK_K);
|
||||
assign(L, XK_L);
|
||||
assign(M, XK_M);
|
||||
assign(N, XK_N);
|
||||
assign(O, XK_O);
|
||||
assign(P, XK_P);
|
||||
assign(Q, XK_Q);
|
||||
assign(R, XK_R);
|
||||
assign(S, XK_S);
|
||||
assign(T, XK_T);
|
||||
assign(U, XK_U);
|
||||
assign(V, XK_V);
|
||||
assign(W, XK_W);
|
||||
assign(X, XK_X);
|
||||
assign(Y, XK_Y);
|
||||
assign(Z, XK_Z);
|
||||
|
||||
assign(LeftBracket, XK_bracketleft);
|
||||
assign(RightBracket, XK_bracketright);
|
||||
assign(Backslash, XK_backslash);
|
||||
assign(Semicolon, XK_semicolon);
|
||||
assign(Apostrophe, XK_apostrophe);
|
||||
assign(Comma, XK_comma);
|
||||
assign(Period, XK_period);
|
||||
assign(Slash, XK_slash);
|
||||
|
||||
assign(Keypad0, XK_KP_0);
|
||||
assign(Keypad1, XK_KP_1);
|
||||
assign(Keypad2, XK_KP_2);
|
||||
assign(Keypad3, XK_KP_3);
|
||||
assign(Keypad4, XK_KP_4);
|
||||
assign(Keypad5, XK_KP_5);
|
||||
assign(Keypad6, XK_KP_6);
|
||||
assign(Keypad7, XK_KP_7);
|
||||
assign(Keypad8, XK_KP_8);
|
||||
assign(Keypad9, XK_KP_9);
|
||||
|
||||
assign(Add, XK_KP_Add);
|
||||
assign(Subtract, XK_KP_Subtract);
|
||||
assign(Multiply, XK_KP_Multiply);
|
||||
assign(Divide, XK_KP_Divide);
|
||||
assign(Enter, XK_KP_Enter);
|
||||
|
||||
assign(Up, XK_Up);
|
||||
assign(Down, XK_Down);
|
||||
assign(Left, XK_Left);
|
||||
assign(Right, XK_Right);
|
||||
|
||||
assign(Tab, XK_Tab);
|
||||
assign(Return, XK_Return);
|
||||
assign(Spacebar, XK_space);
|
||||
|
||||
assign(LeftControl, XK_Control_L);
|
||||
assign(RightControl, XK_Control_R);
|
||||
assign(LeftAlt, XK_Alt_L);
|
||||
assign(RightAlt, XK_Alt_R);
|
||||
assign(LeftShift, XK_Shift_L);
|
||||
assign(RightShift, XK_Shift_R);
|
||||
assign(LeftSuper, XK_Super_L);
|
||||
assign(RightSuper, XK_Super_R);
|
||||
assign(Menu, XK_Menu);
|
||||
|
||||
#undef assign
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void term() {
|
||||
if(display) {
|
||||
XCloseDisplay(display);
|
||||
display = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
namespace ruby {
|
||||
|
||||
struct InputMouseXlib {
|
||||
uintptr_t handle = 0;
|
||||
|
||||
Display* display = nullptr;
|
||||
Window rootWindow;
|
||||
Cursor invisibleCursor;
|
||||
unsigned screenWidth = 0;
|
||||
unsigned screenHeight = 0;
|
||||
|
||||
struct Mouse {
|
||||
bool acquired = false;
|
||||
signed numerator = 0;
|
||||
signed denominator = 0;
|
||||
signed threshold = 0;
|
||||
unsigned relativeX = 0;
|
||||
unsigned relativeY = 0;
|
||||
} ms;
|
||||
|
||||
bool acquire() {
|
||||
if(acquired()) return true;
|
||||
|
||||
if(XGrabPointer(display, handle, True, 0, GrabModeAsync, GrabModeAsync, rootWindow, invisibleCursor, CurrentTime) == GrabSuccess) {
|
||||
//backup existing cursor acceleration settings
|
||||
XGetPointerControl(display, &ms.numerator, &ms.denominator, &ms.threshold);
|
||||
|
||||
//disable cursor acceleration
|
||||
XChangePointerControl(display, True, False, 1, 1, 0);
|
||||
|
||||
//center cursor (so that first relative poll returns 0, 0 if mouse has not moved)
|
||||
XWarpPointer(display, None, rootWindow, 0, 0, 0, 0, screenWidth / 2, screenHeight / 2);
|
||||
|
||||
return ms.acquired = true;
|
||||
} else {
|
||||
return ms.acquired = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool unacquire() {
|
||||
if(acquired()) {
|
||||
//restore cursor acceleration and release cursor
|
||||
XChangePointerControl(display, True, True, ms.numerator, ms.denominator, ms.threshold);
|
||||
XUngrabPointer(display, CurrentTime);
|
||||
ms.acquired = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool acquired() {
|
||||
return ms.acquired;
|
||||
}
|
||||
|
||||
bool poll(int16_t* table) {
|
||||
Window rootReturn;
|
||||
Window childReturn;
|
||||
signed rootXReturn = 0;
|
||||
signed rootYReturn = 0;
|
||||
signed windowXReturn = 0;
|
||||
signed windowYReturn = 0;
|
||||
unsigned maskReturn = 0;
|
||||
XQueryPointer(display, handle, &rootReturn, &childReturn, &rootXReturn, &rootYReturn, &windowXReturn, &windowYReturn, &maskReturn);
|
||||
|
||||
if(acquired()) {
|
||||
XWindowAttributes attributes;
|
||||
XGetWindowAttributes(display, handle, &attributes);
|
||||
|
||||
//absolute -> relative conversion
|
||||
table[mouse(0).axis(0)] = (int16_t)(rootXReturn - screenWidth / 2);
|
||||
table[mouse(0).axis(1)] = (int16_t)(rootYReturn - screenHeight / 2);
|
||||
|
||||
if(table[mouse(0).axis(0)] != 0 || table[mouse(0).axis(1)] != 0) {
|
||||
//if mouse moved, re-center mouse for next poll
|
||||
XWarpPointer(display, None, rootWindow, 0, 0, 0, 0, screenWidth / 2, screenHeight / 2);
|
||||
}
|
||||
} else {
|
||||
table[mouse(0).axis(0)] = (int16_t)(rootXReturn - ms.relativeX);
|
||||
table[mouse(0).axis(1)] = (int16_t)(rootYReturn - ms.relativeY);
|
||||
|
||||
ms.relativeX = rootXReturn;
|
||||
ms.relativeY = rootYReturn;
|
||||
}
|
||||
|
||||
table[mouse(0).button(0)] = (bool)(maskReturn & Button1Mask);
|
||||
table[mouse(0).button(1)] = (bool)(maskReturn & Button2Mask);
|
||||
table[mouse(0).button(2)] = (bool)(maskReturn & Button3Mask);
|
||||
table[mouse(0).button(3)] = (bool)(maskReturn & Button4Mask);
|
||||
table[mouse(0).button(4)] = (bool)(maskReturn & Button5Mask);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool init(uintptr_t handle) {
|
||||
this->handle = handle;
|
||||
display = XOpenDisplay(0);
|
||||
rootWindow = DefaultRootWindow(display);
|
||||
|
||||
XWindowAttributes attributes;
|
||||
XGetWindowAttributes(display, rootWindow, &attributes);
|
||||
screenWidth = attributes.width;
|
||||
screenHeight = attributes.height;
|
||||
|
||||
//create invisible cursor for use when mouse is acquired
|
||||
Pixmap pixmap;
|
||||
XColor black, unused;
|
||||
static char invisibleData[8] = {0};
|
||||
Colormap colormap = DefaultColormap(display, DefaultScreen(display));
|
||||
XAllocNamedColor(display, colormap, "black", &black, &unused);
|
||||
pixmap = XCreateBitmapFromData(display, handle, invisibleData, 8, 8);
|
||||
invisibleCursor = XCreatePixmapCursor(display, pixmap, pixmap, &black, &black, 0, 0);
|
||||
XFreePixmap(display, pixmap);
|
||||
XFreeColors(display, colormap, &black.pixel, 1, 0);
|
||||
|
||||
ms.acquired = false;
|
||||
ms.relativeX = 0;
|
||||
ms.relativeY = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void term() {
|
||||
unacquire();
|
||||
XFreeCursor(display, invisibleCursor);
|
||||
XCloseDisplay(display);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -20,15 +20,15 @@
|
|||
|
||||
#define DIRECTINPUT_VERSION 0x0800
|
||||
#include <dinput.h>
|
||||
#include <xinput.h>
|
||||
|
||||
#include "joypad/xinput.cpp"
|
||||
|
||||
namespace ruby {
|
||||
|
||||
static DWORD WINAPI RawInputThreadProc(void*);
|
||||
static LRESULT CALLBACK RawInputWindowProc(HWND, UINT, WPARAM, LPARAM);
|
||||
|
||||
class RawInput {
|
||||
public:
|
||||
struct RawInput {
|
||||
HANDLE mutex;
|
||||
HWND hwnd;
|
||||
bool initialized;
|
||||
|
@ -393,107 +393,11 @@ LRESULT CALLBACK RawInputWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM l
|
|||
return rawinput.window_proc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
class XInput {
|
||||
public:
|
||||
HMODULE libxinput;
|
||||
DWORD WINAPI (*pXInputGetState)(DWORD, XINPUT_STATE*);
|
||||
|
||||
struct Gamepad {
|
||||
unsigned id;
|
||||
|
||||
int16_t hat;
|
||||
int16_t axis[6];
|
||||
bool button[10];
|
||||
|
||||
void poll(XINPUT_STATE &state) {
|
||||
hat = Joypad::HatCenter;
|
||||
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ) hat |= Joypad::HatUp;
|
||||
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) hat |= Joypad::HatRight;
|
||||
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ) hat |= Joypad::HatDown;
|
||||
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ) hat |= Joypad::HatLeft;
|
||||
|
||||
axis[0] = (int16_t)state.Gamepad.sThumbLX;
|
||||
axis[1] = (int16_t)state.Gamepad.sThumbLY;
|
||||
axis[2] = (int16_t)state.Gamepad.sThumbRX;
|
||||
axis[3] = (int16_t)state.Gamepad.sThumbRY;
|
||||
|
||||
//transform left and right trigger ranges:
|
||||
//from: 0 (low, eg released) to 255 (high, eg pressed all the way down)
|
||||
//to: +32767 (low) to -32768 (high)
|
||||
uint16_t triggerX = state.Gamepad.bLeftTrigger;
|
||||
uint16_t triggerY = state.Gamepad.bRightTrigger;
|
||||
|
||||
triggerX = (triggerX << 8) | triggerX;
|
||||
triggerY = (triggerY << 8) | triggerY;
|
||||
|
||||
axis[4] = (~triggerX) - 32768;
|
||||
axis[5] = (~triggerY) - 32768;
|
||||
|
||||
button[0] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_A);
|
||||
button[1] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_B);
|
||||
button[2] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_X);
|
||||
button[3] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_Y);
|
||||
button[4] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK);
|
||||
button[5] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_START);
|
||||
button[6] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER);
|
||||
button[7] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER);
|
||||
button[8] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB);
|
||||
button[9] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB);
|
||||
}
|
||||
|
||||
Gamepad() {
|
||||
hat = Joypad::HatCenter;
|
||||
for(unsigned n = 0; n < 6; n++) axis[n] = 0;
|
||||
for(unsigned n = 0; n < 10; n++) button[n] = false;
|
||||
}
|
||||
};
|
||||
|
||||
vector<Gamepad> lgamepad;
|
||||
|
||||
void poll() {
|
||||
if(!pXInputGetState) return;
|
||||
|
||||
for(unsigned i = 0; i < lgamepad.size(); i++) {
|
||||
XINPUT_STATE state;
|
||||
DWORD result = pXInputGetState(lgamepad(i).id, &state);
|
||||
if(result == ERROR_SUCCESS) lgamepad(i).poll(state);
|
||||
}
|
||||
}
|
||||
|
||||
void init() {
|
||||
if(!pXInputGetState) return;
|
||||
|
||||
//XInput only supports up to four controllers
|
||||
for(unsigned i = 0; i <= 3; i++) {
|
||||
XINPUT_STATE state;
|
||||
DWORD result = pXInputGetState(i, &state);
|
||||
if(result == ERROR_SUCCESS) {
|
||||
//valid controller detected, add to gamepad list
|
||||
unsigned n = lgamepad.size();
|
||||
lgamepad(n).id = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XInput() : pXInputGetState(0) {
|
||||
//bind xinput1 dynamically, as it does not ship with Windows Vista or below
|
||||
libxinput = LoadLibraryA("xinput1_3.dll");
|
||||
if(!libxinput) libxinput = LoadLibraryA("xinput1_2.dll");
|
||||
if(!libxinput) libxinput = LoadLibraryA("xinput1_1.dll");
|
||||
if(!libxinput) return;
|
||||
pXInputGetState = (DWORD WINAPI (*)(DWORD, XINPUT_STATE*))GetProcAddress(libxinput, "XInputGetState");
|
||||
}
|
||||
|
||||
~XInput() {
|
||||
if(libxinput) FreeLibrary(libxinput);
|
||||
}
|
||||
};
|
||||
|
||||
static BOOL CALLBACK DirectInput_EnumJoypadsCallback(const DIDEVICEINSTANCE*, void*);
|
||||
static BOOL CALLBACK DirectInput_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE*, void*);
|
||||
static BOOL CALLBACK DirectInput_EnumJoypadFeedbacksCallback(const DIDEVICEOBJECTINSTANCE*, void*);
|
||||
|
||||
class DirectInput {
|
||||
public:
|
||||
struct DirectInput {
|
||||
HWND handle;
|
||||
LPDIRECTINPUT8 context;
|
||||
struct Gamepad {
|
||||
|
@ -502,6 +406,7 @@ public:
|
|||
int16_t hat[4];
|
||||
int16_t axis[6];
|
||||
bool button[128];
|
||||
LPDIRECTINPUTEFFECT effect = nullptr;
|
||||
|
||||
void poll(DIJOYSTATE2& state) {
|
||||
//POV hats
|
||||
|
@ -557,7 +462,17 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
bool init_joypad(const DIDEVICEINSTANCE* instance) {
|
||||
void rumble(unsigned id, bool enable) {
|
||||
if(lgamepad(id).effect == nullptr) return;
|
||||
|
||||
if(enable) {
|
||||
lgamepad(id).effect->Start(1, 0);
|
||||
} else {
|
||||
lgamepad(id).effect->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
bool initJoypad(const DIDEVICEINSTANCE* instance) {
|
||||
//if this is an XInput device, do not acquire it via DirectInput ...
|
||||
//the XInput driver above will handle said device.
|
||||
for(unsigned i = 0; i < rawinput.lgamepad.size(); i++) {
|
||||
|
@ -573,15 +488,53 @@ public:
|
|||
return DIENUM_CONTINUE;
|
||||
}
|
||||
|
||||
device->SetDataFormat(&c_dfDIJoystick2);
|
||||
device->SetCooperativeLevel(handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
|
||||
device->EnumObjects(DirectInput_EnumJoypadAxesCallback, (void*)this, DIDFT_ABSAXIS);
|
||||
unsigned n = lgamepad.size();
|
||||
lgamepad(n).handle = device;
|
||||
|
||||
device->SetDataFormat(&c_dfDIJoystick2);
|
||||
device->SetCooperativeLevel(handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
|
||||
|
||||
forceFeedbackAxes = 0;
|
||||
device->EnumObjects(DirectInput_EnumJoypadAxesCallback, (void*)this, DIDFT_ABSAXIS);
|
||||
device->EnumObjects(DirectInput_EnumJoypadFeedbacksCallback, (void*)this, DIDFT_FFACTUATOR);
|
||||
if(forceFeedbackAxes == 0) return DIENUM_CONTINUE;
|
||||
|
||||
//disable auto-centering spring for rumble support
|
||||
DIPROPDWORD property;
|
||||
memset(&property, 0, sizeof(DIPROPDWORD));
|
||||
property.diph.dwSize = sizeof(DIPROPDWORD);
|
||||
property.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
||||
property.diph.dwObj = 0;
|
||||
property.diph.dwHow = DIPH_DEVICE;
|
||||
property.dwData = false;
|
||||
device->SetProperty(DIPROP_AUTOCENTER, &property.diph);
|
||||
|
||||
DWORD dwAxes[2] = {DIJOFS_X, DIJOFS_Y};
|
||||
LONG lDirection[2] = {0, 0};
|
||||
DICONSTANTFORCE force;
|
||||
force.lMagnitude = DI_FFNOMINALMAX; //full force
|
||||
DIEFFECT effect;
|
||||
memset(&effect, 0, sizeof(DIEFFECT));
|
||||
effect.dwSize = sizeof(DIEFFECT);
|
||||
effect.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
|
||||
effect.dwDuration = INFINITE;
|
||||
effect.dwSamplePeriod = 0;
|
||||
effect.dwGain = DI_FFNOMINALMAX;
|
||||
effect.dwTriggerButton = DIEB_NOTRIGGER;
|
||||
effect.dwTriggerRepeatInterval = 0;
|
||||
effect.cAxes = 2;
|
||||
effect.rgdwAxes = dwAxes;
|
||||
effect.rglDirection = lDirection;
|
||||
effect.lpEnvelope = 0;
|
||||
effect.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
|
||||
effect.lpvTypeSpecificParams = &force;
|
||||
effect.dwStartDelay = 0;
|
||||
device->CreateEffect(GUID_ConstantForce, &effect, &lgamepad(n).effect, NULL);
|
||||
|
||||
return DIENUM_CONTINUE;
|
||||
}
|
||||
|
||||
bool init_axis(const DIDEVICEOBJECTINSTANCE* instance) {
|
||||
bool initAxis(const DIDEVICEOBJECTINSTANCE* instance) {
|
||||
DIPROPRANGE range;
|
||||
range.diph.dwSize = sizeof(DIPROPRANGE);
|
||||
range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
||||
|
@ -593,6 +546,11 @@ public:
|
|||
return DIENUM_CONTINUE;
|
||||
}
|
||||
|
||||
bool initFeedback(const DIDEVICEOBJECTINSTANCE* instance) {
|
||||
forceFeedbackAxes++;
|
||||
return DIENUM_CONTINUE;
|
||||
}
|
||||
|
||||
void init(HWND handle_) {
|
||||
handle = handle_;
|
||||
DirectInput8Create(GetModuleHandle(0), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&context, 0);
|
||||
|
@ -602,37 +560,42 @@ public:
|
|||
void term() {
|
||||
for(unsigned i = 0; i < lgamepad.size(); i++) {
|
||||
lgamepad(i).handle->Unacquire();
|
||||
if(lgamepad(i).effect) lgamepad(i).effect->Release();
|
||||
lgamepad(i).handle->Release();
|
||||
}
|
||||
lgamepad.reset();
|
||||
|
||||
if(context) {
|
||||
context->Release();
|
||||
context = 0;
|
||||
context = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
LPDIRECTINPUTDEVICE8 device;
|
||||
unsigned forceFeedbackAxes;
|
||||
};
|
||||
|
||||
BOOL CALLBACK DirectInput_EnumJoypadsCallback(const DIDEVICEINSTANCE* instance, void* p) {
|
||||
return ((DirectInput*)p)->init_joypad(instance);
|
||||
return ((DirectInput*)p)->initJoypad(instance);
|
||||
}
|
||||
|
||||
BOOL CALLBACK DirectInput_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, void* p) {
|
||||
return ((DirectInput*)p)->init_axis(instance);
|
||||
return ((DirectInput*)p)->initAxis(instance);
|
||||
}
|
||||
|
||||
class pInputRaw {
|
||||
public:
|
||||
XInput xinput;
|
||||
BOOL CALLBACK DirectInput_EnumJoypadFeedbacksCallback(const DIDEVICEOBJECTINSTANCE* instance, void* p) {
|
||||
return ((DirectInput*)p)->initFeedback(instance);
|
||||
}
|
||||
|
||||
struct pInputRaw {
|
||||
InputJoypadXInput xinput;
|
||||
DirectInput dinput;
|
||||
|
||||
bool acquire_mouse;
|
||||
bool cursor_visible;
|
||||
|
||||
struct {
|
||||
struct Settings {
|
||||
HWND handle;
|
||||
} settings;
|
||||
|
||||
|
@ -641,6 +604,7 @@ public:
|
|||
if(name == Input::KeyboardSupport) return true;
|
||||
if(name == Input::MouseSupport) return true;
|
||||
if(name == Input::JoypadSupport) return true;
|
||||
if(name == Input::JoypadRumbleSupport) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -720,31 +684,15 @@ public:
|
|||
|
||||
ReleaseMutex(rawinput.mutex);
|
||||
|
||||
unsigned joy = 0;
|
||||
|
||||
//==================
|
||||
//XInput controllers
|
||||
//==================
|
||||
xinput.poll();
|
||||
for(unsigned i = 0; i < xinput.lgamepad.size(); i++) {
|
||||
if(joy >= Joypad::Count) break;
|
||||
|
||||
table[joypad(joy).hat(0)] = xinput.lgamepad(i).hat;
|
||||
|
||||
for(unsigned axis = 0; axis < min(6U, (unsigned)Joypad::Axes); axis++) {
|
||||
table[joypad(joy).axis(axis)] = xinput.lgamepad(i).axis[axis];
|
||||
}
|
||||
|
||||
for(unsigned button = 0; button < min(10U, (unsigned)Joypad::Buttons); button++) {
|
||||
table[joypad(joy).button(button)] = xinput.lgamepad(i).button[button];
|
||||
}
|
||||
|
||||
joy++;
|
||||
}
|
||||
xinput.poll(table);
|
||||
|
||||
//=======================
|
||||
//DirectInput controllers
|
||||
//=======================
|
||||
unsigned joy = xinput.joysticks.size();
|
||||
dinput.poll();
|
||||
for(unsigned i = 0; i < dinput.lgamepad.size(); i++) {
|
||||
if(joy >= Joypad::Count) break;
|
||||
|
@ -767,6 +715,15 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
void rumble(unsigned id, bool enable) {
|
||||
//id is the nall joypad# to rumble; but we have two lists of joypads
|
||||
//XInput joypads are enumerated first, and then DirectInput joypads
|
||||
if(id < xinput.joysticks.size()) return xinput.rumble(id, enable);
|
||||
id -= xinput.joysticks.size();
|
||||
if(id < dinput.lgamepad.size()) return dinput.rumble(id, enable);
|
||||
id -= dinput.lgamepad.size();
|
||||
}
|
||||
|
||||
bool init() {
|
||||
//only spawn RawInput processing thread one time
|
||||
if(rawinput.initialized == false) {
|
||||
|
@ -795,6 +752,7 @@ public:
|
|||
|
||||
void term() {
|
||||
unacquire();
|
||||
xinput.term();
|
||||
dinput.term();
|
||||
}
|
||||
|
||||
|
|
|
@ -171,6 +171,9 @@ struct pInputSDL {
|
|||
return true;
|
||||
}
|
||||
|
||||
void rumble(unsigned id, bool enable) {
|
||||
}
|
||||
|
||||
bool init() {
|
||||
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
|
||||
SDL_JoystickEventState(SDL_IGNORE);
|
||||
|
@ -227,4 +230,4 @@ struct pInputSDL {
|
|||
|
||||
DeclareInput(SDL)
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/poll.h>
|
||||
#include <fcntl.h>
|
||||
#include <libudev.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#include "keyboard/xlib.cpp"
|
||||
#include "mouse/xlib.cpp"
|
||||
#include "joypad/udev.cpp"
|
||||
|
||||
namespace ruby {
|
||||
|
||||
struct pInputUdev {
|
||||
InputKeyboardXlib xlibKeyboard;
|
||||
InputMouseXlib xlibMouse;
|
||||
InputJoypadUdev udev;
|
||||
|
||||
struct Settings {
|
||||
uintptr_t handle = 0;
|
||||
} settings;
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Input::Handle) return true;
|
||||
if(name == Input::KeyboardSupport) return true;
|
||||
if(name == Input::MouseSupport) return true;
|
||||
if(name == Input::JoypadSupport) return true;
|
||||
if(name == Input::JoypadRumbleSupport) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Input::Handle) return (uintptr_t)settings.handle;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Input::Handle) {
|
||||
settings.handle = any_cast<uintptr_t>(value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool acquire() {
|
||||
return xlibMouse.acquire();
|
||||
}
|
||||
|
||||
bool unacquire() {
|
||||
return xlibMouse.unacquire();
|
||||
}
|
||||
|
||||
bool acquired() {
|
||||
return xlibMouse.acquired();
|
||||
}
|
||||
|
||||
bool poll(int16_t* table) {
|
||||
xlibKeyboard.poll(table);
|
||||
xlibMouse.poll(table);
|
||||
udev.poll(table);
|
||||
}
|
||||
|
||||
void rumble(unsigned id, bool enable) {
|
||||
udev.rumble(id, enable);
|
||||
}
|
||||
|
||||
bool init() {
|
||||
if(xlibKeyboard.init() == false) return false;
|
||||
if(xlibMouse.init(settings.handle) == false) return false;
|
||||
if(udev.init() == false) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void term() {
|
||||
xlibKeyboard.term();
|
||||
xlibMouse.term();
|
||||
udev.term();
|
||||
}
|
||||
};
|
||||
|
||||
DeclareInput(Udev)
|
||||
|
||||
}
|
|
@ -34,6 +34,9 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
void rumble(unsigned id, bool enable) {
|
||||
}
|
||||
|
||||
bool init() {
|
||||
display = XOpenDisplay(0);
|
||||
x_init(display);
|
||||
|
@ -47,4 +50,4 @@ public:
|
|||
|
||||
DeclareInput(X)
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -366,6 +366,7 @@ const char* Input::Handle = "Handle";
|
|||
const char* Input::KeyboardSupport = "KeyboardSupport";
|
||||
const char* Input::MouseSupport = "MouseSupport";
|
||||
const char* Input::JoypadSupport = "JoypadSupport";
|
||||
const char* Input::JoypadRumbleSupport = "JoypadRumbleSupport";
|
||||
|
||||
void InputInterface::driver(const char* driver) {
|
||||
if(p) term();
|
||||
|
@ -386,6 +387,10 @@ void InputInterface::driver(const char* driver) {
|
|||
else if(!strcmp(driver, "Carbon")) p = new InputCarbon();
|
||||
#endif
|
||||
|
||||
#ifdef INPUT_UDEV
|
||||
else if(!strcmp(driver, "udev")) p = new InputUdev();
|
||||
#endif
|
||||
|
||||
#ifdef INPUT_SDL
|
||||
else if(!strcmp(driver, "SDL")) p = new InputSDL();
|
||||
#endif
|
||||
|
@ -406,6 +411,8 @@ const char* InputInterface::optimalDriver() {
|
|||
#elif defined(INPUT_CARBON)
|
||||
return "Carbon";
|
||||
|
||||
#elif defined(INPUT_UDEV)
|
||||
return "udev";
|
||||
#elif defined(INPUT_SDL)
|
||||
return "SDL";
|
||||
#elif defined(INPUT_X)
|
||||
|
@ -425,6 +432,8 @@ const char* InputInterface::safestDriver() {
|
|||
#elif defined(INPUT_CARBON)
|
||||
return "Carbon";
|
||||
|
||||
#elif defined(INPUT_UDEV)
|
||||
return "udev";
|
||||
#elif defined(INPUT_SDL)
|
||||
return "SDL";
|
||||
#elif defined(INPUT_X)
|
||||
|
@ -456,6 +465,10 @@ const char* InputInterface::availableDrivers() {
|
|||
|
||||
//Linux
|
||||
|
||||
#if defined(INPUT_UDEV)
|
||||
"udev;"
|
||||
#endif
|
||||
|
||||
#if defined(INPUT_SDL)
|
||||
"SDL;"
|
||||
#endif
|
||||
|
@ -487,6 +500,7 @@ bool InputInterface::acquire() { return p ? p->acquire() : false; }
|
|||
bool InputInterface::unacquire() { return p ? p->unacquire() : false; }
|
||||
bool InputInterface::acquired() { return p ? p->acquired() : false; }
|
||||
bool InputInterface::poll(int16_t* table) { return p ? p->poll(table) : false; }
|
||||
void InputInterface::rumble(unsigned id, bool enable) { if(p) return p->rumble(id, enable); }
|
||||
InputInterface::InputInterface() : p(nullptr) {}
|
||||
InputInterface::~InputInterface() { term(); }
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
ruby
|
||||
version: 0.10 (2013-07-27)
|
||||
version: 0.11 (2013-12-19)
|
||||
license: public domain
|
||||
*/
|
||||
|
||||
|
@ -32,6 +32,7 @@ struct VideoInterface {
|
|||
void unlock();
|
||||
void clear();
|
||||
void refresh();
|
||||
|
||||
VideoInterface();
|
||||
~VideoInterface();
|
||||
|
||||
|
@ -53,6 +54,7 @@ struct AudioInterface {
|
|||
|
||||
void sample(uint16_t left, uint16_t right);
|
||||
void clear();
|
||||
|
||||
AudioInterface();
|
||||
~AudioInterface();
|
||||
|
||||
|
@ -77,6 +79,8 @@ struct InputInterface {
|
|||
bool acquired();
|
||||
|
||||
bool poll(int16_t* table);
|
||||
void rumble(unsigned id, bool enable);
|
||||
|
||||
InputInterface();
|
||||
~InputInterface();
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ void ICD2::reset() {
|
|||
joyp14lock = 0;
|
||||
pulselock = true;
|
||||
|
||||
GameBoy::video.generate_palette(Emulator::Interface::PaletteMode::None);
|
||||
GameBoy::video.generate_palette(Emulator::Interface::PaletteMode::Literal);
|
||||
GameBoy::system.init();
|
||||
GameBoy::system.power();
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ Video video;
|
|||
|
||||
void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
|
||||
for(unsigned color = 0; color < (1 << 19); color++) {
|
||||
if(mode == Emulator::Interface::PaletteMode::None) {
|
||||
if(mode == Emulator::Interface::PaletteMode::Literal) {
|
||||
palette[color] = color;
|
||||
continue;
|
||||
}
|
||||
|
@ -14,23 +14,32 @@ void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
|
|||
unsigned g = (color >> 5) & 31;
|
||||
unsigned r = (color >> 0) & 31;
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Channel) {
|
||||
l = image::normalize(l, 4, 16);
|
||||
r = image::normalize(r, 5, 16);
|
||||
g = image::normalize(g, 5, 16);
|
||||
b = image::normalize(b, 5, 16);
|
||||
palette[color] = interface->videoColor(color, l, r, g, b);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Emulation) {
|
||||
r = gamma_ramp[r];
|
||||
g = gamma_ramp[g];
|
||||
b = gamma_ramp[b];
|
||||
} else {
|
||||
r = (r << 3) | (r >> 2);
|
||||
g = (g << 3) | (g >> 2);
|
||||
b = (b << 3) | (b >> 2);
|
||||
r = image::normalize(r, 5, 8);
|
||||
g = image::normalize(g, 5, 8);
|
||||
b = image::normalize(b, 5, 8);
|
||||
}
|
||||
|
||||
double L = (1.0 + l) / 16.0;
|
||||
if(l == 0) L *= 0.5;
|
||||
unsigned R = L * (r << 8 | r << 0);
|
||||
unsigned G = L * (g << 8 | g << 0);
|
||||
unsigned B = L * (b << 8 | b << 0);
|
||||
unsigned R = L * image::normalize(r, 8, 16);
|
||||
unsigned G = L * image::normalize(g, 8, 16);
|
||||
unsigned B = L * image::normalize(b, 8, 16);
|
||||
|
||||
palette[color] = interface->videoColor(color, R, G, B);
|
||||
palette[color] = interface->videoColor(color, 0, R, G, B);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,9 +23,9 @@ else ifeq ($(platform),macosx)
|
|||
ruby += audio.openal
|
||||
ruby += input.carbon
|
||||
else ifeq ($(platform),linux)
|
||||
ruby := video.glx video.xv video.sdl
|
||||
ruby := video.glx video.xv video.xshm video.sdl
|
||||
ruby += audio.alsa audio.openal audio.oss audio.pulseaudio audio.pulseaudiosimple audio.ao
|
||||
ruby += input.sdl input.x
|
||||
ruby += input.udev input.sdl input.x
|
||||
else ifeq ($(platform),bsd)
|
||||
ruby := video.glx
|
||||
ruby += audio.openal audio.oss
|
||||
|
|
|
@ -15,16 +15,21 @@ void AbstractInput::bind() {
|
|||
else if(mapping.endsWith(".Lo")) type = Input::Type::AxisLo;
|
||||
else if(mapping.endsWith(".Hi")) type = Input::Type::AxisHi;
|
||||
else if(mapping.beginsWith("JP") && mapping.find("Axis")) type = Input::Type::Axis;
|
||||
else if(mapping.beginsWith("JP") && mapping.endsWith("Rumble")) type = Input::Type::Rumble;
|
||||
else if(mapping.beginsWith("MS") && mapping.endsWith("axis")) type = Input::Type::MouseAxis;
|
||||
else if(mapping.beginsWith("MS")) type = Input::Type::MouseButton;
|
||||
else type = Input::Type::Button;
|
||||
|
||||
if(type == Input::Type::Rumble) {
|
||||
unsigned joypad = mapping[2] - '0';
|
||||
inputList.append({type, joypad});
|
||||
} else {
|
||||
string decode = mapping;
|
||||
if(auto position = decode.find(".")) decode.resize(position());
|
||||
unsigned scancode = Scancode::decode(decode);
|
||||
|
||||
inputList.append({type, scancode});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AbstractInput::append(string encode) {
|
||||
|
@ -208,6 +213,39 @@ int16_t AbsoluteInput::poll() {
|
|||
return result;
|
||||
}
|
||||
|
||||
bool RumbleInput::bind(unsigned scancode, int16_t value) {
|
||||
using nall::Keyboard;
|
||||
|
||||
if(scancode == Scancode::None || scancode == keyboard(0)[Keyboard::Escape]) {
|
||||
inputList.reset();
|
||||
mapping = "None";
|
||||
return true;
|
||||
}
|
||||
|
||||
string encode = Scancode::encode(scancode);
|
||||
|
||||
if(Joypad::isAnyButton(scancode)) {
|
||||
if(value == 0) return false;
|
||||
if(auto position = encode.find("::")) encode.resize(position());
|
||||
encode.append("::Rumble");
|
||||
return append(encode);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int16_t RumbleInput::poll() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void RumbleInput::rumble(bool enable) {
|
||||
if(program->focused() == false) return;
|
||||
|
||||
for(auto& item : inputList) {
|
||||
input.rumble(item.scancode, enable);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
HotkeyInput::HotkeyInput() {
|
||||
|
@ -279,7 +317,8 @@ void InputManager::bootstrap() {
|
|||
if(input.type == 0) abstract = new DigitalInput;
|
||||
if(input.type == 1) abstract = new RelativeInput;
|
||||
if(input.type == 2) abstract = new AbsoluteInput;
|
||||
if(input.type >= 3) continue;
|
||||
if(input.type == 3) abstract = new RumbleInput;
|
||||
if(abstract == nullptr) continue;
|
||||
|
||||
abstract->name = string{input.name}.replace(" ", "");
|
||||
abstract->mapping = "None";
|
||||
|
|
|
@ -5,7 +5,7 @@ struct AbstractInput {
|
|||
bool state;
|
||||
|
||||
struct Input {
|
||||
enum class Type : unsigned { Button, MouseButton, MouseAxis, HatUp, HatDown, HatLeft, HatRight, Axis, AxisLo, AxisHi } type;
|
||||
enum class Type : unsigned { Button, MouseButton, MouseAxis, HatUp, HatDown, HatLeft, HatRight, Axis, AxisLo, AxisHi, Rumble } type;
|
||||
unsigned scancode;
|
||||
};
|
||||
vector<Input> inputList;
|
||||
|
@ -14,6 +14,7 @@ struct AbstractInput {
|
|||
bool append(string mapping);
|
||||
virtual bool bind(unsigned scancode, int16_t value) = 0;
|
||||
virtual int16_t poll() = 0;
|
||||
virtual void rumble(bool enable) {}
|
||||
AbstractInput();
|
||||
};
|
||||
|
||||
|
@ -35,6 +36,13 @@ struct AbsoluteInput : AbstractInput {
|
|||
int16_t poll();
|
||||
};
|
||||
|
||||
struct RumbleInput : AbstractInput {
|
||||
using AbstractInput::bind;
|
||||
bool bind(unsigned scancode, int16_t value);
|
||||
int16_t poll();
|
||||
void rumble(bool enable);
|
||||
};
|
||||
|
||||
struct HotkeyInput : DigitalInput {
|
||||
function<void ()> press;
|
||||
function<void ()> release;
|
||||
|
|
|
@ -13,7 +13,8 @@ void Interface::saveRequest(unsigned id, string path) {
|
|||
return utility->saveRequest(id, path);
|
||||
}
|
||||
|
||||
uint32_t Interface::videoColor(unsigned source, uint16_t r, uint16_t g, uint16_t b) {
|
||||
uint32_t Interface::videoColor(unsigned source, uint16_t a, uint16_t r, uint16_t g, uint16_t b) {
|
||||
if(config->video.shader != "Display Emulation") {
|
||||
if(config->video.saturation != 100) {
|
||||
uint16_t grayscale = uclamp<16>((r + g + b) / 3);
|
||||
double saturation = config->video.saturation * 0.01;
|
||||
|
@ -37,15 +38,16 @@ uint32_t Interface::videoColor(unsigned source, uint16_t r, uint16_t g, uint16_t
|
|||
g = g * luminance;
|
||||
b = b * luminance;
|
||||
}
|
||||
}
|
||||
|
||||
if(program->depth == 30) {
|
||||
r >>= 6, g >>= 6, b >>= 6;
|
||||
return r << 20 | g << 10 | b << 0;
|
||||
a >>= 14, r >>= 6, g >>= 6, b >>= 6;
|
||||
return a << 30 | r << 20 | g << 10 | b << 0;
|
||||
}
|
||||
|
||||
if(program->depth == 24) {
|
||||
r >>= 8, g >>= 8, b >>= 8;
|
||||
return r << 16 | g << 8 | b << 0;
|
||||
a >>= 8, r >>= 8, g >>= 8, b >>= 8;
|
||||
return a << 24 | r << 16 | g << 8 | b << 0;
|
||||
}
|
||||
|
||||
return 0u;
|
||||
|
@ -111,6 +113,11 @@ int16_t Interface::inputPoll(unsigned port, unsigned device, unsigned input) {
|
|||
return inputManager->inputMap[guid]->poll();
|
||||
}
|
||||
|
||||
void Interface::inputRumble(unsigned port, unsigned device, unsigned input, bool enable) {
|
||||
unsigned guid = system().port[port].device[device].input[input].guid;
|
||||
return inputManager->inputMap[guid]->rumble(enable);
|
||||
}
|
||||
|
||||
unsigned Interface::dipSettings(const Markup::Node& node) {
|
||||
return dipSwitches->run(node);
|
||||
}
|
||||
|
|
|
@ -2,10 +2,11 @@ struct Interface : Emulator::Interface::Bind {
|
|||
void loadRequest(unsigned id, string name, string type);
|
||||
void loadRequest(unsigned id, string path);
|
||||
void saveRequest(unsigned id, string path);
|
||||
uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue);
|
||||
uint32_t videoColor(unsigned source, uint16_t alpha, uint16_t red, uint16_t green, uint16_t blue);
|
||||
void videoRefresh(const uint32_t* palette, const uint32_t* data, unsigned pitch, unsigned width, unsigned height);
|
||||
void audioSample(int16_t lsample, int16_t rsample);
|
||||
int16_t inputPoll(unsigned port, unsigned device, unsigned input);
|
||||
void inputRumble(unsigned port, unsigned device, unsigned input, bool enable);
|
||||
unsigned dipSettings(const Markup::Node& node);
|
||||
string path(unsigned group);
|
||||
string server();
|
||||
|
|
|
@ -182,8 +182,8 @@ void Utility::synchronizeRuby() {
|
|||
void Utility::updatePalette() {
|
||||
if(program->active == nullptr) return;
|
||||
|
||||
if(config->video.shader == "Display Emulation") {
|
||||
system().paletteUpdate(Emulator::Interface::PaletteMode::None);
|
||||
if(config->video.shader == "Display Emulation" && config->video.driver == "OpenGL") {
|
||||
system().paletteUpdate(Emulator::Interface::PaletteMode::Channel);
|
||||
} else if(config->video.colorEmulation) {
|
||||
system().paletteUpdate(Emulator::Interface::PaletteMode::Emulation);
|
||||
} else {
|
||||
|
@ -198,6 +198,9 @@ void Utility::updateShader() {
|
|||
} else if(config->video.shader == "Blur") {
|
||||
video.set(Video::Shader, (const char*)"");
|
||||
video.set(Video::Filter, Video::FilterLinear);
|
||||
} else if(config->video.shader == "Display Emulation" && config->video.driver != "OpenGL") {
|
||||
video.set(Video::Shader, (const char*)"");
|
||||
video.set(Video::Filter, Video::FilterLinear);
|
||||
} else if(config->video.shader == "Display Emulation") {
|
||||
if(program->active) {
|
||||
string pathname = program->path("Video Shaders/");
|
||||
|
|
Loading…
Reference in New Issue