Update to v094r12 release.

byuu says:

Changelog:
* added driver selection
* added video scale + aspect correction settings
* added A/V sync + audio mute settings
* added configuration file
* fixed compilation bugs under Windows and Linux
* fixed window sizing
* removed HSU1
* the system menu stays as "System", because "Game Boy Advance" was too
  long a string for the smallest scale size
* some more stuff

You guys probably won't be ecstatic about the video sizing options, but
it's basically your choice of 1x, 2x or 4x scale with optional aspect
correction. 3x was intentionally skipped because it looks horrible on
hires SNES games. The window is resized and recentered upon loading
games. The window doesn't resize otherwise. I never really liked the way
v094 always left you with black screen areas and left you with
off-centered window positions.

I might go ahead and add the pseudo-fullscreen toggle that will jump
into 4x mode (respecting your aspect setting.)

Short-term:
* add input port changing support
* add other input types (mouse-based, etc)
* add save states
* add cheat codes
* add timing configuration (video/audio sync)
* add hotkeys (single state)

We can probably do a new release once the short-term items are
completed.

Long-term:
* add slotted cart loader (SGB, BSX, ST)
* add DIP switch selection window (NSS)
* add cheat code database
* add state manager
* add overscan masking

Not planned:
* video color adjustments (will allow emulated color vs raw color; but
  no more sliders)
* pixel shaders
* ananke integration (will need to make a command-line version to get my
  games in)
