From 458775a481d486fe6652776d79dfd50498268ee9 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sat, 23 May 2015 15:29:18 +1000 Subject: [PATCH] Update to v094r19 release. byuu says: The input port menu was hooked up. Alternate input support was added, although I wasn't able to test rumble support because SDL doesn't support that, and I don't have XInput or udev drivers on FreeBSD. This one's going to be tricky. Maybe I can test via cross-compiling on Windows/GTK. Added mouse capture hotkey, and auto capture/release on toggling fullscreen (as a bonus it hides the mouse cursor.) Added all possible video and input drivers to ruby for BSD systems. Remaining issues before we can release v095: - add slotted cart loader (SGB, BSX, ST) - add DIP switch selection window (NSS) - add timing configuration (video/audio sync) - hide inapplicable options from system menu (eg controller ports and reset button from handheld systems) --- GNUmakefile | 2 - emulator/emulator.hpp | 2 +- nall/GNUmakefile | 2 + target-tomoko/GNUmakefile | 4 +- target-tomoko/input/hotkeys.cpp | 8 ++ target-tomoko/input/input.cpp | 109 ++++++++++++++++++-- target-tomoko/input/input.hpp | 10 ++ target-tomoko/presentation/presentation.cpp | 25 +++++ target-tomoko/presentation/presentation.hpp | 6 +- target-tomoko/program/media.cpp | 1 + target-tomoko/settings/hotkeys.cpp | 5 +- target-tomoko/settings/input.cpp | 54 ++++++++-- target-tomoko/settings/settings.cpp | 9 ++ target-tomoko/settings/settings.hpp | 8 +- 14 files changed, 215 insertions(+), 30 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index 8a9928b5..d5c0aa52 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -49,8 +49,6 @@ else ifeq ($(platform),linux) link += -lX11 -lXext -ldl else ifeq ($(platform),bsd) flags += -march=native - link += -Wl,-rpath=/usr/local/lib - link += -Wl,-rpath=/usr/local/lib/gcc49 link += -Wl,-export-dynamic link += -lX11 -lXext else diff --git a/emulator/emulator.hpp b/emulator/emulator.hpp index 9f00f55c..ea2c7a61 100644 --- a/emulator/emulator.hpp +++ b/emulator/emulator.hpp @@ -3,7 +3,7 @@ namespace Emulator { static const char Name[] = "higan"; - static const char Version[] = "094.18"; + static const char Version[] = "094.19"; static const char Author[] = "byuu"; static const char License[] = "GPLv3"; static const char Website[] = "http://byuu.org/"; diff --git a/nall/GNUmakefile b/nall/GNUmakefile index efcc3bff..d667125e 100644 --- a/nall/GNUmakefile +++ b/nall/GNUmakefile @@ -73,6 +73,8 @@ endif # bsd settings ifeq ($(platform),bsd) flags += -I/usr/local/include + link += -Wl,-rpath=/usr/local/lib + link += -Wl,-rpath=/usr/local/lib/gcc49 endif # cross-compilation support diff --git a/target-tomoko/GNUmakefile b/target-tomoko/GNUmakefile index 68e0122d..166e0848 100644 --- a/target-tomoko/GNUmakefile +++ b/target-tomoko/GNUmakefile @@ -26,9 +26,9 @@ else ifeq ($(platform),linux) ruby += audio.alsa audio.openal audio.oss audio.pulseaudio audio.pulseaudiosimple audio.ao ruby += input.udev input.sdl input.xlib else ifeq ($(platform),bsd) - ruby := video.glx video.xshm + ruby := video.glx video.xv video.xshm video.sdl ruby += audio.openal audio.oss - ruby += input.xlib + ruby += input.sdl input.xlib endif # ruby diff --git a/target-tomoko/input/hotkeys.cpp b/target-tomoko/input/hotkeys.cpp index 6b87104d..aaa5f8ed 100644 --- a/target-tomoko/input/hotkeys.cpp +++ b/target-tomoko/input/hotkeys.cpp @@ -7,6 +7,14 @@ auto InputManager::appendHotkeys() -> void { hotkeys.append(hotkey); } + { auto hotkey = new InputHotkey; + hotkey->name = "Toggle Mouse Capture"; + hotkey->action = [] { + input.acquired() ? input.unacquire() : input.acquire(); + }; + hotkeys.append(hotkey); + } + { auto hotkey = new InputHotkey; hotkey->name = "Save State"; hotkey->action = [] { diff --git a/target-tomoko/input/input.cpp b/target-tomoko/input/input.cpp index d1c30110..c1fcdc38 100644 --- a/target-tomoko/input/input.cpp +++ b/target-tomoko/input/input.cpp @@ -4,10 +4,11 @@ InputManager* inputManager = nullptr; auto InputMapping::bind() -> void { auto token = assignment.split("/"); - if(token.size() < 3) return; + if(token.size() < 3) return unbind(); uint64_t id = token[0].hex(); unsigned group = token[1].decimal(); unsigned input = token[2].decimal(); + string qualifier = token(3, "None"); for(auto& device : inputManager->devices) { if(id != device->id) continue; @@ -15,22 +16,89 @@ auto InputMapping::bind() -> void { this->device = device; this->group = group; this->input = input; + this->qualifier = Qualifier::None; + if(qualifier == "Lo") this->qualifier = Qualifier::Lo; + if(qualifier == "Hi") this->qualifier = Qualifier::Hi; + if(qualifier == "Rumble") this->qualifier = Qualifier::Rumble; break; } } auto InputMapping::bind(HID::Device& device, unsigned group, unsigned input, int16 oldValue, int16 newValue) -> bool { - if(device.group[group].input[input].name == "Escape") return unbind(), true; + if(device.isNull() || (device.isKeyboard() && device.group[group].input[input].name == "Escape")) { + return unbind(), true; + } - this->assignment = {hex(device.id), "/", group, "/", input, "/", device.group[group].input[input].name}; - this->device = &device; - this->group = group; - this->input = input; - return true; + string encoding = {hex(device.id), "/", group, "/", input}; + + if(isDigital()) { + if((device.isKeyboard() && group == HID::Keyboard::GroupID::Button) + || (device.isMouse() && group == HID::Mouse::GroupID::Button) + || (device.isJoypad() && group == HID::Joypad::GroupID::Button)) { + if(newValue) { + this->assignment = encoding; + return bind(), true; + } + } + + if((device.isJoypad() && group == HID::Joypad::GroupID::Axis) + || (device.isJoypad() && group == HID::Joypad::GroupID::Hat)) { + if(newValue < -16384) { + this->assignment = {encoding, "/Lo"}; + return bind(), true; + } + + if(newValue > +16384) { + this->assignment = {encoding, "/Hi"}; + return bind(), true; + } + } + } + + if(isAnalog()) { + 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 || newValue > +16384) { + this->assignment = encoding; + return bind(), true; + } + } + } + + if(isRumble()) { + if(device.isJoypad() && group == HID::Joypad::GroupID::Button) { + if(newValue) { + encoding = {this->assignment, "/Rumble"}; + return bind(), true; + } + } + } + + return false; } auto InputMapping::poll() -> int16 { - if(device) return device->group[group].input[input].value; + if(!device) return 0; + auto value = device->group[group].input[input].value; + + if(isDigital()) { + if(device->isKeyboard() && group == HID::Keyboard::GroupID::Button) return value != 0; + if(device->isMouse() && group == HID::Mouse::GroupID::Button) return value != 0; + if(device->isJoypad() && group == HID::Joypad::GroupID::Button) return value != 0; + if((device->isJoypad() && group == HID::Joypad::GroupID::Axis) + || (device->isJoypad() && group == HID::Joypad::GroupID::Hat)) { + if(qualifier == Qualifier::Lo) return value < -16384; + if(qualifier == Qualifier::Hi) return value > +16384; + } + } + + if(isAnalog()) { + if(device->isMouse() && group == HID::Mouse::GroupID::Axis) return value; + if(device->isJoypad() && group == HID::Joypad::GroupID::Axis) return value >> 8; + if(device->isJoypad() && group == HID::Joypad::GroupID::Hat) return value < 0 ? -1 : value > 0 ? +1 : 0; + } + return 0; } @@ -39,6 +107,24 @@ auto InputMapping::unbind() -> void { this->device = nullptr; this->group = 0; this->input = 0; + this->qualifier = Qualifier::None; +} + +auto InputMapping::assignmentName() const -> string { + if(!device) return "None"; + string path; + path.append(device->name); + path.append(".", device->group[group].name); + path.append(".", device->group[group].input[input].name); + if(qualifier == Qualifier::Lo) path.append(".Lo"); + if(qualifier == Qualifier::Hi) path.append(".Hi"); + if(qualifier == Qualifier::Rumble) path.append(".Rumble"); + return path; +} + +auto InputMapping::deviceName() const -> string { + if(!device) return ""; + return device->id; } // @@ -137,3 +223,10 @@ auto InputManager::quit() -> void { emulators.reset(); hotkeys.reset(); } + +auto InputManager::findMouse() -> HID::Device* { + for(auto device : devices) { + if(device->isMouse()) return device; + } + return nullptr; +} diff --git a/target-tomoko/input/input.hpp b/target-tomoko/input/input.hpp index a47554d6..1db119ba 100644 --- a/target-tomoko/input/input.hpp +++ b/target-tomoko/input/input.hpp @@ -4,12 +4,20 @@ struct InputMapping { auto poll() -> int16; auto unbind() -> void; + auto isDigital() const -> bool { return !link || link->type == 0; } + auto isAnalog() const -> bool { return link && link->type == 1; } + auto isRumble() const -> bool { return link && link->type == 2; } + + auto assignmentName() const -> string; + auto deviceName() const -> string; + string name; string assignment = "None"; Emulator::Interface::Device::Input* link = nullptr; HID::Device* device = nullptr; unsigned group = 0; unsigned input = 0; + enum class Qualifier : unsigned { None, Lo, Hi, Rumble } qualifier = Qualifier::None; }; struct InputHotkey : InputMapping { @@ -40,6 +48,8 @@ struct InputManager { auto onChange(HID::Device& device, unsigned group, unsigned input, int16 oldValue, int16 newValue) -> void; auto quit() -> void; + auto findMouse() -> HID::Device*; + //hotkeys.cpp auto appendHotkeys() -> void; auto pollHotkeys() -> void; diff --git a/target-tomoko/presentation/presentation.cpp b/target-tomoko/presentation/presentation.cpp index a004f3a5..4cb7bb50 100644 --- a/target-tomoko/presentation/presentation.cpp +++ b/target-tomoko/presentation/presentation.cpp @@ -102,6 +102,29 @@ Presentation::Presentation() { resizeViewport(); } +auto Presentation::updateEmulator() -> void { + inputPort1.reset(); + inputPort2.reset(); + if(!emulator) return; + + for(auto n : range(emulator->port)) { + if(n >= 2) break; + auto& port = emulator->port[n]; + auto& menu = (n == 0 ? inputPort1 : inputPort2); + menu.setText(port.name); + + vector items; + for(auto& device : port.device) { + MenuRadioItem item{&menu}; + item.setText(device.name).onActivate([=] { + emulator->connect(port.id, device.id); + }); + items.append(item); + } + MenuRadioItem::group(items); + } +} + auto Presentation::resizeViewport() -> void { signed width = 256; signed height = 240; @@ -154,7 +177,9 @@ auto Presentation::toggleFullScreen() -> void { statusBar.setVisible(false); setResizable(true); setFullScreen(true); + if(!input.acquired()) input.acquire(); } else { + if(input.acquired()) input.unacquire(); setFullScreen(false); setResizable(false); menuBar.setVisible(true); diff --git a/target-tomoko/presentation/presentation.hpp b/target-tomoko/presentation/presentation.hpp index 2c94ef71..4f7b3f2d 100644 --- a/target-tomoko/presentation/presentation.hpp +++ b/target-tomoko/presentation/presentation.hpp @@ -1,5 +1,6 @@ struct Presentation : Window { Presentation(); + auto updateEmulator() -> void; auto resizeViewport() -> void; auto toggleFullScreen() -> void; auto drawSplashScreen() -> void; @@ -10,7 +11,10 @@ struct Presentation : Window { Menu systemMenu{&menuBar}; MenuItem powerSystem{&systemMenu}; MenuItem resetSystem{&systemMenu}; - MenuSeparator systemMenuSeparator{&systemMenu}; + MenuSeparator systemMenuSeparatorPorts{&systemMenu}; + Menu inputPort1{&systemMenu}; + Menu inputPort2{&systemMenu}; + MenuSeparator systemMenuSeparatorUnload{&systemMenu}; MenuItem unloadSystem{&systemMenu}; Menu settingsMenu{&menuBar}; Menu videoScaleMenu{&settingsMenu}; diff --git a/target-tomoko/program/media.cpp b/target-tomoko/program/media.cpp index 2dd037c1..904de5a2 100644 --- a/target-tomoko/program/media.cpp +++ b/target-tomoko/program/media.cpp @@ -29,6 +29,7 @@ auto Program::loadMedia(Emulator::Interface& _emulator, Emulator::Interface::Med presentation->setTitle(emulator->title()); presentation->systemMenu.setVisible(true); presentation->toolsMenu.setVisible(true); + presentation->updateEmulator(); toolsManager->cheatEditor.loadCheats(); toolsManager->stateManager.doRefresh(); } diff --git a/target-tomoko/settings/hotkeys.cpp b/target-tomoko/settings/hotkeys.cpp index 3d5b076e..9057237a 100644 --- a/target-tomoko/settings/hotkeys.cpp +++ b/target-tomoko/settings/hotkeys.cpp @@ -33,10 +33,7 @@ auto HotkeySettings::reloadMappings() -> void { auto HotkeySettings::refreshMappings() -> void { unsigned position = 0; for(auto& hotkey : inputManager->hotkeys) { - auto path = hotkey->assignment.split("/"); - string assignment = path.takeLast(); - string device = path(0); - mappingList.item(position++)->setText(1, assignment).setText(2, device); + mappingList.item(position++)->setText(1, hotkey->assignmentName()).setText(2, hotkey->deviceName()); } mappingList.resizeColumns(); } diff --git a/target-tomoko/settings/input.cpp b/target-tomoko/settings/input.cpp index 6abe57f8..8e2679ea 100644 --- a/target-tomoko/settings/input.cpp +++ b/target-tomoko/settings/input.cpp @@ -11,9 +11,10 @@ InputSettings::InputSettings(TabFrame* parent) : TabFrameItem(parent) { deviceList.onChange([&] { reloadMappings(); }); mappingList.setHeaderVisible(); mappingList.onActivate([&] { assignMapping(); }); - mappingList.onChange([&] { - eraseButton.setEnabled((bool)mappingList.selected()); - }); + mappingList.onChange([&] { updateControls(); }); + assignMouse1.setVisible(false).onActivate([&] { assignMouseInput(0); }); + assignMouse2.setVisible(false).onActivate([&] { assignMouseInput(1); }); + assignMouse3.setVisible(false).onActivate([&] { assignMouseInput(2); }); resetButton.setText("Reset").onActivate([&] { if(MessageDialog("Are you sure you want to erase all mappings for this device?").setParent(*settingsManager).question() == 0) { for(auto& mapping : activeDevice().mappings) mapping->unbind(); @@ -30,6 +31,26 @@ InputSettings::InputSettings(TabFrame* parent) : TabFrameItem(parent) { reloadPorts(); } +auto InputSettings::updateControls() -> void { + eraseButton.setEnabled((bool)mappingList.selected()); + assignMouse1.setVisible(false); + assignMouse2.setVisible(false); + assignMouse3.setVisible(false); + + if(auto mapping = mappingList.selected()) { + auto input = activeDevice().mappings[mapping->offset()]; + + if(input->isDigital()) { + assignMouse1.setVisible().setText("Mouse Left"); + assignMouse2.setVisible().setText("Mouse Middle"); + assignMouse3.setVisible().setText("Mouse Right"); + } else if(input->isAnalog()) { + assignMouse1.setVisible().setText("Mouse X-axis"); + assignMouse2.setVisible().setText("Mouse Y-axis"); + } + } +} + auto InputSettings::activeEmulator() -> InputEmulator& { return inputManager->emulators[emulatorList.selected()->offset()]; } @@ -73,10 +94,7 @@ auto InputSettings::reloadMappings() -> void { auto InputSettings::refreshMappings() -> void { unsigned position = 0; for(auto& mapping : activeDevice().mappings) { - auto path = mapping->assignment.split("/"); - string assignment = path.takeLast(); - string device = path(0); - mappingList.item(position++)->setText(1, assignment).setText(2, device); + mappingList.item(position++)->setText(1, mapping->assignmentName()).setText(2, mapping->deviceName()); } mappingList.resizeColumns(); } @@ -84,16 +102,30 @@ auto InputSettings::refreshMappings() -> void { auto InputSettings::assignMapping() -> void { inputManager->poll(); //clear any pending events first - auto item = mappingList.selected(); - activeMapping = activeDevice().mappings[item->offset()]; + auto mapping = mappingList.selected(); + activeMapping = activeDevice().mappings[mapping->offset()]; //settingsManager->layout.setEnabled(false); settingsManager->statusBar.setText({"Press a key or button to map [", activeMapping->name, "] ..."}); } -auto InputSettings::inputEvent(HID::Device& device, unsigned group, unsigned input, int16 oldValue, int16 newValue) -> void { +auto InputSettings::assignMouseInput(unsigned id) -> void { + if(auto mouse = inputManager->findMouse()) { + if(auto mapping = mappingList.selected()) { + activeMapping = activeDevice().mappings[mapping->offset()]; + + if(activeMapping->isDigital()) { + return inputEvent(*mouse, HID::Mouse::GroupID::Button, id, 0, 1, true); + } else if(activeMapping->isAnalog()) { + return inputEvent(*mouse, HID::Mouse::GroupID::Axis, id, 0, +32767, true); + } + } + } +} + +auto InputSettings::inputEvent(HID::Device& device, unsigned group, unsigned input, int16 oldValue, int16 newValue, bool allowMouseInput) -> void { if(!activeMapping) return; - if(!device.isKeyboard() || oldValue != 0 || newValue != 1) return; + if(device.isMouse() && !allowMouseInput) return; if(activeMapping->bind(device, group, input, oldValue, newValue)) { activeMapping = nullptr; diff --git a/target-tomoko/settings/settings.cpp b/target-tomoko/settings/settings.cpp index a294cf47..c7713866 100644 --- a/target-tomoko/settings/settings.cpp +++ b/target-tomoko/settings/settings.cpp @@ -20,6 +20,15 @@ SettingsManager::SettingsManager() { }); } +auto SettingsManager::setVisible(bool visible) -> SettingsManager& { + if(visible) { + input.refreshMappings(); + hotkeys.refreshMappings(); + } + Window::setVisible(visible); + return *this; +} + auto SettingsManager::show(unsigned setting) -> void { panel.item(setting)->setSelected(); setVisible(); diff --git a/target-tomoko/settings/settings.hpp b/target-tomoko/settings/settings.hpp index e1a88db7..64b183a5 100644 --- a/target-tomoko/settings/settings.hpp +++ b/target-tomoko/settings/settings.hpp @@ -1,5 +1,6 @@ struct InputSettings : TabFrameItem { InputSettings(TabFrame*); + auto updateControls() -> void; auto activeEmulator() -> InputEmulator&; auto activePort() -> InputPort&; auto activeDevice() -> InputDevice&; @@ -8,7 +9,8 @@ struct InputSettings : TabFrameItem { auto reloadMappings() -> void; auto refreshMappings() -> void; auto assignMapping() -> void; - auto inputEvent(HID::Device& device, unsigned group, unsigned input, int16 oldValue, int16 newValue) -> void; + auto assignMouseInput(unsigned id) -> void; + auto inputEvent(HID::Device& device, unsigned group, unsigned input, int16 oldValue, int16 newValue, bool allowMouseInput = false) -> void; InputMapping* activeMapping = nullptr; @@ -19,6 +21,9 @@ struct InputSettings : TabFrameItem { ComboButton deviceList{&selectionLayout, Size{~0, 0}}; ListView mappingList{&layout, Size{~0, ~0}}; HorizontalLayout controlLayout{&layout, Size{~0, 0}}; + Button assignMouse1{&controlLayout, Size{100, 0}}; + Button assignMouse2{&controlLayout, Size{100, 0}}; + Button assignMouse3{&controlLayout, Size{100, 0}}; Widget spacer{&controlLayout, Size{~0, 0}}; Button resetButton{&controlLayout, Size{80, 0}}; Button eraseButton{&controlLayout, Size{80, 0}}; @@ -61,6 +66,7 @@ struct AdvancedSettings : TabFrameItem { struct SettingsManager : Window { SettingsManager(); + auto setVisible(bool visible = true) -> SettingsManager&; auto show(unsigned setting) -> void; VerticalLayout layout{this};