#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); } // //convert an input mapping string to a more human-readable form for the UI string InputManager::sanitize(string mapping, string concatenate) const { lstring values = mapping.split(","); for(auto& value : values) { lstring part = value.split("/"); if(part.size() < 2) continue; //skip "None" mapping if(part[0] == "1") part[0] = "Keyboard"; else if(part[0] == "2") part[0] = "Mouse"; else part[0] = {"Joypad(", part[0].slice(0, 3), ")"}; value = part.merge("."); } return values.merge(concatenate); } 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(); }