Update to v088r10 release.

byuu says:

ethos is going to be absolutely amazing. You guys are in for a treat :D
I'm impressing the hell out of myself with how well-structured this code
is, it's allowing me to do amazing new things.

Just a small sampling of what's in store (and already implemented):

The file browser will display folders as "[ folder name ]", and
cartridge folders as "Game Name" (no extension, no /) [icons would be
nicer, but well ... phoenix.]
Folders are sorted above cartridge folders.
Cartridge folders for other systems do not show up in the list.
Not only are unique paths stored for each image type, your position in
the list is saved across runs.
Some voodoo was added to GTK+ so that all targets even scroll directly
to that item when you open the list. Load->System->Enter restarts your
last game.
That sounds really simple and obvious, but it makes an -incredible-
difference. Didn't realize it until I tried an implementation of it,
wow.

The input mapping list now lets you bind as many hotkeys as you want to
any given input.
So SFC::Port1::Joypad::B = Keyboard::Z or Joypad::Button1 ... no need to
remap everything to switch between keyboard and joypad. Either one
activates the key.
There is a separate Hotkeys tab now. This should hopefully end the
confusion about how to remap hotkeys that users experience.
Hotkeys are different, too. Instead of OR logic, they use AND logic.
So Fullscreen = Keyboard::Alt and Keyboard::Enter. Both must be pressed
to enter the key. This lets you easily implement "super" modifier keys.

The actual codebase has new features the old UI never had, and has about
~50% of the old functionality (so far, of course), yet is only ~25% as
much code.
The entire GUI no longer needs to pull in all the headers for each
emulated system. It just needs a small interface header file.
Then bind the entire system with exactly **two** lines of code.
Everything is dynamically generated for you after that.
This commit is contained in:
Tim Allen 2012-05-01 09:43:23 +10:00
parent 76553756a2
commit 9ad8b7eaac
37 changed files with 1101 additions and 162 deletions

View File

@ -6,7 +6,7 @@ gb := gb
gba := gba
profile := accuracy
target := ui
target := ethos
# options += console

View File

@ -1,7 +1,12 @@
#ifndef EMULATOR_HPP
#define EMULATOR_HPP
static const char Version[] = "088.09";
namespace Emulator {
static const char Name[] = "bsnes";
static const char Version[] = "088.10";
static const char Author[] = "byuu";
static const char License[] = "GPLv3";
}
#include <nall/platform.hpp>
#include <nall/algorithm.hpp>

View File