* fancy audio adjustment controls (resampler, latency, volume)
* input focus settings
* relocating game library (not hard, just don't feel like it)
* localization support (not enough users)
* window geometry memory
* anything else not in higan v094
This commit is contained in:
Tim Allen 2015-03-03 21:14:49 +11:00
parent 4a069761f9
commit a1b2fb0124
31 changed files with 239 additions and 246 deletions

View File

@ -3,7 +3,7 @@
namespace Emulator {
static const char Name[] = "higan";
static const char Version[] = "094.11";
static const char Version[] = "094.12";
static const char Author[] = "byuu";
static const char License[] = "GPLv3";
static const char Website[] = "http://byuu.org/";

View File

@ -183,6 +183,9 @@ auto pWindow::destruct() -> void {
}
auto pWindow::append(shared_pointer<mMenuBar> menuBar) -> void {
_setMenuEnabled(menuBar->enabled(true));
_setMenuFont(menuBar->font(true));
_setMenuVisible(menuBar->visible(true));
}
auto pWindow::append(shared_pointer<mStatusBar> statusBar) -> void {
@ -298,10 +301,17 @@ auto pWindow::setVisible(bool visible) -> void {
if(menuBar->self()) menuBar->self()->setVisible(menuBar->visible(true));
}
if(auto& layout = state().layout) {
if(layout->self()) layout->self()->setVisible(layout->visible(true));
}
if(auto& statusBar = state().statusBar) {
if(statusBar->self()) statusBar->self()->setVisible(statusBar->visible(true));
}
//GTK+ returns invalid widget sizes below without this call
Application::processEvents();
if(visible) {
if(gtk_widget_get_visible(gtkMenu)) {
GtkAllocation allocation;
@ -317,8 +327,6 @@ auto pWindow::setVisible(bool visible) -> void {
}
if(auto& layout = state().layout) {
if(layout->self()) layout->self()->setVisible(layout->visible(true));
Application::processEvents(); //todo: this should not be necessary
layout->setGeometry(self().geometry().setPosition(0, 0));
}
}

View File

@ -50,7 +50,7 @@ struct stream {
string text() const {
string buffer;
buffer.resize(size() + 1);
buffer.resize(size());
seek(0);
read((uint8_t*)buffer.data(), size());
return buffer;

View File

@ -10,7 +10,6 @@
#include <Carbon/Carbon.h>
#undef decimal
#elif defined(PLATFORM_WINDOWS)
#define _WIN32_WINNT 0x0501
#include <windows.h>
#endif

View File

@ -137,7 +137,7 @@ struct InputJoypadDirectInput {
property.diph.dwHow = DIPH_DEVICE;
device->GetProperty(DIPROP_GUIDANDPATH, &property.diph);
string devicePath = (const char*)utf8_t(property.wszPath);
jp.pathID = crc32_calculate((const uint8_t*)devicePath.data(), devicePath.size());
jp.pathID = Hash::CRC32(devicePath.data(), devicePath.size()).value();
jp.hid.id = (uint64_t)jp.pathID << 32 | jp.vendorID << 16 | jp.productID << 0;
if(jp.hid.rumble) {

View File

@ -255,7 +255,7 @@ private:
}
void createJoypadHID(Joypad& jp) {
uint64_t pathID = crc32_calculate((const uint8_t*)jp.deviceName.data(), jp.deviceName.size());
uint64_t pathID = Hash::CRC32(jp.deviceName.data(), jp.deviceName.size()).value();
jp.hid.id = pathID << 32 | hex(jp.vendorID) << 16 | hex(jp.productID) << 0;
for(unsigned n = 0; n < jp.axes.size(); n++) jp.hid.axis().append({n});

View File

@ -23,11 +23,11 @@ struct RawInput {
};
vector<Device> devices;
optional<Device&> find(uint16_t vendorID, uint16_t productID) {
maybe<Device&> find(uint16_t vendorID, uint16_t productID) {
for(auto& device : devices) {
if(device.vendorID == vendorID && device.productID == productID) return {true, device};
if(device.vendorID == vendorID && device.productID == productID) return device;
}
return false;
return nothing;
}
void scanDevices() {

View File

@ -366,7 +366,7 @@ public:
TextureProc textureProc = (TextureProc)GetProcAddress(d3dx, "D3DXCreateTextureFromFileA");
LPD3DXBUFFER pBufferErrors = NULL;
effectProc(device, shader_source, lstrlenA(shader_source), NULL, NULL, 0, NULL, &effect, &pBufferErrors);
effectProc(device, (const void*)shader_source.data(), lstrlenA(shader_source), NULL, NULL, 0, NULL, &effect, &pBufferErrors);
D3DXHANDLE hTech;
effect->FindNextValidTechnique(NULL, &hTech);

View File

@ -7,7 +7,7 @@ sfc_objects += sfc-sa1 sfc-superfx
sfc_objects += sfc-armdsp sfc-hitachidsp sfc-necdsp
sfc_objects += sfc-epsonrtc sfc-sharprtc
sfc_objects += sfc-spc7110 sfc-sdd1 sfc-obc1
sfc_objects += sfc-hsu1 sfc-msu1
sfc_objects += sfc-msu1
sfc_objects += sfc-satellaviewcart sfc-sufamiturbo
objects += $(sfc_objects)
@ -30,14 +30,14 @@ else ifeq ($(profile),performance)
sfcdsp := $(sfc)/alt/dsp
sfcppu := $(sfc)/alt/ppu-performance
else
$(error unknown profile.)
$(error unknown Super Famicom profile)
endif
obj/sfc-interface.o: $(sfc)/interface/interface.cpp $(call rwildcard,$(sfc)/interface)
obj/sfc-system.o: $(sfc)/system/system.cpp $(call rwildcard,$(sfc)/system/)
obj/sfc-controller.o: $(sfc)/controller/controller.cpp $(call rwildcard,$(sfc)/controller/)
obj/sfc-cartridge.o: $(sfc)/cartridge/cartridge.cpp $(sfc)/cartridge/*
obj/sfc-cheat.o: $(sfc)/cheat/cheat.cpp $(sfc)/cheat/*
obj/sfc-cartridge.o: $(sfc)/cartridge/cartridge.cpp $(call rwildcard,$(sfc)/cartridge/)
obj/sfc-cheat.o: $(sfc)/cheat/cheat.cpp $(call rwildcard,$(sfc)/cheat/)
obj/sfc-memory.o: $(sfc)/memory/memory.cpp $(call rwildcard,$(sfc)/memory/)
obj/sfc-cpu.o: $(sfccpu)/cpu.cpp $(call rwildcard,$(sfccpu)/)
obj/sfc-smp.o: $(sfcsmp)/smp.cpp $(call rwildcard,$(sfcsmp)/)
@ -61,12 +61,11 @@ obj/sfc-necdsp.o: $(sfc)/chip/necdsp/necdsp.cpp $(call rwildcard,$(sfc)
obj/sfc-epsonrtc.o: $(sfc)/chip/epsonrtc/epsonrtc.cpp $(call rwildcard,$(sfc)/chip/epsonrtc/)
obj/sfc-sharprtc.o: $(sfc)/chip/sharprtc/sharprtc.cpp $(call rwildcard,$(sfc)/chip/sharprtc/)
obj/sfc-spc7110.o: $(sfc)/chip/spc7110/spc7110.cpp $(sfc)/chip/spc7110/*
obj/sfc-sdd1.o: $(sfc)/chip/sdd1/sdd1.cpp $(sfc)/chip/sdd1/*
obj/sfc-obc1.o: $(sfc)/chip/obc1/obc1.cpp $(sfc)/chip/obc1/*
obj/sfc-spc7110.o: $(sfc)/chip/spc7110/spc7110.cpp $(call rwildcard,$(sfc)/chip/spc7110/)
obj/sfc-sdd1.o: $(sfc)/chip/sdd1/sdd1.cpp $(call rwildcard,$(sfc)/chip/sdd1/)
obj/sfc-obc1.o: $(sfc)/chip/obc1/obc1.cpp $(call rwildcard,$(sfc)/chip/obc1/)
obj/sfc-hsu1.o: $(sfc)/chip/hsu1/hsu1.cpp $(sfc)/chip/hsu1/*
obj/sfc-msu1.o: $(sfc)/chip/msu1/msu1.cpp $(sfc)/chip/msu1/*
obj/sfc-msu1.o: $(sfc)/chip/msu1/msu1.cpp $(call rwildcard,$(sfc)/chip/msu1/)
obj/sfc-satellaviewcart.o: $(sfc)/slot/satellaview/satellaview.cpp $(call rwildcard,$(sfc)/slot/satellaview/)
obj/sfc-sufamiturbo.o: $(sfc)/slot/sufamiturbo/sufamiturbo.cpp $(call rwildcard,$(sfc)/slot/sufamiturbo/)

View File

@ -114,7 +114,6 @@ private:
void parse_markup_spc7110(Markup::Node);
void parse_markup_sdd1(Markup::Node);
void parse_markup_obc1(Markup::Node);
void parse_markup_hsu1(Markup::Node);
void parse_markup_msu1(Markup::Node);
friend class Interface;

View File

@ -26,7 +26,6 @@ void Cartridge::parse_markup(const char* markup) {
parse_markup_spc7110(cartridge["spc7110"]);
parse_markup_sdd1(cartridge["sdd1"]);
parse_markup_obc1(cartridge["obc1"]);
parse_markup_hsu1(cartridge["hsu1"]);
parse_markup_msu1(cartridge["msu1"]);
}
@ -556,21 +555,6 @@ void Cartridge::parse_markup_obc1(Markup::Node root) {
}
}
void Cartridge::parse_markup_hsu1(Markup::Node root) {
if(root.exists() == false) return;
has_hsu1 = true;
for(auto& node : root) {
if(node.name != "map") continue;
if(node["id"].data == "io") {
Mapping m({&HSU1::read, &hsu1}, {&HSU1::write, &hsu1});
parse_markup_map(m, node);
mapping.append(m);
}
}
}
void Cartridge::parse_markup_msu1(Markup::Node root) {
if(root.exists() == false) return;
has_msu1 = true;

View File

@ -22,7 +22,6 @@ struct Coprocessor : Thread {
#include <sfc/chip/sdd1/sdd1.hpp>
#include <sfc/chip/obc1/obc1.hpp>
#include <sfc/chip/hsu1/hsu1.hpp>
#include <sfc/chip/msu1/msu1.hpp>
void Coprocessor::step(unsigned clocks) {

View File

@ -1,112 +0,0 @@
#include <sfc/sfc.hpp>
#define HSU1_CPP
namespace SuperFamicom {
#include "serialization.cpp"
HSU1 hsu1;
void HSU1::init() {
}
void HSU1::load() {
}
void HSU1::unload() {
}
void HSU1::power() {
}
void HSU1::reset() {
txbusy = 0;
rxbusy = 1;
txlatch = 0;
txbuffer.reset();
rxbuffer.reset();
}
uint8 HSU1::read(unsigned addr) {
addr &= 1;
if(addr == 0) {
return (txbusy << 7) | (rxbusy << 6) | (1 << 0);
}
if(addr == 1) {
if(rxbusy) return 0x00;
uint8 data = rxbuffer.take(0);
if(rxbuffer.size() == 0) rxbusy = 1;
return data;
}
//unreachable
return cpu.regs.mdr;
}
void HSU1::write(unsigned addr, uint8 data) {
addr &= 1;
if(addr == 0) {
if(txbusy) return;
bool latch = data & 0x01;
if(txlatch == 1 && latch == 0) {
//username:password@http://server:port/path
lstring side = interface->server().split<1>("@");
string username = side(0).split<1>(":")(0);
string password = side(0).split<1>(":")(1);
side(1).ltrim("http://");
string hostname = side(1).split<1>("/")(0);
string hostpath = side(1).split<1>("/")(1);
side = hostname.split<1>(":");
hostname = side(0);
string hostport = side(1);
if(hostport.empty()) hostport = "80";
/*http server;
if(server.connect(hostname, decimal(hostport))) {
string header {
"username:", username, "\n",
"password:", password, "\n",
"emulator:bsnes\n",
"sha256:", interface->sha256(), "\n",
"\n"
};
string packet {
"POST /", hostpath, " HTTP/1.0\r\n",
"Host: ", hostname, "\r\n",
"Connection: close\r\n",
"Content-Type: application/octet-stream\r\n",
"Content-Length: ", header.length() + txbuffer.size(), "\r\n",
"\r\n",
};
server.send(packet);
server.send(header);
server.send(txbuffer.data(), txbuffer.size());
txbuffer.reset();
server.header = server.downloadHeader();
uint8_t* data = nullptr;
unsigned size = 0;
server.downloadContent(data, size);
rxbuffer.resize(size);
memcpy(rxbuffer.data(), data, size);
rxbusy = rxbuffer.size() == 0;
free(data);
server.disconnect();
}*/
}
txlatch = latch;
}
if(addr == 1) {
if(txbusy) return;
if(txlatch == 0) return;
txbuffer.append(data);
}
}
}

View File

@ -1,21 +0,0 @@
struct HSU1 {
void init();
void load();
void unload();
void power();
void reset();
uint8 read(unsigned addr);
void write(unsigned addr, uint8 data);
void serialize(serializer&);
private:
bool txbusy;
bool rxbusy;
bool txlatch;
vector<uint8> txbuffer;
vector<uint8> rxbuffer;
};
extern HSU1 hsu1;

View File

@ -1,21 +0,0 @@
#ifdef HSU1_CPP
void HSU1::serialize(serializer& s) {
s.integer(txbusy);
s.integer(rxbusy);
s.integer(txlatch);
unsigned size;
size = txbuffer.size();
s.integer(size);
txbuffer.resize(size);
s.array(txbuffer.data(), txbuffer.size());
size = rxbuffer.size();
s.integer(size);
rxbuffer.resize(size);
s.array(rxbuffer.data(), rxbuffer.size());
}
#endif

View File

@ -70,7 +70,6 @@ void System::serialize_all(serializer& s) {
if(cartridge.has_spc7110()) spc7110.serialize(s);
if(cartridge.has_sdd1()) sdd1.serialize(s);
if(cartridge.has_obc1()) obc1.serialize(s);
if(cartridge.has_hsu1()) hsu1.serialize(s);
if(cartridge.has_msu1()) msu1.serialize(s);
if(cartridge.has_st_slots()) sufamiturboA.serialize(s), sufamiturboB.serialize(s);
}

View File

@ -79,7 +79,6 @@ void System::init() {
spc7110.init();
sdd1.init();
obc1.init();
hsu1.init();
msu1.init();
satellaviewcartridge.init();
@ -134,7 +133,6 @@ void System::load() {
if(cartridge.has_spc7110()) spc7110.load();
if(cartridge.has_sdd1()) sdd1.load();
if(cartridge.has_obc1()) obc1.load();
if(cartridge.has_hsu1()) hsu1.load();
if(cartridge.has_msu1()) msu1.load();
if(cartridge.has_bs_slot()) satellaviewcartridge.load();
if(cartridge.has_st_slots()) sufamiturboA.load(), sufamiturboB.load();
@ -158,7 +156,6 @@ void System::unload() {
if(cartridge.has_spc7110()) spc7110.unload();
if(cartridge.has_sdd1()) sdd1.unload();
if(cartridge.has_obc1()) obc1.unload();
if(cartridge.has_hsu1()) hsu1.unload();
if(cartridge.has_msu1()) msu1.unload();
if(cartridge.has_bs_slot()) satellaviewcartridge.unload();
if(cartridge.has_st_slots()) sufamiturboA.unload(), sufamiturboB.unload();
@ -187,7 +184,6 @@ void System::power() {
if(cartridge.has_spc7110()) spc7110.power();
if(cartridge.has_sdd1()) sdd1.power();
if(cartridge.has_obc1()) obc1.power();
if(cartridge.has_hsu1()) hsu1.power();
if(cartridge.has_msu1()) msu1.power();
if(cartridge.has_bs_slot()) satellaviewcartridge.power();
@ -215,7 +211,6 @@ void System::reset() {
if(cartridge.has_spc7110()) spc7110.reset();
if(cartridge.has_sdd1()) sdd1.reset();
if(cartridge.has_obc1()) obc1.reset();
if(cartridge.has_hsu1()) hsu1.reset();
if(cartridge.has_msu1()) msu1.reset();
if(cartridge.has_bs_slot()) satellaviewcartridge.reset();

View File

@ -8,7 +8,7 @@ include sfc/GNUmakefile
include gb/GNUmakefile
include gba/GNUmakefile
ui_objects := ui-tomoko ui-program ui-input
ui_objects := ui-tomoko ui-program ui-configuration ui-input
ui_objects += ui-library ui-settings ui-presentation
ui_objects += ruby hiro
@ -51,6 +51,7 @@ 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-configuration.o: $(ui)/configuration/configuration.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)/)

View File

@ -0,0 +1,31 @@
#include "../tomoko.hpp"
ConfigurationManager* configurationManager = nullptr;
auto config() -> ConfigurationManager& { return *configurationManager; }
ConfigurationManager::ConfigurationManager() {
configurationManager = this;
video.append(video.driver, "Driver");
video.append(video.synchronize, "Synchronize");
video.append(video.scale, "Scale");
video.append(video.aspectCorrection, "AspectCorrection");
append(video, "Video");
audio.append(audio.driver, "Driver");
audio.append(audio.synchronize, "Synchronize");
audio.append(audio.mute, "Mute");
append(audio, "Audio");
input.append(input.driver, "Driver");
append(input, "Input");
load({configpath(), "tomoko/settings.bml"});
if(!video.driver) video.driver = ruby::video.safestDriver();
if(!audio.driver) audio.driver = ruby::audio.safestDriver();
if(!input.driver) input.driver = ruby::input.safestDriver();
save({configpath(), "tomoko/settings.bml"});
}
auto ConfigurationManager::quit() -> void {
save({configpath(), "tomoko/settings.bml"});
}

View File

@ -0,0 +1,24 @@
struct ConfigurationManager : Configuration::Document {
ConfigurationManager();
auto quit() -> void;
struct Video : Configuration::Node {
string driver;
bool synchronize = false;
string scale = "Normal";
bool aspectCorrection = true;
} video;
struct Audio : Configuration::Node {
string driver;
bool synchronize = true;
bool mute = false;
} audio;
struct Input : Configuration::Node {
string driver;
} input;
};
extern ConfigurationManager* configurationManager;
auto config() -> ConfigurationManager&;

View File

@ -76,8 +76,8 @@ InputManager::InputManager() {
config.append(nodeEmulator, string{inputEmulator.name}.replace(" ", ""));
}
config.load({configpath(), "tomoko/settings.bml"});
config.save({configpath(), "tomoko/settings.bml"});
config.load({configpath(), "tomoko/input.bml"});
config.save({configpath(), "tomoko/input.bml"});
poll(); //will call bind();
}
@ -115,5 +115,5 @@ auto InputManager::onChange(HID::Device& device, unsigned group, unsigned input,
}
auto InputManager::quit() -> void {
config.save({configpath(), "tomoko/settings.bml"});
config.save({configpath(), "tomoko/input.bml"});
}

View File

@ -16,12 +16,45 @@ Presentation::Presentation() {
}
}
systemMenu.setVisible(false);
systemMenu.setText("System").setVisible(false);
powerSystem.setText("Power");
resetSystem.setText("Reset");
unloadSystem.setText("Unload").onActivate([&] { program->unloadMedia(); });
unloadSystem.setText("Unload").onActivate([&] { program->unloadMedia(); drawSplashScreen(); });
settingsMenu.setText("Settings");
videoScaleMenu.setText("Video Scale");
MenuRadioItem::group({videoScaleSmall, videoScaleNormal, videoScaleLarge});
if(config().video.scale == "Small") videoScaleSmall.setChecked();
if(config().video.scale == "Normal") videoScaleNormal.setChecked();
if(config().video.scale == "Large") videoScaleLarge.setChecked();
videoScaleSmall.setText("Small").onActivate([&] {
config().video.scale = "Small";
resizeViewport();
});
videoScaleNormal.setText("Normal").onActivate([&] {
config().video.scale = "Normal";
resizeViewport();
});
videoScaleLarge.setText("Large").onActivate([&] {
config().video.scale = "Large";
resizeViewport();
});
aspectCorrection.setText("Aspect Correction").setChecked(config().video.aspectCorrection).onToggle([&] {
config().video.aspectCorrection = aspectCorrection.checked();
resizeViewport();
});
synchronizeVideo.setText("Synchronize Video").setChecked(config().video.synchronize).onToggle([&] {
config().video.synchronize = synchronizeVideo.checked();
video.set(Video::Synchronize, config().video.synchronize);
});
synchronizeAudio.setText("Synchronize Audio").setChecked(config().audio.synchronize).onToggle([&] {
config().audio.synchronize = synchronizeAudio.checked();
audio.set(Audio::Synchronize, config().audio.synchronize);
});
muteAudio.setText("Mute Audio").setChecked(config().audio.mute).onToggle([&] {
config().audio.mute = muteAudio.checked();
program->dsp.setVolume(config().audio.mute ? 0.0 : 1.0);
});
showConfiguration.setText("Configuration ...").onActivate([&] {
settingsManager->setVisible();
settingsManager->setFocused();
@ -34,7 +67,40 @@ Presentation::Presentation() {
onClose([&] { program->quit(); });
setTitle({"tomoko v", Emulator::Version});
//setResizable(false);
setSize({512, 480});
setCentered();
setResizable(false);
resizeViewport();
}
auto Presentation::resizeViewport() -> void {
signed width = 256;
signed height = 240;
if(program->activeEmulator) {
width = program->emulator().information.width;
height = program->emulator().information.height;
}
if(config().video.scale == "Small" ) width *= 1, height *= 1;
if(config().video.scale == "Normal") width *= 2, height *= 2;
if(config().video.scale == "Large" ) width *= 4, height *= 4;
if(config().video.aspectCorrection) {
if(!program->activeEmulator || program->emulator().information.aspectRatio != 1.0) width = width * 5 / 4;
}
setSize({width, height});
setCentered();
if(!program->activeEmulator) drawSplashScreen();
}
auto Presentation::drawSplashScreen() -> void {
uint32* output;
unsigned length;
if(video.lock(output, length, 256, 240)) {
for(auto y : range(240)) {
uint32* dp = output + y * (length >> 2);
for(auto x : range(256)) *dp++ = 0xff000000;
}
video.unlock();
video.refresh();
}
}

View File

@ -1,5 +1,7 @@
struct Presentation : Window {
Presentation();
auto resizeViewport() -> void;
auto drawSplashScreen() -> void;
MenuBar menuBar{this};
Menu libraryMenu{&menuBar};
@ -10,6 +12,17 @@ struct Presentation : Window {
MenuSeparator systemMenuSeparator{&systemMenu};
MenuItem unloadSystem{&systemMenu};
Menu settingsMenu{&menuBar};
Menu videoScaleMenu{&settingsMenu};
MenuRadioItem videoScaleSmall{&videoScaleMenu};
MenuRadioItem videoScaleNormal{&videoScaleMenu};
MenuRadioItem videoScaleLarge{&videoScaleMenu};
MenuSeparator videoScaleSeparator{&videoScaleMenu};
MenuCheckItem aspectCorrection{&videoScaleMenu};
MenuSeparator settingsMenuSeparator1{&settingsMenu};
MenuCheckItem synchronizeVideo{&settingsMenu};
MenuCheckItem synchronizeAudio{&settingsMenu};
MenuCheckItem muteAudio{&settingsMenu};
MenuSeparator settingsMenuSeparator2{&settingsMenu};
MenuItem showConfiguration{&settingsMenu};
Menu toolsMenu{&menuBar};

View File

@ -66,9 +66,11 @@ 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();
if(presentation->focused()) {
auto guid = emulator().port[port].device[device].input[input].guid;
auto mapping = (InputMapping*)guid;
if(mapping) return mapping->poll();
}
return 0;
}

View File

@ -1,5 +1,6 @@
auto Program::loadMedia(string location) -> void {
location.transform("\\", "/");
if(!location.endsWith("/")) location.append("/");
if(!directory::exists(location)) return;
string type = suffixname(location).ltrim(".");
@ -23,8 +24,9 @@ auto Program::loadMedia(Emulator::Interface& _emulator, Emulator::Interface::Med
emulator().load(media.id);
emulator().power();
presentation->resizeViewport();
presentation->setTitle(emulator().title());
presentation->systemMenu.setText(emulator().information.name).setVisible(true);
presentation->systemMenu.setVisible(true);
}
auto Program::unloadMedia() -> void {
@ -35,5 +37,4 @@ auto Program::unloadMedia() -> void {
presentation->setTitle({"tomoko v", Emulator::Version});
presentation->systemMenu.setVisible(false);
drawSplashScreen();
}

View File

@ -18,6 +18,7 @@ Program::Program() {
emulators.append(new GameBoyAdvance::Interface);
for(auto& emulator : emulators) emulator->bind = this;
new ConfigurationManager;
new InputManager;
new LibraryManager;
new SettingsManager;
@ -25,30 +26,30 @@ Program::Program() {
presentation->setVisible();
video.driver("XShm");
video.driver(config().video.driver);
video.set(Video::Handle, presentation->viewport.handle());
video.set(Video::Synchronize, false);
video.set(Video::Synchronize, config().video.synchronize);
if(!video.init()) { video.driver("None"); video.init(); }
audio.driver("OpenAL");
audio.driver(config().audio.driver);
audio.set(Audio::Handle, presentation->viewport.handle());
audio.set(Audio::Synchronize, false);
audio.set(Audio::Synchronize, config().audio.synchronize);
audio.set(Audio::Frequency, 96000u);
audio.set(Audio::Latency, 80u);
if(!audio.init()) { audio.driver("None"); audio.init(); }
input.driver("Xlib");
input.driver(config().input.driver);
input.set(Input::Handle, presentation->viewport.handle());
if(!input.init()) { input.driver("None"); input.init(); }
dsp.setPrecision(16);
dsp.setBalance(0.0);
dsp.setVolume(1.0);
dsp.setVolume(config().audio.mute ? 0.0 : 1.0);
dsp.setFrequency(32040);
dsp.setResampler(DSP::ResampleEngine::Sinc);
dsp.setResamplerFrequency(96000);
drawSplashScreen();
presentation->drawSplashScreen();
}
auto Program::emulator() -> Emulator::Interface& {
@ -70,35 +71,14 @@ auto Program::main() -> void {
auto Program::quit() -> void {
unloadMedia();
configurationManager->quit();
inputManager->quit();
exit(0);
video.term();
audio.term();
input.term();
Application::quit();
}
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();
}
}

View File

@ -5,7 +5,6 @@ struct Program : Emulator::Interface::Bind {
auto main() -> void;
auto quit() -> void;
auto setEmulator(Emulator::Interface*) -> void;
auto drawSplashScreen() -> void;
//interface.cpp
auto loadRequest(unsigned id, string name, string type) -> void override;

View File

@ -0,0 +1,31 @@
AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
setIcon(Icon::Action::Settings);
setText("Advanced");
layout.setMargin(5);
driverLabel.setText("Driver Selection").setFont(Font::sans(8, "Bold"));
videoLabel.setText("Video:");
videoDriver.onChange([&] { config().video.driver = videoDriver.selected()->text(); });
for(auto& driver : string{video.availableDrivers()}.split(";")) {
ComboButtonItem item;
item.setText(driver);
videoDriver.append(item);
if(config().video.driver == driver) item.setSelected();
}
audioLabel.setText("Audio:");
audioDriver.onChange([&] { config().audio.driver = audioDriver.selected()->text(); });
for(auto& driver : string{audio.availableDrivers()}.split(";")) {
ComboButtonItem item;
item.setText(driver);
audioDriver.append(item);
if(config().audio.driver == driver) item.setSelected();
}
inputLabel.setText("Input:");
inputDriver.onChange([&] { config().input.driver = inputDriver.selected()->text(); });
for(auto& driver : string{input.availableDrivers()}.split(";")) {
ComboButtonItem item;
item.setText(driver);
inputDriver.append(item);
if(config().input.driver == driver) item.setSelected();
}
}

View File

@ -1,5 +1,6 @@
#include "../tomoko.hpp"
#include "input.cpp"
#include "advanced.cpp"
SettingsManager* settingsManager = nullptr;
SettingsManager::SettingsManager() {

View File

@ -24,12 +24,27 @@ struct InputSettings : TabFrameItem {
Button eraseButton{&controlLayout, Size{80, 0}};
};
struct AdvancedSettings : TabFrameItem {
AdvancedSettings(TabFrame*);
VerticalLayout layout{this};
Label driverLabel{&layout, Size{~0, 0}, 0};
HorizontalLayout driverLayout{&layout, Size{~0, 0}};
Label videoLabel{&driverLayout, Size{0, 0}};
ComboButton videoDriver{&driverLayout, Size{~0, 0}};
Label audioLabel{&driverLayout, Size{0, 0}};
ComboButton audioDriver{&driverLayout, Size{~0, 0}};
Label inputLabel{&driverLayout, Size{0, 0}};
ComboButton inputDriver{&driverLayout, Size{~0, 0}};
};
struct SettingsManager : Window {
SettingsManager();
VerticalLayout layout{this};
TabFrame panelLayout{&layout, Size{~0, ~0}};
InputSettings input{&panelLayout};
AdvancedSettings advanced{&panelLayout};
StatusBar statusBar{this};
};

View File

@ -8,6 +8,7 @@ using namespace ruby;
using namespace hiro;
#include "program/program.hpp"
#include "configuration/configuration.hpp"
#include "input/input.hpp"
#include "library/library.hpp"
#include "settings/settings.hpp"