From 4a069761f98f435e45eaede7983b85c1fb67b05c Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 2 Mar 2015 20:13:28 +1100 Subject: [PATCH] Update to v094r11 release. byuu says: I've hooked up the input subsystem, and the input manager to assign hotkeys. So far I only have digital buttons working (keyboard only), and I'm not planning on supporting input groups again (mapping multiple physical buttons to one emulated button), but it's progress. As with the rest of tomoko, the code's a lot more compact. The nice thing about redoing code so many times is that each time you get a little bit better at it. The input configuration is saved to ~/.config/tomoko/settings.bml (just realized that I'm an idiot and need to rename it to input.bml) Also hooked up game saves and cartridge unloading. Active controller changing isn't hooked up yet, and I'll probably do it differently. Oh, and I declared the ruby lines for other platforms. Still need to add Cydrak's Windows compilation fixes. I am nothing if not lazy :P --- emulator/emulator.hpp | 2 +- emulator/interface.hpp | 2 +- hiro/gtk/application.cpp | 22 ++-- hiro/gtk/widget/tab-frame.cpp | 15 ++- hiro/gtk/window.cpp | 2 +- target-tomoko/GNUmakefile | 15 ++- target-tomoko/input/input.cpp | 119 ++++++++++++++++++++ target-tomoko/input/input.hpp | 41 +++++++ target-tomoko/presentation/presentation.cpp | 15 ++- target-tomoko/presentation/presentation.hpp | 7 +- target-tomoko/program/interface.cpp | 8 ++ target-tomoko/program/media.cpp | 16 ++- target-tomoko/program/program.cpp | 56 ++++++--- target-tomoko/program/program.hpp | 5 +- target-tomoko/settings/input.cpp | 85 ++++++++++++++ target-tomoko/settings/settings.cpp | 14 +++ target-tomoko/settings/settings.hpp | 37 ++++++ target-tomoko/tomoko.hpp | 2 + 18 files changed, 422 insertions(+), 41 deletions(-) create mode 100644 target-tomoko/input/input.cpp create mode 100644 target-tomoko/input/input.hpp create mode 100644 target-tomoko/settings/input.cpp create mode 100644 target-tomoko/settings/settings.cpp create mode 100644 target-tomoko/settings/settings.hpp diff --git a/emulator/emulator.hpp b/emulator/emulator.hpp index 2582fcda..96a9a47d 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.10"; + static const char Version[] = "094.11"; static const char Author[] = "byuu"; static const char License[] = "GPLv3"; static const char Website[] = "http://byuu.org/"; diff --git a/emulator/interface.hpp b/emulator/interface.hpp index b46fdfe4..7c1ca515 100644 --- a/emulator/interface.hpp +++ b/emulator/interface.hpp @@ -33,7 +33,7 @@ struct Interface { unsigned id; unsigned type; //0 = digital, 1 = analog (relative), 2 = rumble string name; - unsigned guid; + uintptr_t guid; }; vector input; vector order; diff --git a/hiro/gtk/application.cpp b/hiro/gtk/application.cpp index 14783989..1cd3dfa9 100644 --- a/hiro/gtk/application.cpp +++ b/hiro/gtk/application.cpp @@ -37,14 +37,14 @@ void pApplication::initialize() { #if 1 int argc = 1; - char* argv[] = {new char[8], nullptr}; - strcpy(argv[0], "phoenix"); + char* argv[] = {new char[5], nullptr}; + strcpy(argv[0], "hiro"); #else //--g-fatal-warnings will force a trap on Gtk-CRITICAL errors - //this allows gdb to perform a backtrace to find error origin point + //this allows gdb to perform a backtrace to find an error's origin point int argc = 2; - char* argv[] = {new char[8], new char[19], nullptr}; - strcpy(argv[0], "phoenix"); + char* argv[] = {new char[5], new char[19], nullptr}; + strcpy(argv[0], "hiro"); strcpy(argv[1], "--g-fatal-warnings"); #endif char** argvp = argv; @@ -54,20 +54,20 @@ void pApplication::initialize() { g_object_set(gtkSettings, "gtk-button-images", true, nullptr); gtk_rc_parse_string(R"( - style "PhoenixWindow" + style "HiroWindow" { GtkWindow::resize-grip-width = 0 GtkWindow::resize-grip-height = 0 } - class "GtkWindow" style "PhoenixWindow" + class "GtkWindow" style "HiroWindow" - style "PhoenixTreeView" + style "HiroTreeView" { GtkTreeView::vertical-separator = 0 } - class "GtkTreeView" style "PhoenixTreeView" + class "GtkTreeView" style "HiroTreeView" - style "PhoenixTabFrameCloseButton" + style "HiroTabFrameCloseButton" { GtkWidget::focus-line-width = 0 GtkWidget::focus-padding = 0 @@ -75,7 +75,7 @@ void pApplication::initialize() { GtkButton::default-outer-border = {0, 0, 0, 0} GtkButton::inner-border = {0, 1, 0, 0} } - widget_class "*..." style "PhoenixTabFrameCloseButton" + widget_class "*..." style "HiroTabFrameCloseButton" )"); pKeyboard::initialize(); diff --git a/hiro/gtk/widget/tab-frame.cpp b/hiro/gtk/widget/tab-frame.cpp index 0ffa1243..757c771c 100644 --- a/hiro/gtk/widget/tab-frame.cpp +++ b/hiro/gtk/widget/tab-frame.cpp @@ -91,12 +91,23 @@ auto pTabFrame::append(sTabFrameItem item) -> void { } auto pTabFrame::container(mWidget& widget) -> GtkWidget* { - auto widgetLayout = widget.parentLayout(); + //TabFrame holds multiple TabFrameItem controls + //each TabFrameItem has its own GtkWindow; plus its own layout + //we need to recurse up from the widget to its topmost layout before the TabFrameItem + //once we know the topmost layout, we search through all TabFrameItems for a match + mObject* object = &widget; + while(object) { + if(object->parentTabFrameItem()) break; + if(auto layout = object->parentLayout()) { object = layout; continue; } + break; + } + unsigned position = 0; for(auto& item : state().items) { - if(item->state.layout.data() == widgetLayout) return tabs[position].child; + if(item->state.layout.data() == object) return tabs[position].child; position++; } + return nullptr; } diff --git a/hiro/gtk/window.cpp b/hiro/gtk/window.cpp index 11de8a97..443be7da 100644 --- a/hiro/gtk/window.cpp +++ b/hiro/gtk/window.cpp @@ -326,7 +326,7 @@ auto pWindow::setVisible(bool visible) -> void { auto pWindow::_append(mWidget& widget) -> void { if(!widget.self()) return; if(auto parent = widget.parentWidget(true)) { - if(parent->self()) widget.self()->gtkParent = parent->self()->container(widget); + if(auto instance = parent->self()) widget.self()->gtkParent = instance->container(widget); } else { widget.self()->gtkParent = formContainer; } diff --git a/target-tomoko/GNUmakefile b/target-tomoko/GNUmakefile index 48bdf8ff..4348863b 100644 --- a/target-tomoko/GNUmakefile +++ b/target-tomoko/GNUmakefile @@ -8,14 +8,23 @@ include sfc/GNUmakefile include gb/GNUmakefile include gba/GNUmakefile -ui_objects := ui-tomoko ui-program -ui_objects += ui-library ui-presentation +ui_objects := ui-tomoko ui-program ui-input +ui_objects += ui-library ui-settings ui-presentation ui_objects += ruby hiro # platform ifeq ($(platform),windows) + ruby := video.direct3d video.wgl video.directdraw video.gdi + ruby += audio.xaudio2 audio.directsound + ruby += input.windows else ifeq ($(platform),macosx) + ruby := video.cgl + ruby += audio.openal + ruby += input.carbon else ifeq ($(platform),linux) + ruby := video.glx video.xv video.xshm video.sdl + 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 += audio.openal audio.oss @@ -42,7 +51,9 @@ obj/hiro.o: hiro/hiro.cpp $(call rwildcard,hiro/) obj/ui-tomoko.o: $(ui)/tomoko.cpp $(call rwildcard,$(ui)/) obj/ui-program.o: $(ui)/program/program.cpp $(call rwildcard,$(ui)/) +obj/ui-input.o: $(ui)/input/input.cpp $(call rwildcard,$(ui)/) obj/ui-library.o: $(ui)/library/library.cpp $(call rwildcard,$(ui)/) +obj/ui-settings.o: $(ui)/settings/settings.cpp $(call rwildcard,$(ui)/) obj/ui-presentation.o: $(ui)/presentation/presentation.cpp $(call rwildcard,$(ui)/) # targets diff --git a/target-tomoko/input/input.cpp b/target-tomoko/input/input.cpp new file mode 100644 index 00000000..37ae7580 --- /dev/null +++ b/target-tomoko/input/input.cpp @@ -0,0 +1,119 @@ +#include "../tomoko.hpp" +InputManager* inputManager = nullptr; + +auto InputMapping::bind() -> void { + auto token = assignment.split("/"); + if(token.size() < 3) return; + uint64_t id = token[0].hex(); + unsigned group = token[1].decimal(); + unsigned input = token[2].decimal(); + + for(auto& device : inputManager->devices) { + if(id != device->id) continue; + + this->device = device; + this->group = group; + this->input = input; + break; + } +} + +auto InputMapping::bind(HID::Device& device, unsigned group, unsigned input, int16 oldValue, int16 newValue) -> bool { + this->assignment = {hex(device.id), "/", group, "/", input, "/", device.group[group].input[input].name}; + this->device = &device; + this->group = group; + this->input = input; + return true; +} + +auto InputMapping::poll() -> int16 { + if(device) return device->group[group].input[input].value; + return 0; +} + +// + +InputManager::InputManager() { + inputManager = this; + input.onChange = {&InputManager::onChange, this}; + + for(auto& emulator : program->emulators) { + Configuration::Node nodeEmulator; + + emulators.append(InputEmulator()); + auto& inputEmulator = emulators.last(); + inputEmulator.name = emulator->information.name; + + for(auto& port : emulator->port) { + Configuration::Node nodePort; + + inputEmulator.ports.append(InputPort()); + auto& inputPort = inputEmulator.ports.last(); + inputPort.name = port.name; + for(auto& device : port.device) { + Configuration::Node nodeDevice; + + inputPort.devices.append(InputDevice()); + auto& inputDevice = inputPort.devices.last(); + inputDevice.name = device.name; + for(auto number : device.order) { + auto& input = device.input[number]; + inputDevice.mappings.append(new InputMapping()); + auto& inputMapping = inputDevice.mappings.last(); + inputMapping->name = input.name; + inputMapping->link = &input; + input.guid = (uintptr_t)inputMapping; + + nodeDevice.append(inputMapping->assignment, inputMapping->name); + } + + nodePort.append(nodeDevice, string{inputDevice.name}.replace(" ", "")); + } + + nodeEmulator.append(nodePort, string{inputPort.name}.replace(" ", "")); + } + + config.append(nodeEmulator, string{inputEmulator.name}.replace(" ", "")); + } + + config.load({configpath(), "tomoko/settings.bml"}); + config.save({configpath(), "tomoko/settings.bml"}); + poll(); //will call bind(); +} + +auto InputManager::bind() -> void { + for(auto& emulator : emulators) { + for(auto& port : emulator.ports) { + for(auto& device : port.devices) { + for(auto& mapping : device.mappings) { + mapping->bind(); + } + } + } + } +} + +auto InputManager::poll() -> void { + auto devices = input.poll(); + bool changed = devices.size() != this->devices.size(); + if(changed == false) { + for(auto n : range(devices)) { + changed = devices[n] != this->devices[n]; + if(changed) break; + } + } + if(changed == true) { + this->devices = devices; + bind(); + } +} + +auto InputManager::onChange(HID::Device& device, unsigned group, unsigned input, int16 oldValue, int16 newValue) -> void { + if(settingsManager->focused()) { + settingsManager->input.inputEvent(device, group, input, oldValue, newValue); + } +} + +auto InputManager::quit() -> void { + config.save({configpath(), "tomoko/settings.bml"}); +} diff --git a/target-tomoko/input/input.hpp b/target-tomoko/input/input.hpp new file mode 100644 index 00000000..83e528d3 --- /dev/null +++ b/target-tomoko/input/input.hpp @@ -0,0 +1,41 @@ +struct InputMapping { + auto bind() -> void; + auto bind(HID::Device& device, unsigned group, unsigned input, int16 oldValue, int16 newValue) -> bool; + auto poll() -> int16; + + string name; + string assignment = "None"; + Emulator::Interface::Device::Input* link = nullptr; + HID::Device* device = nullptr; + unsigned group = 0; + unsigned input = 0; +}; + +struct InputDevice { + string name; + vector mappings; //pointers used so that addresses do not change when arrays are resized +}; + +struct InputPort { + string name; + vector devices; +}; + +struct InputEmulator { + string name; + vector ports; +}; + +struct InputManager { + InputManager(); + auto bind() -> void; + auto poll() -> void; + auto onChange(HID::Device& device, unsigned group, unsigned input, int16 oldValue, int16 newValue) -> void; + auto quit() -> void; + + vector devices; + vector emulators; + Configuration::Document config; +}; + +extern InputManager* inputManager; diff --git a/target-tomoko/presentation/presentation.cpp b/target-tomoko/presentation/presentation.cpp index a5b172d6..9e67f1b2 100644 --- a/target-tomoko/presentation/presentation.cpp +++ b/target-tomoko/presentation/presentation.cpp @@ -16,18 +16,25 @@ Presentation::Presentation() { } } - superFamicomMenu.setText("Super Famicom"); + systemMenu.setVisible(false); + powerSystem.setText("Power"); + resetSystem.setText("Reset"); + unloadSystem.setText("Unload").onActivate([&] { program->unloadMedia(); }); settingsMenu.setText("Settings"); + showConfiguration.setText("Configuration ...").onActivate([&] { + settingsManager->setVisible(); + settingsManager->setFocused(); + }); toolsMenu.setText("Tools"); statusBar.setFont(Font::sans(8, "Bold")); - onClose(&Application::quit); + onClose([&] { program->quit(); }); setTitle({"tomoko v", Emulator::Version}); - setResizable(false); - setSize({640, 480}); +//setResizable(false); + setSize({512, 480}); setCentered(); } diff --git a/target-tomoko/presentation/presentation.hpp b/target-tomoko/presentation/presentation.hpp index dfccbf18..98063196 100644 --- a/target-tomoko/presentation/presentation.hpp +++ b/target-tomoko/presentation/presentation.hpp @@ -4,8 +4,13 @@ struct Presentation : Window { MenuBar menuBar{this}; Menu libraryMenu{&menuBar}; vector loadBootableMedia; - Menu superFamicomMenu{&menuBar}; + Menu systemMenu{&menuBar}; + MenuItem powerSystem{&systemMenu}; + MenuItem resetSystem{&systemMenu}; + MenuSeparator systemMenuSeparator{&systemMenu}; + MenuItem unloadSystem{&systemMenu}; Menu settingsMenu{&menuBar}; + MenuItem showConfiguration{&settingsMenu}; Menu toolsMenu{&menuBar}; VerticalLayout layout{this}; diff --git a/target-tomoko/program/interface.cpp b/target-tomoko/program/interface.cpp index 8fa24a62..cf839e4d 100644 --- a/target-tomoko/program/interface.cpp +++ b/target-tomoko/program/interface.cpp @@ -10,7 +10,11 @@ auto Program::loadRequest(unsigned id, string path) -> void { return emulator().load(id, stream); } +//request from emulation core to save non-volatile media file auto Program::saveRequest(unsigned id, string path) -> void { + string location = {mediaPaths(emulator().group(id)), path}; + filestream stream{location, file::mode::write}; + return emulator().save(id, stream); } auto Program::videoColor(unsigned source, uint16 alpha, uint16 red, uint16 green, uint16 blue) -> uint32 { @@ -62,6 +66,10 @@ auto Program::audioSample(int16 lsample, int16 rsample) -> void { } auto Program::inputPoll(unsigned port, unsigned device, unsigned input) -> int16 { + auto guid = emulator().port[port].device[device].input[input].guid; + auto mapping = (InputMapping*)guid; + if(mapping) return mapping->poll(); + return 0; } auto Program::inputRumble(unsigned port, unsigned device, unsigned input, bool enable) -> void { diff --git a/target-tomoko/program/media.cpp b/target-tomoko/program/media.cpp index 1c83370d..9fa9f575 100644 --- a/target-tomoko/program/media.cpp +++ b/target-tomoko/program/media.cpp @@ -13,13 +13,27 @@ auto Program::loadMedia(string location) -> void { } auto Program::loadMedia(Emulator::Interface& _emulator, Emulator::Interface::Media& media, const string& location) -> void { + unloadMedia(); + mediaPaths(0) = {userpath(), "Emulation/System/", media.name, ".sys/"}; mediaPaths(media.id) = location; - setEmulator(_emulator); + setEmulator(&_emulator); emulator().paletteUpdate(Emulator::Interface::PaletteMode::Standard); emulator().load(media.id); emulator().power(); presentation->setTitle(emulator().title()); + presentation->systemMenu.setText(emulator().information.name).setVisible(true); +} + +auto Program::unloadMedia() -> void { + if(activeEmulator == nullptr) return; + emulator().unload(); + + setEmulator(nullptr); + + presentation->setTitle({"tomoko v", Emulator::Version}); + presentation->systemMenu.setVisible(false); + drawSplashScreen(); } diff --git a/target-tomoko/program/program.cpp b/target-tomoko/program/program.cpp index 6a6d38e6..8543bb9c 100644 --- a/target-tomoko/program/program.cpp +++ b/target-tomoko/program/program.cpp @@ -9,6 +9,7 @@ Program* program = nullptr; Program::Program() { program = this; + directory::create({configpath(), "tomoko/"}); Application::onMain({&Program::main, this}); emulators.append(new Famicom::Interface); @@ -17,7 +18,9 @@ Program::Program() { emulators.append(new GameBoyAdvance::Interface); for(auto& emulator : emulators) emulator->bind = this; + new InputManager; new LibraryManager; + new SettingsManager; new Presentation; presentation->setVisible(); @@ -34,7 +37,7 @@ Program::Program() { audio.set(Audio::Latency, 80u); if(!audio.init()) { audio.driver("None"); audio.init(); } - input.driver("XInput"); + input.driver("Xlib"); input.set(Input::Handle, presentation->viewport.handle()); if(!input.init()) { input.driver("None"); input.init(); } @@ -45,19 +48,7 @@ Program::Program() { dsp.setResampler(DSP::ResampleEngine::Sinc); dsp.setResamplerFrequency(96000); - uint32* output; - unsigned length; - if(video.lock(output, length, 640, 480)) { - for(auto y : range(480)) { - uint32* dp = output + y * (length >> 2); - for(auto x : range(640)) { - *dp++ = 0xff401010; - } - } - - video.unlock(); - video.refresh(); - } + drawSplashScreen(); } auto Program::emulator() -> Emulator::Interface& { @@ -66,6 +57,8 @@ auto Program::emulator() -> Emulator::Interface& { } auto Program::main() -> void { + inputManager->poll(); + if(activeEmulator == nullptr || emulator().loaded() == false) { audio.clear(); usleep(20 * 1000); @@ -75,6 +68,37 @@ auto Program::main() -> void { emulator().run(); } -auto Program::setEmulator(Emulator::Interface& emulator) -> void { - activeEmulator = &emulator; +auto Program::quit() -> void { + unloadMedia(); + inputManager->quit(); + exit(0); +} + +auto Program::setEmulator(Emulator::Interface* emulator) -> void { + activeEmulator = emulator; +} + +auto Program::drawSplashScreen() -> void { + image emblem{string{userpath(), ".local/share/icons/tomoko.png"}}; + emblem.alphaBlend(0x000000); + emblem.scale(128, 128); + + uint32* output; + unsigned length; + if(video.lock(output, length, 512, 480)) { + for(auto y : range(480)) { + uint32* dp = output + y * (length >> 2); + for(auto x : range(640)) *dp++ = 0xff000000; + } + unsigned z = 0; + for(auto y : range(emblem.height)) { + uint32* dp = output + (480 - emblem.height + y - 8) * (length >> 2) + (512 - emblem.width - 8); + for(auto x : range(emblem.width)) { + *dp++ = emblem.read(emblem.data + z); + z += 4; + } + } + video.unlock(); + video.refresh(); + } } diff --git a/target-tomoko/program/program.hpp b/target-tomoko/program/program.hpp index d3706073..04cfd465 100644 --- a/target-tomoko/program/program.hpp +++ b/target-tomoko/program/program.hpp @@ -3,7 +3,9 @@ struct Program : Emulator::Interface::Bind { Program(); auto emulator() -> Emulator::Interface&; auto main() -> void; - auto setEmulator(Emulator::Interface&) -> void; + auto quit() -> void; + auto setEmulator(Emulator::Interface*) -> void; + auto drawSplashScreen() -> void; //interface.cpp auto loadRequest(unsigned id, string name, string type) -> void override; @@ -21,6 +23,7 @@ struct Program : Emulator::Interface::Bind { //media.cpp auto loadMedia(string location) -> void; auto loadMedia(Emulator::Interface& interface, Emulator::Interface::Media& media, const string& location) -> void; + auto unloadMedia() -> void; DSP dsp; diff --git a/target-tomoko/settings/input.cpp b/target-tomoko/settings/input.cpp new file mode 100644 index 00000000..1db9df71 --- /dev/null +++ b/target-tomoko/settings/input.cpp @@ -0,0 +1,85 @@ +InputSettings::InputSettings(TabFrame* parent) : TabFrameItem(parent) { + setIcon(Icon::Device::Joypad); + setText("Input"); + + layout.setMargin(5); + for(auto& emulator : inputManager->emulators) { + emulatorList.append(ComboButtonItem().setText(emulator.name)); + } + emulatorList.onChange([&] { reloadPorts(); }); + portList.onChange([&] { reloadDevices(); }); + deviceList.onChange([&] { reloadMappings(); }); + mappingList.onActivate([&] { assignMapping(); }); + mappingList.setHeaderVisible(); + resetButton.setText("Reset"); + eraseButton.setText("Erase"); + reloadPorts(); +} + +auto InputSettings::activeEmulator() -> InputEmulator& { + return inputManager->emulators[emulatorList.selected()->offset()]; +} + +auto InputSettings::activePort() -> InputPort& { + return activeEmulator().ports[portList.selected()->offset()]; +} + +auto InputSettings::activeDevice() -> InputDevice& { + return activePort().devices[deviceList.selected()->offset()]; +} + +auto InputSettings::reloadPorts() -> void { + portList.reset(); + for(auto& port : activeEmulator().ports) { + portList.append(ComboButtonItem().setText(port.name)); + } + reloadDevices(); +} + +auto InputSettings::reloadDevices() -> void { + deviceList.reset(); + for(auto& device : activePort().devices) { + deviceList.append(ComboButtonItem().setText(device.name)); + } + reloadMappings(); +} + +auto InputSettings::reloadMappings() -> void { + mappingList.reset(); + mappingList.append(ListViewColumn().setText("Name")); + mappingList.append(ListViewColumn().setText("Mapping")); + for(auto& mapping : activeDevice().mappings) { + mappingList.append(ListViewItem().setText(0, mapping->name)); + } + refreshMappings(); +} + +auto InputSettings::refreshMappings() -> void { + unsigned position = 0; + for(auto& mapping : activeDevice().mappings) { + mappingList.item(position++)->setText(1, mapping->assignment); + } + mappingList.resizeColumns(); +} + +auto InputSettings::assignMapping() -> void { + inputManager->poll(); //clear any pending events first + + auto item = mappingList.selected(); + activeMapping = activeDevice().mappings[item->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 { + if(!activeMapping) return; + if(!device.isKeyboard() || oldValue != 0 || newValue != 1) return; + + if(activeMapping->bind(device, group, input, oldValue, newValue)) { + activeMapping = nullptr; + settingsManager->statusBar.setText(""); + //settingsManager->layout.setEnabled(true); //todo: this isn't enabling child widgets properly (bug in hiro) + refreshMappings(); + } +} diff --git a/target-tomoko/settings/settings.cpp b/target-tomoko/settings/settings.cpp new file mode 100644 index 00000000..8541e638 --- /dev/null +++ b/target-tomoko/settings/settings.cpp @@ -0,0 +1,14 @@ +#include "../tomoko.hpp" +#include "input.cpp" +SettingsManager* settingsManager = nullptr; + +SettingsManager::SettingsManager() { + settingsManager = this; + + layout.setMargin(5); + statusBar.setFont(Font::sans(8, "Bold")); + + setTitle("Configuration Settings"); + setPosition({0, 0}); + setSize({600, 400}); +} diff --git a/target-tomoko/settings/settings.hpp b/target-tomoko/settings/settings.hpp new file mode 100644 index 00000000..7bcc4fd2 --- /dev/null +++ b/target-tomoko/settings/settings.hpp @@ -0,0 +1,37 @@ +struct InputSettings : TabFrameItem { + InputSettings(TabFrame*); + auto activeEmulator() -> InputEmulator&; + auto activePort() -> InputPort&; + auto activeDevice() -> InputDevice&; + auto reloadPorts() -> void; + auto reloadDevices() -> void; + auto reloadMappings() -> void; + auto refreshMappings() -> void; + auto assignMapping() -> void; + auto inputEvent(HID::Device& device, unsigned group, unsigned input, int16 oldValue, int16 newValue) -> void; + + InputMapping* activeMapping = nullptr; + + VerticalLayout layout{this}; + HorizontalLayout selectionLayout{&layout, Size{~0, 0}}; + ComboButton emulatorList{&selectionLayout, Size{~0, 0}}; + ComboButton portList{&selectionLayout, Size{~0, 0}}; + ComboButton deviceList{&selectionLayout, Size{~0, 0}}; + ListView mappingList{&layout, Size{~0, ~0}}; + HorizontalLayout controlLayout{&layout, Size{~0, 0}}; + Widget spacer{&controlLayout, Size{~0, 0}}; + Button resetButton{&controlLayout, Size{80, 0}}; + Button eraseButton{&controlLayout, Size{80, 0}}; +}; + +struct SettingsManager : Window { + SettingsManager(); + + VerticalLayout layout{this}; + TabFrame panelLayout{&layout, Size{~0, ~0}}; + InputSettings input{&panelLayout}; + + StatusBar statusBar{this}; +}; + +extern SettingsManager* settingsManager; diff --git a/target-tomoko/tomoko.hpp b/target-tomoko/tomoko.hpp index ad45ac1e..e681a276 100644 --- a/target-tomoko/tomoko.hpp +++ b/target-tomoko/tomoko.hpp @@ -8,5 +8,7 @@ using namespace ruby; using namespace hiro; #include "program/program.hpp" +#include "input/input.hpp" #include "library/library.hpp" +#include "settings/settings.hpp" #include "presentation/presentation.hpp"