mirror of https://github.com/bsnes-emu/bsnes.git
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
This commit is contained in:
parent
80c1c9c2ef
commit
4a069761f9
|
@ -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/";
|
||||
|
|
|
@ -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> input;
|
||||
vector<unsigned> order;
|
||||
|
|
|
@ -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 "*.<GtkNotebook>.<GtkHBox>.<GtkButton>" style "PhoenixTabFrameCloseButton"
|
||||
widget_class "*.<GtkNotebook>.<GtkHBox>.<GtkButton>" style "HiroTabFrameCloseButton"
|
||||
)");
|
||||
|
||||
pKeyboard::initialize();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"});
|
||||
}
|
|
@ -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<InputMapping*> mappings; //pointers used so that addresses do not change when arrays are resized
|
||||
};
|
||||
|
||||
struct InputPort {
|
||||
string name;
|
||||
vector<InputDevice> devices;
|
||||
};
|
||||
|
||||
struct InputEmulator {
|
||||
string name;
|
||||
vector<InputPort> 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<HID::Device*> devices;
|
||||
vector<InputEmulator> emulators;
|
||||
Configuration::Document config;
|
||||
};
|
||||
|
||||
extern InputManager* inputManager;
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -4,8 +4,13 @@ struct Presentation : Window {
|
|||
MenuBar menuBar{this};
|
||||
Menu libraryMenu{&menuBar};
|
||||
vector<MenuItem*> 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};
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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});
|
||||
}
|
|
@ -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;
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue