mirror of https://github.com/bsnes-emu/bsnes.git
281 lines
7.8 KiB
C++
281 lines
7.8 KiB
C++
#include "../ethos.hpp"
|
|
#include "hotkeys.cpp"
|
|
InputManager* inputManager = nullptr;
|
|
HID::Null hidNull;
|
|
|
|
void AbstractInput::bind() {
|
|
inputList.reset();
|
|
lstring list = mapping.split(",");
|
|
|
|
for(auto& mapping : list) {
|
|
lstring values = mapping.split("/");
|
|
if(values.size() == 1) continue; //skip "None" mapping
|
|
|
|
uint64_t id = hex(values[0]);
|
|
string group = values(1, "");
|
|
string input = values(2, "");
|
|
string qualifier = values(3, "");
|
|
|
|
Input item;
|
|
for(auto device : inputManager->devices) {
|
|
if(id != device->id) continue;
|
|
if(group == "Rumble") {
|
|
item.device = device;
|
|
item.id = id;
|
|
item.group = 0;
|
|
item.input = 0;
|
|
break;
|
|
}
|
|
if(auto groupID = device->find(group)) {
|
|
if(auto inputID = device->group[groupID()].find(input)) {
|
|
item.device = device;
|
|
item.id = id;
|
|
item.group = groupID();
|
|
item.input = inputID();
|
|
item.qualifier = Input::Qualifier::None;
|
|
if(qualifier == "Lo") item.qualifier = Input::Qualifier::Lo;
|
|
if(qualifier == "Hi") item.qualifier = Input::Qualifier::Hi;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(item.device == nullptr) continue;
|
|
|
|
inputList.append(item);
|
|
}
|
|
}
|
|
|
|
bool AbstractInput::append(string encode) {
|
|
lstring mappings = mapping.split(",");
|
|
if(mappings.find(encode)) return true; //mapping already bound
|
|
if(mapping.empty() || mapping == "None") mapping = encode; //remove "None"
|
|
else mapping.append(",", encode); //add to existing mapping list
|
|
bind();
|
|
return true;
|
|
}
|
|
|
|
//
|
|
|
|
bool DigitalInput::bind(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue) {
|
|
if(device.isNull() || (device.isKeyboard() && device.group[group].input[input].name == "Escape")) {
|
|
inputList.reset();
|
|
mapping = "None";
|
|
return true;
|
|
}
|
|
|
|
string encode = {hex(device.id), "/", device.group[group].name, "/", device.group[group].input[input].name};
|
|
|
|
if((device.isKeyboard() && group == HID::Keyboard::GroupID::Button)
|
|
|| (device.isMouse() && group == HID::Mouse::GroupID::Button)
|
|
|| (device.isJoypad() && group == HID::Joypad::GroupID::Button)
|
|
) {
|
|
if(newValue != 0) return append(encode);
|
|
}
|
|
|
|
if((device.isJoypad() && group == HID::Joypad::GroupID::Axis)
|
|
|| (device.isJoypad() && group == HID::Joypad::GroupID::Hat)
|
|
) {
|
|
if(newValue < -16384) return append({encode, "/Lo"});
|
|
if(newValue > +16384) return append({encode, "/Hi"});
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int16_t DigitalInput::poll() {
|
|
if(program->focused() == false) return 0;
|
|
if(inputList.size() == 0) return 0;
|
|
bool result = logic;
|
|
|
|
for(auto& item : inputList) {
|
|
HID::Device& device = *(item.device);
|
|
int16_t value = device.group[item.group].input[item.input].value;
|
|
bool output = logic;
|
|
if((device.isKeyboard() && item.group == HID::Keyboard::GroupID::Button)
|
|
|| (device.isMouse() && item.group == HID::Mouse::GroupID::Button)
|
|
|| (device.isJoypad() && item.group == HID::Joypad::GroupID::Button)
|
|
) {
|
|
output = value;
|
|
}
|
|
if((device.isJoypad() && item.group == HID::Joypad::GroupID::Axis)
|
|
|| (device.isJoypad() && item.group == HID::Joypad::GroupID::Hat)
|
|
) {
|
|
if(item.qualifier == Input::Qualifier::Lo) output = value < -16384;
|
|
if(item.qualifier == Input::Qualifier::Hi) output = value > +16384;
|
|
}
|
|
if(logic == 0) result |= output;
|
|
if(logic == 1) result &= output;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//
|
|
|
|
bool RelativeInput::bind(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue) {
|
|
if(device.isNull() || (device.isKeyboard() && device.group[group].input[input].name == "Escape")) {
|
|
inputList.reset();
|
|
mapping = "None";
|
|
return true;
|
|
}
|
|
|
|
string encode = {hex(device.id), "/", device.group[group].name, "/", device.group[group].input[input].name};
|
|
|
|
if((device.isMouse() && group == HID::Mouse::GroupID::Axis)
|
|
|| (device.isJoypad() && group == HID::Joypad::GroupID::Axis)
|
|
|| (device.isJoypad() && group == HID::Joypad::GroupID::Hat)
|
|
) {
|
|
if(newValue < -16384) return append(encode);
|
|
if(newValue > +16384) return append(encode);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int16_t RelativeInput::poll() {
|
|
if(program->focused() == false) return 0;
|
|
if(inputList.size() == 0) return 0;
|
|
int16_t result = 0;
|
|
|
|
for(auto& item : inputList) {
|
|
HID::Device& device = *(item.device);
|
|
int16_t value = device.group[item.group].input[item.input].value;
|
|
if(device.isJoypad() && item.group == HID::Joypad::GroupID::Axis) value >>= 8;
|
|
if(device.isJoypad() && item.group == HID::Joypad::GroupID::Hat) value = (value < 0 ? -1 : value > 0 ? + 1 : 0);
|
|
if(device.isMouse() && input.acquired() == false) value = 0;
|
|
result += value;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//
|
|
|
|
bool RumbleInput::bind(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue) {
|
|
if(device.isNull() || (device.isKeyboard() && device.group[group].input[input].name == "Escape")) {
|
|
inputList.reset();
|
|
mapping = "None";
|
|
return true;
|
|
}
|
|
|
|
string encode = {hex(device.id), "/Rumble"};
|
|
|
|
if(device.isJoypad() && group == HID::Joypad::GroupID::Button) {
|
|
if(newValue != 0) return append(encode);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void RumbleInput::rumble(bool enable) {
|
|
if(program->focused() == false) return;
|
|
if(inputList.size() == 0) return;
|
|
|
|
for(auto& item : inputList) {
|
|
input.rumble(item.id, enable);
|
|
}
|
|
}
|
|
|
|
//
|
|
|
|
HotkeyInput::HotkeyInput() {
|
|
logic = 1; //AND
|
|
inputManager->hotkeyMap.append(this);
|
|
}
|
|
|
|
//
|
|
|
|
void InputManager::onChange(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue) {
|
|
if(settings->focused()) {
|
|
inputSettings->inputEvent(device, group, input, oldValue, newValue);
|
|
hotkeySettings->inputEvent(device, group, input, oldValue, newValue);
|
|
}
|
|
}
|
|
|
|
HID::Device* InputManager::findMouse() {
|
|
for(auto device : devices) {
|
|
if(device->isMouse()) return device;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void InputManager::bind() {
|
|
for(auto& input : inputMap) input->bind();
|
|
for(auto& input : hotkeyMap) input->bind();
|
|
}
|
|
|
|
void InputManager::poll() {
|
|
auto devices = input.poll();
|
|
bool changed = devices.size() != this->devices.size();
|
|
if(changed == false) {
|
|
for(unsigned n = 0; n < devices.size(); n++) {
|
|
changed = devices[n] != this->devices[n];
|
|
if(changed) break;
|
|
}
|
|
}
|
|
if(changed == true) {
|
|
this->devices = devices;
|
|
bind();
|
|
}
|
|
|
|
if(presentation->focused()) pollHotkeys();
|
|
}
|
|
|
|
void InputManager::saveConfiguration() {
|
|
config.save(program->path("input.bml"));
|
|
}
|
|
|
|
InputManager::InputManager() {
|
|
inputManager = this;
|
|
bootstrap();
|
|
|
|
input.onChange = {&InputManager::onChange, this};
|
|
}
|
|
|
|
void InputManager::bootstrap() {
|
|
unsigned guid = 0;
|
|
for(auto& emulator : program->emulator) {
|
|
Configuration::Node emulatorNode;
|
|
|
|
for(auto& port : emulator->port) {
|
|
Configuration::Node portNode;
|
|
|
|
for(auto& device : port.device) {
|
|
Configuration::Node deviceNode;
|
|
|
|
for(auto& number : device.order) {
|
|
auto& input = device.input[number];
|
|
|
|
AbstractInput* abstract = nullptr;
|
|
if(input.type == 0) abstract = new DigitalInput;
|
|
if(input.type == 1) abstract = new RelativeInput;
|
|
if(input.type == 2) abstract = new RumbleInput;
|
|
if(abstract == nullptr) continue;
|
|
|
|
abstract->name = string{input.name}.replace(" ", "");
|
|
abstract->mapping = "None";
|
|
abstract->logic = 0; //OR
|
|
|
|
input.guid = guid++;
|
|
inputMap.append(abstract);
|
|
|
|
deviceNode.append(abstract->mapping, abstract->name);
|
|
}
|
|
|
|
portNode.append(deviceNode, string{device.name}.replace(" ", ""));
|
|
}
|
|
|
|
emulatorNode.append(portNode, string{port.name}.replace(" ", ""));
|
|
}
|
|
|
|
config.append(emulatorNode, string{emulator->information.name}.replace(" ", ""));
|
|
}
|
|
|
|
appendHotkeys();
|
|
|
|
config.load(program->path("input.bml"));
|
|
config.save(program->path("input.bml"));
|
|
|
|
bind();
|
|
}
|