@ -14,6 +14,7 @@ struct Interface {
} information;
struct Firmware {
string displayname;
string name;
unsigned id;
};
@ -47,6 +48,7 @@ struct Interface {
unsigned id;
struct Input {
string name;
unsigned type; //0 = digital, 1 = analog
unsigned id;
unsigned guid;
};

View File

@ -4,4 +4,101 @@ namespace Famicom {
Interface *interface = nullptr;
bool Interface::loaded() {
return cartridge.loaded();
}
void Interface::load(unsigned id, const stream &memory, const string &markup) {
if(id == 0) {
cartridge.load(markup, memory);
system.power();
input.connect(0, Input::Device::Joypad);
input.connect(1, Input::Device::Joypad);
}
}
void Interface::unload() {
cartridge.unload();
}
void Interface::power() {
system.power();
}
void Interface::reset() {
system.reset();
}
void Interface::run() {
system.run();
}
void Interface::updatePalette() {
video.generate_palette();
}
Interface::Interface() {
interface = this;
information.name = "Famicom";
information.width = 256;
information.height = 240;
information.frequency = 1789772;
information.ports = 2;
information.resettable = true;
{
Media media;
media.displayname = "Famicom";
media.name = "program.rom";
media.filter = "*.fc";
media.id = 0;
this->media.append(media);
}
{
Port port;
port.name = "Port 1";
port.id = 0;
{
Port::Device device;
device.name = "Controller";
device.id = 0;
device.input.append({"A", 0, 0});
device.input.append({"B", 0, 1});
device.input.append({"Select", 0, 2});
device.input.append({"Start", 0, 3});
device.input.append({"Up", 0, 4});
device.input.append({"Down", 0, 5});
device.input.append({"Left", 0, 6});
device.input.append({"Right", 0, 7});
device.displayinput = {4, 5, 6, 7, 1, 0, 2, 3};
port.device.append(device);
}
this->port.append(port);
}
{
Port port;
port.name = "Port 2";
port.id = 1;
{
Port::Device device;
device.name = "Controller";
device.id = 0;
device.input.append({"A", 0, 0});
device.input.append({"B", 0, 1});
device.input.append({"Select", 0, 2});
device.input.append({"Start", 0, 3});
device.input.append({"Up", 0, 4});
device.input.append({"Down", 0, 5});
device.input.append({"Left", 0, 6});
device.input.append({"Right", 0, 7});
device.displayinput = {4, 5, 6, 7, 1, 0, 2, 3};
port.device.append(device);
}
this->port.append(port);
}
}
}

View File

@ -1,4 +1,23 @@
#ifndef FC_HPP
namespace Famicom {
#endif
struct Interface : Emulator::Interface {
bool loaded();
void load(unsigned id, const stream &memory, const string &markup = "");
void unload();
void power();
void reset();
void run();
void updatePalette();
Interface();
};
extern Interface *interface;
#ifndef FC_HPP
}
#endif

View File

@ -1,5 +1,5 @@
#ifndef GAMEBOY_HPP
#define GAMEBOY_HPP
#ifndef GB_HPP
#define GB_HPP
#include <emulator/emulator.hpp>
#include <processor/lr35902/lr35902.hpp>

View File

@ -4,4 +4,121 @@ namespace GameBoy {
Interface *interface = nullptr;
bool Interface::loaded() {
return cartridge.loaded();
}
void Interface::load(unsigned id, const stream &stream, const string &markup) {
if(id == 0) stream.read(system.bootROM.dmg, min( 256u, stream.size()));
if(id == 1) stream.read(system.bootROM.sgb, min( 256u, stream.size()));
if(id == 2) stream.read(system.bootROM.cgb, min(2048u, stream.size()));
if(id == 3) {
cartridge.load(System::Revision::GameBoy, markup, stream);
system.power();
}
if(id == 4) {
cartridge.load(System::Revision::SuperGameBoy, markup, stream);
system.power();
}
if(id == 5) {
cartridge.load(System::Revision::GameBoyColor, markup, stream);
system.power();
}
}
void Interface::unload() {
cartridge.unload();
}
void Interface::power() {
system.power();
}
void Interface::reset() {
system.power();
}
void Interface::run() {
system.run();
}
void Interface::updatePalette() {
video.generate_palette();
}
Interface::Interface() {
interface = this;
information.name = "Game Boy";
information.width = 160;
information.height = 144;
information.frequency = 4194304;
information.ports = 1;
information.resettable = false;
{
Firmware firmware;
firmware.displayname = "Game Boy";
firmware.name = "Game Boy.sys/boot.rom";
firmware.id = 0;
this->firmware.append(firmware);
}
{
Firmware firmware;
firmware.displayname = "Super Game Boy";
firmware.name = "Super Game Boy.sfc/boot.rom";
firmware.id = 1;
this->firmware.append(firmware);
}
{
Firmware firmware;
firmware.displayname = "Game Boy Color";
firmware.name = "Game Boy Color.sys/boot.rom";
firmware.id = 2;
this->firmware.append(firmware);
}
{
Media media;
media.displayname = "Game Boy";
media.name = "program.rom";
media.filter = "*.gb";
media.id = 3;
this->media.append(media);
}
{
Media media;
media.displayname = "Game Boy Color";
media.name = "program.rom";
media.filter = "*.gbc";
media.id = 5;
this->media.append(media);
}
{
Port port;
port.name = "Device";
port.id = 0;
{
Port::Device device;
device.name = "Controller";
device.id = 0;
device.input.append({"Up", 0, 0});
device.input.append({"Down", 0, 1});
device.input.append({"Left", 0, 2});
device.input.append({"Right", 0, 3});
device.input.append({"B", 0, 4});
device.input.append({"A", 0, 5});
device.input.append({"Select", 0, 6});
device.input.append({"Start", 0, 7});
device.displayinput = {0, 1, 2, 3, 4, 5, 6, 7};
port.device.append(device);
}
this->port.append(port);
}
}
}

View File

@ -1,6 +1,27 @@
#ifndef GB_HPP
namespace GameBoy {
#endif
struct Interface : Emulator::Interface {
//Super Game Boy bindings
virtual void lcdScanline() {}
virtual void joypWrite(bool p15, bool p14) {}
bool loaded();
void load(unsigned id, const stream &memory, const string &markup = "");
void unload();
void power();
void reset();
void run();
void updatePalette();
Interface();
};
extern Interface *interface;
#ifndef GB_HPP
}
#endif

View File

@ -59,43 +59,44 @@ Interface::Interface() {
information.resettable = false;
{
Firmware firmware;
firmware.name = "BIOS";
firmware.id = 1;
this->firmware.append(firmware);
Firmware firmware;
firmware.displayname = "Game Boy Advance";
firmware.name = "Game Boy Advance.sys/bios.rom";
firmware.id = 1;
this->firmware.append(firmware);
}
{
Media media;
media.displayname = "Game Boy Advance";
media.name = "program.rom";
media.filter = "*.gba";
media.id = 0;
this->media.append(media);
Media media;
media.displayname = "Game Boy Advance";
media.name = "program.rom";
media.filter = "*.gba";
media.id = 0;
this->media.append(media);
}
{
Port port;
port.name = "Device";
port.id = 0;
Port port;
port.name = "Device";
port.id = 0;
{
Port::Device device;
device.name = "Controller";
device.id = 0;
device.input.append({"A", 0});
device.input.append({"B", 1});
device.input.append({"Select", 2});
device.input.append({"Start", 3});
device.input.append({"Right", 4});
device.input.append({"Left", 5});
device.input.append({"Up", 6});
device.input.append({"Down", 7});
device.input.append({"R", 8});
device.input.append({"L", 9});
device.displayinput = { 6, 7, 5, 4, 1, 0, 9, 8, 2, 3 };
port.device.append(device);
Port::Device device;
device.name = "Controller";
device.id = 0;
device.input.append({"A", 0, 0});
device.input.append({"B", 0, 1});
device.input.append({"Select", 0, 2});
device.input.append({"Start", 0, 3});
device.input.append({"Right", 0, 4});
device.input.append({"Left", 0, 5});
device.input.append({"Up", 0, 6});
device.input.append({"Down", 0, 7});
device.input.append({"R", 0, 8});
device.input.append({"L", 0, 9});
device.displayinput = {6, 7, 5, 4, 1, 0, 9, 8, 2, 3};
port.device.append(device);
}
this->port.append(port);
this->port.append(port);
}
}

View File

@ -129,13 +129,13 @@ namespace nall {
file_offset = req_offset;
}
int offset() const {
if(!fp) return -1; //file not open
unsigned offset() const {
if(!fp) return 0; //file not open
return file_offset;
}
int size() const {
if(!fp) return -1; //file not open
unsigned size() const {
if(!fp) return 0; //file not open
return file_size;
}
@ -227,7 +227,7 @@ namespace nall {
file() {
memset(buffer, 0, sizeof buffer);
buffer_offset = -1;
buffer_offset = -1; //invalidate buffer
buffer_dirty = false;
fp = 0;
file_offset = 0;

View File

@ -104,6 +104,13 @@ void pListView::setSelection(unsigned row) {
GtkTreeIter iter;
if(gtk_tree_model_get_iter_from_string(model, &iter, string(row)) == false) return;
gtk_tree_selection_select_iter(selection, &iter);
//scroll window to selected item
char *path = gtk_tree_model_get_string_from_iter(model, &iter);
GtkTreePath *treePath = gtk_tree_path_new_from_string(path);
gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(subWidget), treePath, nullptr, true, 0.5, 0.0);
gtk_tree_path_free(treePath);
g_free(path);
}
void pListView::constructor() {

View File

@ -223,7 +223,7 @@ void Cartridge::parse_markup_necdsp(XML::Node &root) {
string firmware = root["firmware"].data;
string sha256 = root["sha256"].data;
string path = interface->path(Slot::Base, firmware);
string path = interface->path((unsigned)Slot::Base, firmware);
unsigned promsize = (necdsp.revision == NECDSP::Revision::uPD7725 ? 2048 : 16384);
unsigned dromsize = (necdsp.revision == NECDSP::Revision::uPD7725 ? 1024 : 2048);
unsigned filesize = promsize * 3 + dromsize * 2;
@ -288,7 +288,7 @@ void Cartridge::parse_markup_hitachidsp(XML::Node &root) {
string firmware = root["firmware"].data;
string sha256 = root["sha256"].data;
string path = interface->path(Slot::Base, firmware);
string path = interface->path((unsigned)Slot::Base, firmware);
file fp;
if(fp.open(path, file::mode::read) == false) {
interface->message({ "Warning: Hitachi DSP firmware ", firmware, " is missing." });
@ -338,7 +338,7 @@ void Cartridge::parse_markup_armdsp(XML::Node &root) {
string firmware = root["firmware"].data;
string sha256 = root["sha256"].data;
string path = interface->path(Slot::Base, firmware);
string path = interface->path((unsigned)Slot::Base, firmware);
file fp;
if(fp.open(path, file::mode::read) == false) {
interface->message({ "Warning: ARM DSP firmware ", firmware, " is missing." });
@ -520,7 +520,7 @@ void Cartridge::parse_markup_obc1(XML::Node &root) {
void Cartridge::parse_markup_msu1(XML::Node &root) {
if(root.exists() == false) {
has_msu1 = file::exists(interface->path(Cartridge::Slot::Base, "msu1.rom"));
has_msu1 = file::exists(interface->path((unsigned)Cartridge::Slot::Base, "msu1.rom"));
if(has_msu1) {
Mapping m({ &MSU1::mmio_read, &msu1 }, { &MSU1::mmio_write, &msu1 });
m.banklo = 0x00, m.bankhi = 0x3f, m.addrlo = 0x2000, m.addrhi = 0x2007;

View File

@ -22,7 +22,7 @@ void Link::init() {
void Link::load() {
if(opened()) close();
string basename = interface->path(Cartridge::Slot::Base, "");
string basename = interface->path((unsigned)Cartridge::Slot::Base, "");
string name = program != "" ? program : notdir(basename);
string path = dir(basename);
if(open(name, path)) {

View File

@ -52,7 +52,7 @@ void MSU1::init() {
void MSU1::load() {
if(datafile.open()) datafile.close();
datafile.open(interface->path(Cartridge::Slot::Base, "msu1.rom"), file::mode::read);
datafile.open(interface->path((unsigned)Cartridge::Slot::Base, "msu1.rom"), file::mode::read);
}
void MSU1::unload() {
@ -112,7 +112,7 @@ void MSU1::mmio_write(unsigned addr, uint8 data) {
case 4: mmio.audio_track = (mmio.audio_track & 0xff00) | (data << 0);
case 5: mmio.audio_track = (mmio.audio_track & 0x00ff) | (data << 8);
if(audiofile.open()) audiofile.close();
if(audiofile.open(interface->path(Cartridge::Slot::Base, { "track-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
if(audiofile.open(interface->path((unsigned)Cartridge::Slot::Base, { "track-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
uint32 header = audiofile.readm(4);
if(header != 0x4d535531) { //verify 'MSU1' header
audiofile.close();

View File

@ -16,12 +16,12 @@ void MSU1::serialize(serializer &s) {
s.integer(mmio.audio_play);
if(datafile.open()) datafile.close();
if(datafile.open(interface->path(Cartridge::Slot::Base, "msu1.rom"), file::mode::read)) {
if(datafile.open(interface->path((unsigned)Cartridge::Slot::Base, "msu1.rom"), file::mode::read)) {
datafile.seek(mmio.data_offset);
}
if(audiofile.open()) audiofile.close();
if(audiofile.open(interface->path(Cartridge::Slot::Base, { "track-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
if(audiofile.open(interface->path((unsigned)Cartridge::Slot::Base, { "track-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
audiofile.seek(mmio.audio_offset);
}
}

View File

@ -2,7 +2,8 @@
uint2 Gamepad::data() {
if(counter >= 16) return 1;
uint2 result = interface->inputPoll(port, (unsigned)Input::Device::Joypad, counter);
uint2 result = 0;
if(counter < 12) result = interface->inputPoll(port, (unsigned)Input::Device::Joypad, counter);
if(latched == 0) counter++;
return result;
}

View File

@ -121,7 +121,7 @@ USART::USART(bool port) : Controller(port) {
txlength = 0;
txdata = 0;
string filename = interface->path(Cartridge::Slot::Base, "usart.so");
string filename = interface->path((unsigned)Cartridge::Slot::Base, "usart.so");
if(open_absolute(filename)) {
init = sym("usart_init");
main = sym("usart_main");

View File

@ -4,8 +4,134 @@ namespace SuperFamicom {
Interface *interface = nullptr;
void Interface::message(const string &text) {
print(text, "\n");
bool Interface::loaded() {
return cartridge.loaded();
}
void Interface::load(unsigned id, const stream &stream, const string &markup) {
if(id == 0) {
stream.read(smp.iplrom, min(64u, stream.size()));
}
if(id == 1) {
cartridge.rom.copy(stream);
cartridge.load(Cartridge::Mode::Normal, markup);
system.power();
input.connect(0, Input::Device::Joypad);
input.connect(1, Input::Device::Joypad);
}
}
void Interface::unload() {
cartridge.unload();
}
void Interface::power() {
system.power();
}
void Interface::reset() {
system.reset();
}
void Interface::run() {
system.run();
}
void Interface::updatePalette() {
video.generate_palette();
}
Interface::Interface() {
interface = this;
information.name = "Super Famicom";
information.width = 256;
information.height = 240;
information.frequency = 32040;
information.ports = 2;
information.resettable = true;
{
Firmware firmware;
firmware.displayname = "Super Famicom";
firmware.name = "Super Famicom.sys/spc700.rom";
firmware.id = 0;
this->firmware.append(firmware);
}
{
Media media;
media.displayname = "Super Famicom";
media.name = "program.rom";
media.filter = "*.sfc";
media.id = 1;
this->media.append(media);
}
{
Port port;
port.name = "Port 1";
port.id = 0;
{
Port::Device device;
device.name = "None";
device.id = 0;
port.device.append(device);
}
{
Port::Device device;
device.name = "Controller";
device.id = 1;
device.input.append({"B", 0, 0});
device.input.append({"Y", 0, 1});
device.input.append({"Select", 0, 2});
device.input.append({"Start", 0, 3});
device.input.append({"Up", 0, 4});
device.input.append({"Down", 0, 5});
device.input.append({"Left", 0, 6});
device.input.append({"Right", 0, 7});
device.input.append({"A", 0, 8});
device.input.append({"X", 0, 9});
device.input.append({"L", 0, 10});
device.input.append({"R", 0, 11});
device.displayinput = {4, 5, 6, 7, 0, 8, 1, 9, 10, 11, 2, 3};
port.device.append(device);
}
this->port.append(port);
}
{
Port port;
port.name = "Port 2";
port.id = 1;
{
Port::Device device;
device.name = "None";
device.id = 0;
port.device.append(device);
}
{
Port::Device device;
device.name = "Controller";
device.id = 1;
device.input.append({"B", 0, 0});
device.input.append({"Y", 0, 1});
device.input.append({"Select", 0, 2});
device.input.append({"Start", 0, 3});
device.input.append({"Up", 0, 4});
device.input.append({"Down", 0, 5});
device.input.append({"Left", 0, 6});
device.input.append({"Right", 0, 7});
device.input.append({"A", 0, 8});
device.input.append({"X", 0, 9});
device.input.append({"L", 0, 10});
device.input.append({"R", 0, 11});
device.displayinput = {4, 5, 6, 7, 0, 8, 1, 9, 10, 11, 2, 3};
port.device.append(device);
}
this->port.append(port);
}
}
}

View File

@ -1,6 +1,26 @@
#ifndef SFC_HPP
namespace SuperFamicom {
#endif
struct Interface : Emulator::Interface {
virtual string path(Cartridge::Slot slot, const string &hint) = 0;
virtual void message(const string &text);
virtual string path(unsigned slot, const string &hint) { return ""; }
virtual void message(const string &text) {}
bool loaded();
void load(unsigned id, const stream &stream, const string &markup = "");
void unload();
void power();
void reset();
void run();
void updatePalette();
Interface();
};
extern Interface *interface;
#ifndef SFC_HPP
}
#endif

View File

@ -1,8 +1,14 @@
#include <fc/interface/interface.hpp>
#include <sfc/interface/interface.hpp>
#include <gb/interface/interface.hpp>
#include <gba/interface/interface.hpp>
void Application::bootstrap() {
interface = new Interface;
emulator.append(new Famicom::Interface);
emulator.append(new SuperFamicom::Interface);
emulator.append(new GameBoy::Interface);
emulator.append(new GameBoyAdvance::Interface);
for(auto &system : emulator) {
@ -12,23 +18,9 @@ void Application::bootstrap() {
system->callback.inputPoll = {&Interface::inputPoll, interface};
system->updatePalette();
string basepath = path({system->information.name, ".sys/"});
string manifest;
manifest.readfile({basepath, "manifest.xml"});
XML::Document document(manifest);
for(auto &firmware : system->firmware) {
string path = firmware.name;
path.lower();
for(auto &root : document) {
for(auto &node : root) {
if(node.name == path) {
filestream fs({basepath, node["firmware"].data});
system->load(firmware.id, fs);
}
}
}
filestream fs{application->path(firmware.name)};
system->load(firmware.id, fs);
}
}
}

View File

@ -2,6 +2,7 @@
#include "bootstrap.cpp"
Application *application = nullptr;
DSP dspaudio;
Emulator::Interface& system() {
struct application_interface_null{};
@ -67,6 +68,7 @@ Application::Application(int argc, char **argv) {
videoSettings = new VideoSettings;
audioSettings = new AudioSettings;
inputSettings = new InputSettings;
hotkeySettings = new HotkeySettings;
settings = new Settings;
presentation->setVisible();
@ -79,19 +81,28 @@ Application::Application(int argc, char **argv) {
audio.driver("ALSA");
audio.set(Audio::Handle, presentation->viewport.handle());
audio.set(Audio::Synchronize, false);
audio.set(Audio::Synchronize, true);
audio.set(Audio::Latency, 80u);
audio.set(Audio::Frequency, 32768u);
audio.set(Audio::Frequency, 48000u);
audio.init();
input.driver("SDL");
input.set(Input::Handle, presentation->viewport.handle());
input.init();
dspaudio.setPrecision(16);
dspaudio.setVolume(2.0);
dspaudio.setBalance(0.0);
dspaudio.setResampler(DSP::ResampleEngine::Linear);
dspaudio.setResamplerFrequency(48000u);
while(quit == false) {
OS::processEvents();
run();
}
browser->saveConfiguration();
inputManager->saveConfiguration();
}
Application::~Application() {

View File

@ -3,6 +3,7 @@
#include <nall/platform.hpp>
#include <nall/config.hpp>
#include <nall/directory.hpp>
#include <nall/dsp.hpp>
#include <nall/map.hpp>
#include <nall/stream/file.hpp>
#include <nall/stream/memory.hpp>
@ -48,3 +49,4 @@ struct Application {
};
extern Application *application;
extern DSP dspaudio;

View File

@ -1,6 +1,7 @@
Browser *browser = nullptr;
Browser::Browser() {
bootstrap();
setGeometry({128, 128, 640, 400});
layout.setMargin(5);
@ -44,40 +45,112 @@ Browser::Browser() {
synchronize();
}
void Browser::synchronize() {
openButton.setEnabled(fileList.selected());
if(fileList.selected()) {
for(auto &folder : folderList) {
if(folder.filter == media.filter) {
folder.selection = fileList.selection();
}
}
}
}
void Browser::saveConfiguration() {
config.save(application->path("paths.cfg"));
}
void Browser::bootstrap() {
for(auto &emulator : application->emulator) {
for(auto &media : emulator->media) {
bool found = false;
for(auto &folder : folderList) {
if(folder.filter == media.filter) {
found = true;
break;
}
}
if(found == true) continue;
Folder folder;
folder.filter = media.filter;
folder.path = application->basepath;
folder.selection = 0;
folderList.append(folder);
}
}
for(auto &folder : folderList) {
config.append(folder.path, folder.filter);
config.append(folder.selection, string{folder.filter, "::selection"});
}
config.load(application->path("paths.cfg"));
config.save(application->path("paths.cfg"));
}
void Browser::open(Emulator::Interface::Media &media, function<void (string)> callback) {
this->media = media;
this->callback = callback;
setTitle({"Load ", media.displayname});
setPath("/media/sdb1/root/cartridges/Game Boy Advance/");
string path;
unsigned selection = 0;
for(auto &folder : folderList) {
if(folder.filter == media.filter) {
path = folder.path;
selection = folder.selection;
break;
}
}
if(path.empty()) path = application->basepath;
setPath(path, selection);
filterLabel.setText({"Files of type: ", media.filter});
setVisible();
fileList.setFocused();
}
void Browser::synchronize() {
openButton.setEnabled(fileList.selected());
}
void Browser::setPath(const string &path, unsigned selection) {
for(auto &folder : folderList) {
if(folder.filter == media.filter) folder.path = path;
}
void Browser::setPath(const string &path) {
this->path = path;
pathEdit.setText(path);
fileList.reset();
filenameList.reset();
lstring contents = directory::contents(path);
lstring contents = directory::folders(path);
for(auto &filename : contents) {
if(filename.endswith("/")) {
filenameList.append(filename);
} else if(filename.wildcard(media.filter)) {
string filter = {media.filter, "/"};
if(!filename.wildcard(R"(*.??/)") && !filename.wildcard(R"(*.???/)")) {
string name = filename;
name.rtrim<1>("/");
name = {"[ ", name, " ]"};
filenameList.append(filename);
fileList.append(name);
}
}
for(auto &filename : filenameList) fileList.append(filename);
fileList.setSelection(0);
for(auto &filename : contents) {
string filter = {media.filter, "/"};
if(filename.wildcard(R"(*.??/)") || filename.wildcard(R"(*.???/)")) {
if(filename.wildcard(filter)) {
string name = filename;
filter.ltrim<1>("*");
name.rtrim<1>(filter);
filenameList.append(filename);
fileList.append(name);
}
}
}
fileList.setSelection(selection);
fileList.setFocused();
synchronize();
}
@ -85,22 +158,8 @@ void Browser::setPath(const string &path) {
void Browser::fileListActivate() {
unsigned selection = fileList.selection();
string filename = filenameList[selection];
if(filename.endswith("/")) {
if(loadFolder({path, filename})) return;
return setPath({path, filename});
}
loadFile({path, filename});
}
bool Browser::loadFolder(const string &path) {
string requested = path;
requested.rtrim<1>("/");
if(requested.wildcard(media.filter) == false) return false;
loadFile(path);
return true;
}
void Browser::loadFile(const string &filename) {
string filter = {media.filter, "/"};
if(filename.wildcard(filter) == false) return setPath({path, filename});
setVisible(false);
if(callback) callback(filename);
if(callback) callback({path, filename});
}

View File

@ -10,19 +10,27 @@ struct Browser : Window {
Button openButton;
void open(Emulator::Interface::Media &media, function<void (string)> callback);
void saveConfiguration();
void synchronize();
void bootstrap();
Browser();
public:
private:
configuration config;
struct Folder {
string filter;
string path;
unsigned selection;
};
vector<Folder> folderList;
Emulator::Interface::Media media;
function<void (string)> callback;
string path;
lstring filenameList;
void synchronize();
void setPath(const string &path);
void setPath(const string &path, unsigned selection = 0);
void fileListActivate();
bool loadFolder(const string &path);
void loadFile(const string &filename);
};
extern Browser *browser;

View File

@ -1,15 +1,24 @@
Presentation *presentation = nullptr;
void Presentation::synchronize() {
for(auto &system : emulatorList) system->menu.setVisible(false);
for(auto &system : emulatorList) {
system->menu.setVisible(system->interface == application->active);
if(system->interface == application->active) {
activeSystem = system;
system->menu.setVisible(true);
return;
}
}
}
Presentation::Presentation() {
void Presentation::setSystemName(const string &name) {
if(activeSystem) activeSystem->menu.setText(name);
}
Presentation::Presentation() : activeSystem(nullptr) {
bootstrap();
setTitle("ethos");
setTitle({Emulator::Name, " v", Emulator::Version});
setGeometry({1024, 600, 720, 480});
setBackgroundColor({0, 0, 0});
setMenuFont(application->normalFont);
@ -22,13 +31,9 @@ Presentation::Presentation() {
configurationSettings.setText("Configuration ...");
toolsMenu.setText("Tools");
for(auto &system : emulatorList) {
loadMenu.append(system->load);
}
append(loadMenu);
for(auto &system : emulatorList) {
append(system->menu);
}
for(auto &item : loadList) loadMenu.append(*item);
for(auto &system : emulatorList) append(system->menu);
append(settingsMenu);
settingsMenu.append(configurationSettings);
append(toolsMenu);
@ -48,17 +53,18 @@ void Presentation::bootstrap() {
System *system = new System;
system->interface = emulator;
system->name = emulator->information.name;
system->filter = "*.gba";
for(auto &media : emulator->media) {
Item *item = new Item;
item->setText({media.displayname, " ..."});
item->onActivate = [=, &media] {
browser->open(media, [=, &media](string filename) {
utility->loadMedia(system->interface, media, filename);
});
};
loadList.append(item);
}
system->load.setText(system->name);
system->load.onActivate = [=] {
browser->open(system->interface->media[0], [=](string filename) {
utility->loadMedia(system->interface, system->interface->media[0], filename);
});
};
system->menu.setText(system->name);
system->menu.setText(emulator->information.name);
system->power.setText("Power");
system->reset.setText("Reset");
system->unload.setText("Unload");

View File

@ -5,9 +5,6 @@ struct Presentation : Window {
struct System {
Emulator::Interface *interface;
string name;
string filter;
Item load;
Menu menu;
Item power;
Item reset;
@ -18,13 +15,18 @@ struct Presentation : Window {
vector<System*> emulatorList;
Menu loadMenu;
vector<Item*> loadList;
Menu settingsMenu;
Item configurationSettings;
Menu toolsMenu;
void synchronize();
void setSystemName(const string &name);
void bootstrap();
Presentation();
private:
System *activeSystem;
};
extern Presentation *presentation;

View File

@ -0,0 +1,56 @@
void InputManager::appendHotkeys() {
{
auto hotkey = new HotkeyInput;
hotkey->name = "Fast Forward";
hotkey->mapping = "KB0::Tilde";
hotkey->logic = 1;
hotkeyMap.append(hotkey);
hotkey->press = [] {
video.set(Video::Synchronize, false);
audio.set(Audio::Synchronize, false);
};
hotkey->release = [] {
video.set(Video::Synchronize, false);
audio.set(Audio::Synchronize, true);
};
}
{
auto hotkey = new HotkeyInput;
hotkey->name = "Power Cycle";
hotkey->mapping = "None";
hotkey->logic = 1;
hotkeyMap.append(hotkey);
hotkey->press = [] {
utility->power();
};
}
{
auto hotkey = new HotkeyInput;
hotkey->name = "Soft Reset";
hotkey->mapping = "None";
hotkey->logic = 1;
hotkeyMap.append(hotkey);
hotkey->press = [] {
utility->reset();
};
}
for(auto &hotkey : hotkeyMap) {
config.append(hotkey->mapping, hotkey->name);
}
}
void InputManager::pollHotkeys() {
for(auto &hotkey : hotkeyMap) {
bool state = hotkey->poll();
if(hotkey->state == 0 && state == 1) if(hotkey->press) hotkey->press();
if(hotkey->state == 1 && state == 0) if(hotkey->release) hotkey->release();
hotkey->state = state;
}
}

View File

@ -1,35 +1,176 @@
#include "../ethos.hpp"
#include "hotkeys.cpp"
InputManager *inputManager = nullptr;
void AbstractInput::bind() {
if(mapping.empty()) type = Type::Button, mapping = "None";
inputList.reset();
lstring list = mapping.split(",");
if(mapping.endswith(".Up")) type = Type::HatUp;
else if(mapping.endswith(".Down")) type = Type::HatDown;
else if(mapping.endswith(".Left")) type = Type::HatLeft;
else if(mapping.endswith(".Right")) type = Type::HatRight;
else if(mapping.endswith(".Lo")) type = Type::AxisLo;
else if(mapping.endswith(".Hi")) type = Type::AxisHi;
else if(mapping.beginswith("JP") && mapping.position("Axis")) type = Type::Axis;
else if(mapping.beginswith("MS") && mapping.endswith("axis")) type = Type::MouseAxis;
else if(mapping.beginswith("MS")) type = Type::MouseButton;
else type = Type::Button;
for(auto &mapping : list) {
Input::Type type;
if(mapping.endswith(".Up")) type = Input::Type::HatUp;
else if(mapping.endswith(".Down")) type = Input::Type::HatDown;
else if(mapping.endswith(".Left")) type = Input::Type::HatLeft;
else if(mapping.endswith(".Right")) type = Input::Type::HatRight;
else if(mapping.endswith(".Lo")) type = Input::Type::AxisLo;
else if(mapping.endswith(".Hi")) type = Input::Type::AxisHi;
else if(mapping.beginswith("JP") && mapping.position("Axis")) type = Input::Type::Axis;
else if(mapping.beginswith("MS") && mapping.endswith("axis")) type = Input::Type::MouseAxis;
else if(mapping.beginswith("MS")) type = Input::Type::MouseButton;
else type = Input::Type::Button;
string decode = mapping;
if(auto position = decode.position(".")) decode[position()] = 0;
scancode = Scancode::decode(decode);
string decode = mapping;
if(auto position = decode.position(".")) decode[position()] = 0;
unsigned scancode = Scancode::decode(decode);
inputList.append({type, scancode});
}
}
bool AbstractInput::append(const string &encode) {
if(mapping.position(encode)) return false; //mapping already bound
if(mapping.empty() || mapping == "None") mapping = encode; //remove "None"
else mapping.append(",", encode); //add to existing mapping list
bind();
return true;
}
AbstractInput::AbstractInput() : state(false) {
}
//
bool DigitalInput::bind(unsigned scancode, int16_t value) {
using nall::Keyboard;
using nall::Mouse;
if(scancode == Scancode::None || scancode == keyboard(0)[Keyboard::Escape]) {
inputList.reset();
mapping = "None";
return true;
}
string encode = Scancode::encode(scancode);
if(Keyboard::isAnyKey(scancode) || Keyboard::isAnyModifier(scancode) || Joypad::isAnyButton(scancode)) {
if(value == 0) return false;
return append(encode);
}
if(Mouse::isAnyButton(scancode)) {
if(value == 0) return false;
return append(encode);
}
if(Joypad::isAnyHat(scancode)) {
if(value & Joypad::HatUp ) { encode.append(".Up" ); return append(encode); }
if(value & Joypad::HatDown ) { encode.append(".Down" ); return append(encode); }
if(value & Joypad::HatLeft ) { encode.append(".Left" ); return append(encode); }
if(value & Joypad::HatRight) { encode.append(".Right"); return append(encode); }
}
if(Joypad::isAnyAxis(scancode)) {
if(value < -12288) { encode.append(".Lo"); return append(encode); }
if(value > +24576) { encode.append(".Hi"); return append(encode); }
}
return false;
}
int16_t DigitalInput::poll() {
bool result = logic;
for(auto &item : inputList) {
int16_t value = inputManager->poll(item.scancode);
bool output = logic;
switch(item.type) {
case Input::Type::Button: output = value; break;
case Input::Type::MouseButton: output = value & input.acquired(); break;
case Input::Type::HatUp: output = value & Joypad::HatUp; break;
case Input::Type::HatDown: output = value & Joypad::HatDown; break;
case Input::Type::HatLeft: output = value & Joypad::HatLeft; break;
case Input::Type::HatRight: output = value & Joypad::HatRight; break;
case Input::Type::AxisLo: output = value < -16384; break;
case Input::Type::AxisHi: output = value > +16384; break;
}
if(logic == 0) result |= output;
if(logic == 1) result &= output;
}
return result;
}
//
bool AnalogInput::bind(unsigned scancode, int16_t value) {
using nall::Keyboard;
using nall::Mouse;
if(scancode == Scancode::None || scancode == keyboard(0)[Keyboard::Escape]) {
inputList.reset();
mapping = "None";
return true;
}
string encode = Scancode::encode(scancode);
if(Mouse::isAnyAxis(scancode)) return append(encode);
if(Joypad::isAnyAxis(scancode)) return append(encode);
return false;
append:
if(mapping.position(encode)) return false; //mapping already bound
if(mapping.empty() || mapping == "None") mapping = encode; //remove "None"
else mapping.append(",", encode); //add to existing mapping list
AbstractInput::bind();
return true;
}
int16_t AnalogInput::poll() {
int16_t result = 0;
for(auto &item : inputList) {
int16_t value = inputManager->poll(item.scancode);
switch(item.type) {
case Input::Type::MouseAxis: value = input.acquired() ? value : 0; break;
case Input::Type::Axis: value = value; break;
}
result += value;
}
return result;
}
//
void InputManager::bind() {
for(auto &input : inputMap) input.data.bind();
for(auto &input : inputMap) input->bind();
for(auto &input : hotkeyMap) input->bind();
}
void InputManager::poll() {
input.poll(table);
activeScancode = !activeScancode;
input.poll(scancode[activeScancode]);
for(unsigned n = 0; n < Scancode::Limit; n++) {
if(scancode[0][n] != scancode[1][n]) {
if(settings->focused()) {
inputSettings->inputEvent(n, scancode[activeScancode][n]);
hotkeySettings->inputEvent(n, scancode[activeScancode][n]);
}
}
}
if(presentation->focused()) pollHotkeys();
}
int16_t InputManager::poll(unsigned guid) {
return table[inputMap[guid].scancode];
int16_t InputManager::poll(unsigned scancode) {
return this->scancode[activeScancode][scancode];
}
void InputManager::saveConfiguration() {
config.save(application->path("input.cfg"));
}
InputManager::InputManager() {
@ -44,24 +185,29 @@ void InputManager::bootstrap() {
for(auto &number : device.displayinput) {
auto &input = device.input[number];
AbstractInput abstract;
abstract.type = AbstractInput::Type::Button;
abstract.name = {emulator->information.name, "::", port.name, "::", device.name, "::", input.name};
abstract.mapping = "None";
abstract.scancode = 0;
abstract.name.replace(" ", "");
AbstractInput *abstract = nullptr;
if(input.type == 0) abstract = new DigitalInput;
if(input.type == 1) abstract = new AnalogInput;
if(input.type >= 2) continue;
abstract->name = {emulator->information.name, "::", port.name, "::", device.name, "::", input.name};
abstract->name.replace(" ", "");
abstract->mapping = "None";
abstract->logic = 0; //OR
input.guid = guid++;
inputMap(input.guid) = abstract;
inputMap.append(abstract);
}
}
}
}
for(auto &input : inputMap) {
config.append(input.data.mapping, input.data.name);
config.append(input->mapping, input->name);
}
appendHotkeys();
config.load(application->path("input.cfg"));
config.save(application->path("input.cfg"));

View File

@ -1,23 +1,56 @@
struct AbstractInput {
enum class Type : unsigned { Button, MouseButton, MouseAxis, HatUp, HatDown, HatLeft, HatRight, Axis, AxisLo, AxisHi } type;
string name;
string mapping;
unsigned scancode;
bool logic; //0 = OR, 1 = AND
bool state;
struct Input {
enum class Type : unsigned { Button, MouseButton, MouseAxis, HatUp, HatDown, HatLeft, HatRight, Axis, AxisLo, AxisHi } type;
unsigned scancode;
};
vector<Input> inputList;
void bind();
bool append(const string &mapping);
virtual bool bind(unsigned scancode, int16_t value) = 0;
virtual int16_t poll() = 0;
AbstractInput();
};
struct DigitalInput : AbstractInput {
using AbstractInput::bind;
bool bind(unsigned scancode, int16_t value);
int16_t poll();
};
struct AnalogInput : AbstractInput {
using AbstractInput::bind;
bool bind(unsigned scancode, int16_t value);
int16_t poll();
};
struct HotkeyInput : DigitalInput {
function<void ()> press;
function<void ()> release;
};
struct InputManager {
int16_t table[Scancode::Limit];
map<unsigned, AbstractInput> inputMap;
vector<AbstractInput*> inputMap;
vector<HotkeyInput*> hotkeyMap;
int16_t scancode[2][Scancode::Limit];
bool activeScancode;
void bind();
void poll();
int16_t poll(unsigned guid);
int16_t poll(unsigned scancode);
void saveConfiguration();
void bootstrap();
InputManager();
//hotkeys.cpp
void appendHotkeys();
void pollHotkeys();
private:
configuration config;
};

View File

@ -34,10 +34,15 @@ void Interface::videoRefresh(const uint32_t *data, unsigned pitch, unsigned widt
}
void Interface::audioSample(int16_t lsample, int16_t rsample) {
audio.sample(lsample, rsample);
signed samples[] = {lsample, rsample};
dspaudio.sample(samples);
while(dspaudio.pending()) {
dspaudio.read(samples);
audio.sample(samples[0], samples[1]);
}
}
int16_t Interface::inputPoll(unsigned port, unsigned device, unsigned input) {
unsigned guid = system().port[port].device[device].input[input].guid;
return inputManager->poll(guid);
return inputManager->inputMap[guid]->poll();
}

View File

@ -0,0 +1,66 @@
HotkeySettings *hotkeySettings = nullptr;
HotkeySettings::HotkeySettings() : activeInput(nullptr) {
title.setFont(application->titleFont);
title.setText("Hotkey Bindings");
inputList.setHeaderText("Name", "Mapping");
inputList.setHeaderVisible();
clearButton.setText("Clear");
append(title, {~0, 0}, 5);
append(inputList, {~0, ~0}, 5);
append(controlLayout, {~0, 0});
controlLayout.append(spacer, {~0, 0});
controlLayout.append(clearButton, {80, 0});
inputList.onChange = {&HotkeySettings::synchronize, this};
inputList.onActivate = {&HotkeySettings::assignInput, this};
clearButton.onActivate = {&HotkeySettings::clearInput, this};
refresh();
}
void HotkeySettings::synchronize() {
clearButton.setEnabled(inputList.selected());
}
void HotkeySettings::refresh() {
inputList.reset();
for(auto &hotkey : inputManager->hotkeyMap) {
string mapping = hotkey->mapping;
mapping.replace(",", " and ");
inputList.append(hotkey->name, mapping);
}
synchronize();
}
void HotkeySettings::clearInput() {
activeInput = inputManager->hotkeyMap[inputList.selection()];
inputEvent(Scancode::None, 1);
}
void HotkeySettings::assignInput() {
activeInput = inputManager->hotkeyMap[inputList.selection()];
settings->setStatusText({"Set assignment for [", activeInput->name, "] ..."});
settings->panelList.setEnabled(false);
inputList.setEnabled(false);
clearButton.setEnabled(false);
}
void HotkeySettings::inputEvent(unsigned scancode, int16_t value) {
using nall::Mouse;
if(activeInput == nullptr) return;
if(Mouse::isAnyButton(scancode) || Mouse::isAnyAxis(scancode)) return;
if(Joypad::isAnyAxis(scancode)) return;
if(activeInput->bind(scancode, value) == false) return;
activeInput = nullptr;
settings->setStatusText("");
settings->panelList.setEnabled(true);
inputList.setEnabled(true);
clearButton.setEnabled(true);
refresh();
}

View File

@ -0,0 +1,19 @@
struct HotkeySettings : SettingsLayout {
Label title;
ListView inputList;
HorizontalLayout controlLayout;
Widget spacer;
Button clearButton;
void synchronize();
void refresh();
void clearInput();
void assignInput();
void inputEvent(unsigned scancode, int16_t value);
HotkeySettings();
private:
HotkeyInput *activeInput;
};
extern HotkeySettings *hotkeySettings;

View File

@ -1,6 +1,6 @@
InputSettings *inputSettings = nullptr;
InputSettings::InputSettings() {
InputSettings::InputSettings() : activeInput(nullptr) {
title.setFont(application->titleFont);
title.setText("Input Settings");
inputList.setHeaderText("Name", "Mapping");
@ -28,11 +28,43 @@ InputSettings::InputSettings() {
portList.onChange = {&InputSettings::portChanged, this};
deviceList.onChange = {&InputSettings::deviceChanged, this};
inputList.onChange = {&InputSettings::synchronize, this};
inputList.onActivate = {&InputSettings::assignInput, this};
assign[0].onActivate = [&] { assignMouseInput(0); };
assign[1].onActivate = [&] { assignMouseInput(1); };
assign[2].onActivate = [&] { assignMouseInput(2); };
clearButton.onActivate = {&InputSettings::clearInput, this};
systemChanged();
}
void InputSettings::synchronize() {
if(inputList.selected() == false) {
assign[0].setVisible(false);
assign[1].setVisible(false);
assign[2].setVisible(false);
} else {
unsigned number = activeDevice().displayinput[inputList.selection()];
auto &input = activeDevice().input[number];
activeInput = inputManager->inputMap[input.guid];
if(dynamic_cast<DigitalInput*>(activeInput)) {
assign[0].setText("Mouse Left");
assign[1].setText("Mouse Middle");
assign[2].setText("Mouse Right");
assign[0].setVisible(true);
assign[1].setVisible(true);
assign[2].setVisible(true);
}
if(dynamic_cast<AnalogInput*>(activeInput)) {
assign[0].setText("Mouse X-axis");
assign[1].setText("Mouse Y-axis");
assign[0].setVisible(true);
assign[1].setVisible(true);
assign[2].setVisible(false);
}
}
clearButton.setEnabled(inputList.selected());
}
@ -68,7 +100,69 @@ void InputSettings::deviceChanged() {
inputList.reset();
for(unsigned number : activeDevice().displayinput) {
auto &input = activeDevice().input[number];
inputList.append(input.name, inputManager->inputMap(input.guid).mapping);
auto abstract = inputManager->inputMap(input.guid);
string mapping = abstract->mapping;
mapping.replace(",", " or ");
inputList.append(input.name, mapping);
}
synchronize();
}
void InputSettings::clearInput() {
unsigned number = activeDevice().displayinput[inputList.selection()];
auto &input = activeDevice().input[number];
activeInput = inputManager->inputMap[input.guid];
inputEvent(Scancode::None, 1);
}
void InputSettings::assignInput() {
unsigned number = activeDevice().displayinput[inputList.selection()];
auto &input = activeDevice().input[number];
activeInput = inputManager->inputMap[input.guid];
settings->setStatusText({"Set assignment for [", activeDevice().name, "::", input.name, "] ..."});
settings->panelList.setEnabled(false);
systemList.setEnabled(false);
portList.setEnabled(false);
deviceList.setEnabled(false);
inputList.setEnabled(false);
assign[0].setEnabled(false);
assign[1].setEnabled(false);
assign[2].setEnabled(false);
clearButton.setEnabled(false);
}
void InputSettings::assignMouseInput(unsigned n) {
unsigned number = activeDevice().displayinput[inputList.selection()];
auto &input = activeDevice().input[number];
activeInput = inputManager->inputMap[input.guid];
if(dynamic_cast<DigitalInput*>(activeInput)) {
return inputEvent(mouse(0).button(n), 1, true);
}
if(dynamic_cast<AnalogInput*>(activeInput)) {
return inputEvent(mouse(0).axis(n), 1, true);
}
}
void InputSettings::inputEvent(unsigned scancode, int16_t value, bool allowMouseInput) {
using nall::Mouse;
if(activeInput == nullptr) return;
if(allowMouseInput == false && (Mouse::isAnyButton(scancode) || Mouse::isAnyAxis(scancode))) return;
if(activeInput->bind(scancode, value) == false) return;
activeInput = nullptr;
deviceChanged();
settings->setStatusText("");
settings->panelList.setEnabled(true);
systemList.setEnabled(true);
portList.setEnabled(true);
deviceList.setEnabled(true);
inputList.setEnabled(true);
assign[0].setEnabled(true);
assign[1].setEnabled(true);
assign[2].setEnabled(true);
clearButton.setEnabled(true);
synchronize();
}

View File

@ -19,7 +19,14 @@ struct InputSettings : SettingsLayout {
void systemChanged();
void portChanged();
void deviceChanged();
void clearInput();
void assignInput();
void assignMouseInput(unsigned n);
void inputEvent(unsigned scancode, int16_t value, bool allowMouseInput = false);
InputSettings();
private:
AbstractInput *activeInput;
};
extern InputSettings *inputSettings;

View File

@ -2,6 +2,7 @@
#include "video.cpp"
#include "audio.cpp"
#include "input.cpp"
#include "hotkey.cpp"
Settings *settings = nullptr;
void SettingsLayout::append(Sizable &sizable, const Size &size, unsigned spacing) {
@ -25,12 +26,14 @@ Settings::Settings() {
panelList.append("Video");
panelList.append("Audio");
panelList.append("Input");
panelList.append("Hotkeys");
append(layout);
layout.append(panelList, {120, ~0}, 5);
append(*videoSettings);
append(*audioSettings);
append(*inputSettings);
append(*hotkeySettings);
panelList.onChange = {&Settings::panelChanged, this};
@ -42,11 +45,13 @@ void Settings::panelChanged() {
videoSettings->setVisible(false);
audioSettings->setVisible(false);
inputSettings->setVisible(false);
hotkeySettings->setVisible(false);
if(panelList.selected() == false) return;
switch(panelList.selection()) {
case 0: return videoSettings->setVisible();
case 1: return audioSettings->setVisible();
case 2: return inputSettings->setVisible();
case 3: return hotkeySettings->setVisible();
}
}

View File

@ -9,6 +9,7 @@ struct SettingsLayout : HorizontalLayout {
#include "video.hpp"
#include "audio.hpp"
#include "input.hpp"
#include "hotkey.hpp"
struct Settings : Window {
HorizontalLayout layout;

View File

@ -21,6 +21,14 @@ void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Medi
filestream fs({pathname, memory.name});
system().load(memory.id, fs);
}
system().updatePalette();
dspaudio.setFrequency(emulator->information.frequency);
string displayname = pathname;
displayname.rtrim<1>("/");
presentation->setTitle(notdir(nall::basename(displayname)));
presentation->setSystemName(media.displayname);
}
void Utility::saveMedia() {
@ -30,10 +38,12 @@ void Utility::saveMedia() {
}
void Utility::power() {
if(application->active == nullptr) return;
system().power();
}
void Utility::reset() {
if(application->active == nullptr) return;
system().reset();
}
@ -43,6 +53,7 @@ void Utility::unload() {
system().unload();
setInterface(nullptr);
}
presentation->setTitle({Emulator::Name, " v", Emulator::Version});
video.clear();
}