mirror of https://github.com/bsnes-emu/bsnes.git
v108.1
* added CPU and SA1 overclocking support * added fast forward speed limiting * added option to mute during fast forwarding and rewinding * lowered volume when not muting during FF/rewind * reformatted settings/tools windows from tabs to lists * moved focus settings to input settings panel * redesigned input and hotkey settings panels to be easier to use * fixed offscreen placement issue with path settings panel * added hotkey combinational logic option (AND / OR mode setting) * added search support to file browser dialog * fixed --fullscreen command-line option
This commit is contained in:
parent
7e88bdde09
commit
f65b7a8528
|
@ -10,45 +10,45 @@ Audio::~Audio() {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Audio::reset(Interface* interface) -> void {
|
auto Audio::reset(Interface* interface) -> void {
|
||||||
this->interface = interface;
|
_interface = interface;
|
||||||
streams.reset();
|
_streams.reset();
|
||||||
channels = 0;
|
_channels = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Audio::setFrequency(double frequency) -> void {
|
auto Audio::setFrequency(double frequency) -> void {
|
||||||
this->frequency = frequency;
|
_frequency = frequency;
|
||||||
for(auto& stream : streams) {
|
for(auto& stream : _streams) {
|
||||||
stream->setFrequency(stream->inputFrequency, frequency);
|
stream->setFrequency(stream->inputFrequency, frequency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Audio::setVolume(double volume) -> void {
|
auto Audio::setVolume(double volume) -> void {
|
||||||
this->volume = volume;
|
_volume = volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Audio::setBalance(double balance) -> void {
|
auto Audio::setBalance(double balance) -> void {
|
||||||
this->balance = balance;
|
_balance = balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Audio::createStream(uint channels, double frequency) -> shared_pointer<Stream> {
|
auto Audio::createStream(uint channels, double frequency) -> shared_pointer<Stream> {
|
||||||
this->channels = max(this->channels, channels);
|
_channels = max(_channels, channels);
|
||||||
shared_pointer<Stream> stream = new Stream;
|
shared_pointer<Stream> stream = new Stream;
|
||||||
stream->reset(channels, frequency, this->frequency);
|
stream->reset(channels, frequency, _frequency);
|
||||||
streams.append(stream);
|
_streams.append(stream);
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Audio::process() -> void {
|
auto Audio::process() -> void {
|
||||||
while(streams) {
|
while(_streams) {
|
||||||
for(auto& stream : streams) {
|
for(auto& stream : _streams) {
|
||||||
if(!stream->pending()) return;
|
if(!stream->pending()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
double samples[channels];
|
double samples[_channels];
|
||||||
for(auto& sample : samples) sample = 0.0;
|
for(auto& sample : samples) sample = 0.0;
|
||||||
|
|
||||||
for(auto& stream : streams) {
|
for(auto& stream : _streams) {
|
||||||
double buffer[channels];
|
double buffer[_channels];
|
||||||
uint length = stream->read(buffer), offset = 0;
|
uint length = stream->read(buffer), offset = 0;
|
||||||
|
|
||||||
for(auto& sample : samples) {
|
for(auto& sample : samples) {
|
||||||
|
@ -57,16 +57,16 @@ auto Audio::process() -> void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto c : range(channels)) {
|
for(auto c : range(_channels)) {
|
||||||
samples[c] = max(-1.0, min(+1.0, samples[c] * volume));
|
samples[c] = max(-1.0, min(+1.0, samples[c] * _volume));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(channels == 2) {
|
if(_channels == 2) {
|
||||||
if(balance < 0.0) samples[1] *= 1.0 + balance;
|
if(_balance < 0.0) samples[1] *= 1.0 + _balance;
|
||||||
if(balance > 0.0) samples[0] *= 1.0 - balance;
|
if(_balance > 0.0) samples[0] *= 1.0 - _balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
platform->audioFrame(samples, channels);
|
platform->audioFrame(samples, _channels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,11 @@ struct Audio {
|
||||||
~Audio();
|
~Audio();
|
||||||
auto reset(Interface* interface) -> void;
|
auto reset(Interface* interface) -> void;
|
||||||
|
|
||||||
|
inline auto channels() const -> uint { return _channels; }
|
||||||
|
inline auto frequency() const -> double { return _frequency; }
|
||||||
|
inline auto volume() const -> double { return _volume; }
|
||||||
|
inline auto balance() const -> double { return _balance; }
|
||||||
|
|
||||||
auto setFrequency(double frequency) -> void;
|
auto setFrequency(double frequency) -> void;
|
||||||
auto setVolume(double volume) -> void;
|
auto setVolume(double volume) -> void;
|
||||||
auto setBalance(double balance) -> void;
|
auto setBalance(double balance) -> void;
|
||||||
|
@ -26,14 +31,14 @@ struct Audio {
|
||||||
private:
|
private:
|
||||||
auto process() -> void;
|
auto process() -> void;
|
||||||
|
|
||||||
Interface* interface = nullptr;
|
Interface* _interface = nullptr;
|
||||||
vector<shared_pointer<Stream>> streams;
|
vector<shared_pointer<Stream>> _streams;
|
||||||
|
|
||||||
uint channels = 0;
|
uint _channels = 0;
|
||||||
double frequency = 48000.0;
|
double _frequency = 48000.0;
|
||||||
|
|
||||||
double volume = 1.0;
|
double _volume = 1.0;
|
||||||
double balance = 0.0;
|
double _balance = 0.0;
|
||||||
|
|
||||||
friend class Stream;
|
friend class Stream;
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,7 +31,7 @@ using namespace nall;
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "bsnes";
|
static const string Name = "bsnes";
|
||||||
static const string Version = "108";
|
static const string Version = "108.1";
|
||||||
static const string Author = "byuu";
|
static const string Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "https://byuu.org";
|
static const string Website = "https://byuu.org";
|
||||||
|
|
|
@ -127,8 +127,10 @@ auto SA1::unload() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SA1::power() -> void {
|
auto SA1::power() -> void {
|
||||||
|
double overclock = max(1.0, min(4.0, configuration.hacks.sa1.overclock / 100.0));
|
||||||
|
|
||||||
WDC65816::power();
|
WDC65816::power();
|
||||||
create(SA1::Enter, system.cpuFrequency());
|
create(SA1::Enter, system.cpuFrequency() * overclock);
|
||||||
|
|
||||||
bwram.dma = false;
|
bwram.dma = false;
|
||||||
for(uint address : range(iram.size())) {
|
for(uint address : range(iram.size())) {
|
||||||
|
|
|
@ -37,8 +37,10 @@ auto SuperFX::unload() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SuperFX::power() -> void {
|
auto SuperFX::power() -> void {
|
||||||
|
double overclock = max(1.0, min(8.0, configuration.hacks.superfx.overclock / 100.0));
|
||||||
|
|
||||||
GSU::power();
|
GSU::power();
|
||||||
create(SuperFX::Enter, Frequency);
|
create(SuperFX::Enter, Frequency * overclock);
|
||||||
|
|
||||||
romMask = rom.size() - 1;
|
romMask = rom.size() - 1;
|
||||||
ramMask = ram.size() - 1;
|
ramMask = ram.size() - 1;
|
||||||
|
|
|
@ -58,8 +58,8 @@ auto CPU::power(bool reset) -> void {
|
||||||
PPUcounter::reset();
|
PPUcounter::reset();
|
||||||
PPUcounter::scanline = {&CPU::scanline, this};
|
PPUcounter::scanline = {&CPU::scanline, this};
|
||||||
|
|
||||||
function<auto (uint, uint8) -> uint8> reader;
|
function<uint8 (uint, uint8)> reader;
|
||||||
function<auto (uint, uint8) -> void> writer;
|
function<void (uint, uint8)> writer;
|
||||||
|
|
||||||
reader = {&CPU::readRAM, this};
|
reader = {&CPU::readRAM, this};
|
||||||
writer = {&CPU::writeRAM, this};
|
writer = {&CPU::writeRAM, this};
|
||||||
|
|
|
@ -68,6 +68,11 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
|
||||||
uint8 wram[128 * 1024];
|
uint8 wram[128 * 1024];
|
||||||
vector<Thread*> coprocessors;
|
vector<Thread*> coprocessors;
|
||||||
|
|
||||||
|
struct Overclocking {
|
||||||
|
uint counter = 0;
|
||||||
|
uint target = 0;
|
||||||
|
} overclocking;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint version = 2; //allowed: 1, 2
|
uint version = 2; //allowed: 1, 2
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,12 @@ auto CPU::stepOnce() -> void {
|
||||||
template<uint Clocks, bool Synchronize>
|
template<uint Clocks, bool Synchronize>
|
||||||
auto CPU::step() -> void {
|
auto CPU::step() -> void {
|
||||||
static_assert(Clocks == 2 || Clocks == 4 || Clocks == 6 || Clocks == 8 || Clocks == 10 || Clocks == 12);
|
static_assert(Clocks == 2 || Clocks == 4 || Clocks == 6 || Clocks == 8 || Clocks == 10 || Clocks == 12);
|
||||||
|
|
||||||
|
if(overclocking.target) {
|
||||||
|
overclocking.counter += Clocks;
|
||||||
|
if(overclocking.counter < overclocking.target) return;
|
||||||
|
}
|
||||||
|
|
||||||
if constexpr(Clocks >= 2) stepOnce();
|
if constexpr(Clocks >= 2) stepOnce();
|
||||||
if constexpr(Clocks >= 4) stepOnce();
|
if constexpr(Clocks >= 4) stepOnce();
|
||||||
if constexpr(Clocks >= 6) stepOnce();
|
if constexpr(Clocks >= 6) stepOnce();
|
||||||
|
@ -88,6 +94,17 @@ auto CPU::scanline() -> void {
|
||||||
status.hdmaPosition = 1104;
|
status.hdmaPosition = 1104;
|
||||||
status.hdmaTriggered = false;
|
status.hdmaTriggered = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//overclocking
|
||||||
|
if(vcounter() == (Region::NTSC() ? 261 : 311)) {
|
||||||
|
overclocking.counter = 0;
|
||||||
|
overclocking.target = 0;
|
||||||
|
double overclock = configuration.hacks.cpu.overclock / 100.0;
|
||||||
|
if(overclock > 1.0) {
|
||||||
|
int clocks = (Region::NTSC() ? 262 : 312) * 1364;
|
||||||
|
overclocking.target = clocks * overclock - clocks;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::aluEdge() -> void {
|
auto CPU::aluEdge() -> void {
|
||||||
|
|
|
@ -16,9 +16,9 @@ void DSP::main() {
|
||||||
clock += 2 * 32;
|
clock += 2 * 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
signed count = spc_dsp.sample_count();
|
int count = spc_dsp.sample_count();
|
||||||
if(count > 0) {
|
if(count > 0) {
|
||||||
for(unsigned n = 0; n < count; n += 2) {
|
for(uint n = 0; n < count; n += 2) {
|
||||||
stream->sample(samplebuffer[n + 0] / 32768.0f, samplebuffer[n + 1] / 32768.0f);
|
stream->sample(samplebuffer[n + 0] / 32768.0f, samplebuffer[n + 1] / 32768.0f);
|
||||||
}
|
}
|
||||||
spc_dsp.set_output(samplebuffer, 8192);
|
spc_dsp.set_output(samplebuffer, 8192);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
struct DSP {
|
struct DSP {
|
||||||
shared_pointer<Emulator::Stream> stream;
|
shared_pointer<Emulator::Stream> stream;
|
||||||
uint8 apuram[64 * 1024] = {};
|
uint8_t apuram[64 * 1024] = {};
|
||||||
|
|
||||||
void main();
|
void main();
|
||||||
uint8 read(uint8 addr);
|
uint8 read(uint8 addr);
|
||||||
|
|
|
@ -16,6 +16,7 @@ auto Configuration::process(Markup::Node document, bool load) -> void {
|
||||||
bind(boolean, "Video/BlurEmulation", video.blurEmulation);
|
bind(boolean, "Video/BlurEmulation", video.blurEmulation);
|
||||||
bind(boolean, "Video/ColorEmulation", video.colorEmulation);
|
bind(boolean, "Video/ColorEmulation", video.colorEmulation);
|
||||||
|
|
||||||
|
bind(natural, "Hacks/CPU/Overclock", hacks.cpu.overclock);
|
||||||
bind(boolean, "Hacks/PPU/Fast", hacks.ppu.fast);
|
bind(boolean, "Hacks/PPU/Fast", hacks.ppu.fast);
|
||||||
bind(boolean, "Hacks/PPU/NoSpriteLimit", hacks.ppu.noSpriteLimit);
|
bind(boolean, "Hacks/PPU/NoSpriteLimit", hacks.ppu.noSpriteLimit);
|
||||||
bind(natural, "Hacks/PPU/Mode7/Scale", hacks.ppu.mode7.scale);
|
bind(natural, "Hacks/PPU/Mode7/Scale", hacks.ppu.mode7.scale);
|
||||||
|
@ -26,6 +27,8 @@ auto Configuration::process(Markup::Node document, bool load) -> void {
|
||||||
bind(boolean, "Hacks/DSP/Cubic", hacks.dsp.cubic);
|
bind(boolean, "Hacks/DSP/Cubic", hacks.dsp.cubic);
|
||||||
bind(boolean, "Hacks/Coprocessors/HLE", hacks.coprocessors.hle);
|
bind(boolean, "Hacks/Coprocessors/HLE", hacks.coprocessors.hle);
|
||||||
bind(boolean, "Hacks/Coprocessors/DelayedSync", hacks.coprocessors.delayedSync);
|
bind(boolean, "Hacks/Coprocessors/DelayedSync", hacks.coprocessors.delayedSync);
|
||||||
|
bind(natural, "Hacks/SA1/Overclock", hacks.sa1.overclock);
|
||||||
|
bind(natural, "Hacks/SuperFX/Overclock", hacks.superfx.overclock);
|
||||||
|
|
||||||
#undef bind
|
#undef bind
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,9 @@ struct Configuration {
|
||||||
} video;
|
} video;
|
||||||
|
|
||||||
struct Hacks {
|
struct Hacks {
|
||||||
|
struct CPU {
|
||||||
|
uint overclock = 100;
|
||||||
|
} cpu;
|
||||||
struct PPU {
|
struct PPU {
|
||||||
bool fast = true;
|
bool fast = true;
|
||||||
bool noSpriteLimit = false;
|
bool noSpriteLimit = false;
|
||||||
|
@ -43,6 +46,12 @@ struct Configuration {
|
||||||
bool delayedSync = true;
|
bool delayedSync = true;
|
||||||
bool hle = true;
|
bool hle = true;
|
||||||
} coprocessors;
|
} coprocessors;
|
||||||
|
struct SA1 {
|
||||||
|
uint overclock = 100;
|
||||||
|
} sa1;
|
||||||
|
struct SuperFX {
|
||||||
|
uint overclock = 100;
|
||||||
|
} superfx;
|
||||||
} hacks;
|
} hacks;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -90,15 +90,11 @@ auto PPU::main() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::load() -> bool {
|
auto PPU::load() -> bool {
|
||||||
if(system.fastPPU()) {
|
|
||||||
return ppufast.load();
|
|
||||||
}
|
|
||||||
|
|
||||||
ppu1.version = max(1, min(1, configuration.system.ppu1.version));
|
ppu1.version = max(1, min(1, configuration.system.ppu1.version));
|
||||||
ppu2.version = max(1, min(3, configuration.system.ppu2.version));
|
ppu2.version = max(1, min(3, configuration.system.ppu2.version));
|
||||||
vram.mask = configuration.system.ppu1.vram.size / sizeof(uint16) - 1;
|
vram.mask = configuration.system.ppu1.vram.size / sizeof(uint16) - 1;
|
||||||
if(vram.mask != 0xffff) vram.mask = 0x7fff;
|
if(vram.mask != 0xffff) vram.mask = 0x7fff;
|
||||||
return true;
|
return true && ppufast.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::power(bool reset) -> void {
|
auto PPU::power(bool reset) -> void {
|
||||||
|
|
|
@ -32,7 +32,6 @@ auto System::runToSave() -> void {
|
||||||
|
|
||||||
auto System::load(Emulator::Interface* interface) -> bool {
|
auto System::load(Emulator::Interface* interface) -> bool {
|
||||||
information = {};
|
information = {};
|
||||||
hacks.fastPPU = configuration.hacks.ppu.fast;
|
|
||||||
|
|
||||||
bus.reset();
|
bus.reset();
|
||||||
if(!cpu.load()) return false;
|
if(!cpu.load()) return false;
|
||||||
|
@ -92,6 +91,8 @@ auto System::unload() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::power(bool reset) -> void {
|
auto System::power(bool reset) -> void {
|
||||||
|
hacks.fastPPU = configuration.hacks.ppu.fast;
|
||||||
|
|
||||||
Emulator::audio.reset(interface);
|
Emulator::audio.reset(interface);
|
||||||
|
|
||||||
random.entropy(Random::Entropy::Low);
|
random.entropy(Random::Entropy::Low);
|
||||||
|
|
|
@ -9,8 +9,11 @@ auto locate(string name) -> string {
|
||||||
string location = {Path::program(), name};
|
string location = {Path::program(), name};
|
||||||
if(inode::exists(location)) return location;
|
if(inode::exists(location)) return location;
|
||||||
|
|
||||||
directory::create({Path::userData(), "bsnes/"});
|
location = {Path::userData(), "bsnes/", name};
|
||||||
return {Path::userData(), "bsnes/", name};
|
if(inode::exists(location)) return location;
|
||||||
|
|
||||||
|
directory::create({Path::userSettings(), "bsnes/"});
|
||||||
|
return {Path::userSettings(), "bsnes/", name};
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <nall/main.hpp>
|
#include <nall/main.hpp>
|
||||||
|
@ -19,7 +22,7 @@ auto nall::main(Arguments arguments) -> void {
|
||||||
|
|
||||||
for(auto argument : arguments) {
|
for(auto argument : arguments) {
|
||||||
if(argument == "--fullscreen") {
|
if(argument == "--fullscreen") {
|
||||||
presentation.startFullScreen = true;
|
program.startFullScreen = true;
|
||||||
} else if(argument.beginsWith("--locale=")) {
|
} else if(argument.beginsWith("--locale=")) {
|
||||||
Application::locale().scan(locate("Locale/"));
|
Application::locale().scan(locate("Locale/"));
|
||||||
Application::locale().select(argument.trimLeft("--locale=", 1L));
|
Application::locale().select(argument.trimLeft("--locale=", 1L));
|
||||||
|
|
|
@ -18,6 +18,7 @@ extern unique_pointer<Emulator::Interface> emulator;
|
||||||
#include <nall/decode/zip.hpp>
|
#include <nall/decode/zip.hpp>
|
||||||
#include <nall/encode/rle.hpp>
|
#include <nall/encode/rle.hpp>
|
||||||
#include <nall/encode/zip.hpp>
|
#include <nall/encode/zip.hpp>
|
||||||
|
#include <nall/hash/crc16.hpp>
|
||||||
|
|
||||||
#include "program/program.hpp"
|
#include "program/program.hpp"
|
||||||
#include "input/input.hpp"
|
#include "input/input.hpp"
|
||||||
|
|
|
@ -1,5 +1,15 @@
|
||||||
|
auto InputHotkey::logic() const -> Logic {
|
||||||
|
return inputManager.hotkeyLogic;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
auto InputManager::bindHotkeys() -> void {
|
auto InputManager::bindHotkeys() -> void {
|
||||||
static int stateSlot = 1;
|
static int stateSlot = 1;
|
||||||
|
static double frequency = 48000.0;
|
||||||
|
static double volume = 0.0;
|
||||||
|
static bool fastForwarding = false;
|
||||||
|
static bool rewinding = false;
|
||||||
|
|
||||||
hotkeys.append(InputHotkey("Toggle Fullscreen Mode").onPress([] {
|
hotkeys.append(InputHotkey("Toggle Fullscreen Mode").onPress([] {
|
||||||
presentation.toggleFullscreenMode();
|
presentation.toggleFullscreenMode();
|
||||||
|
@ -14,15 +24,24 @@ auto InputManager::bindHotkeys() -> void {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
hotkeys.append(InputHotkey("Rewind").onPress([&] {
|
hotkeys.append(InputHotkey("Rewind").onPress([&] {
|
||||||
if(!emulator->loaded()) return;
|
if(!emulator->loaded() || fastForwarding) return;
|
||||||
|
rewinding = true;
|
||||||
if(program.rewind.frequency == 0) {
|
if(program.rewind.frequency == 0) {
|
||||||
program.showMessage("Please enable rewind support in Settings->Emulator first");
|
program.showMessage("Please enable rewind support in Settings->Emulator first");
|
||||||
} else {
|
} else {
|
||||||
program.rewindMode(Program::Rewind::Mode::Rewinding);
|
program.rewindMode(Program::Rewind::Mode::Rewinding);
|
||||||
}
|
}
|
||||||
|
volume = Emulator::audio.volume();
|
||||||
|
if(settings.rewind.mute) {
|
||||||
|
Emulator::audio.setVolume(0.0);
|
||||||
|
} else {
|
||||||
|
Emulator::audio.setVolume(volume * 0.65);
|
||||||
|
}
|
||||||
}).onRelease([&] {
|
}).onRelease([&] {
|
||||||
|
rewinding = false;
|
||||||
if(!emulator->loaded()) return;
|
if(!emulator->loaded()) return;
|
||||||
program.rewindMode(Program::Rewind::Mode::Playing);
|
program.rewindMode(Program::Rewind::Mode::Playing);
|
||||||
|
Emulator::audio.setVolume(volume);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
hotkeys.append(InputHotkey("Save State").onPress([&] {
|
hotkeys.append(InputHotkey("Save State").onPress([&] {
|
||||||
|
@ -56,15 +75,33 @@ auto InputManager::bindHotkeys() -> void {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
hotkeys.append(InputHotkey("Fast Forward").onPress([] {
|
hotkeys.append(InputHotkey("Fast Forward").onPress([] {
|
||||||
emulator->setFrameSkip(emulator->configuration("Hacks/PPU/Fast") == "true" && settings.video.fastForwardFrameSkip ? 9 : 0);
|
if(!emulator->loaded() || rewinding) return;
|
||||||
|
fastForwarding = true;
|
||||||
|
emulator->setFrameSkip(emulator->configuration("Hacks/PPU/Fast") == "true" ? settings.fastForward.frameSkip : 0);
|
||||||
video.setBlocking(false);
|
video.setBlocking(false);
|
||||||
audio.setBlocking(false);
|
audio.setBlocking(settings.fastForward.limiter != 0);
|
||||||
audio.setDynamic(false);
|
audio.setDynamic(false);
|
||||||
|
frequency = Emulator::audio.frequency();
|
||||||
|
volume = Emulator::audio.volume();
|
||||||
|
if(settings.fastForward.limiter) {
|
||||||
|
Emulator::audio.setFrequency(frequency / settings.fastForward.limiter);
|
||||||
|
}
|
||||||
|
if(settings.fastForward.mute) {
|
||||||
|
Emulator::audio.setVolume(0.0);
|
||||||
|
} else if(settings.fastForward.limiter) {
|
||||||
|
Emulator::audio.setVolume(volume * 0.65);
|
||||||
|
}
|
||||||
}).onRelease([] {
|
}).onRelease([] {
|
||||||
|
fastForwarding = false;
|
||||||
|
if(!emulator->loaded()) return;
|
||||||
emulator->setFrameSkip(0);
|
emulator->setFrameSkip(0);
|
||||||
video.setBlocking(settings.video.blocking);
|
video.setBlocking(settings.video.blocking);
|
||||||
audio.setBlocking(settings.audio.blocking);
|
audio.setBlocking(settings.audio.blocking);
|
||||||
audio.setDynamic(settings.audio.dynamic);
|
audio.setDynamic(settings.audio.dynamic);
|
||||||
|
if(settings.fastForward.limiter) {
|
||||||
|
Emulator::audio.setFrequency(frequency);
|
||||||
|
}
|
||||||
|
Emulator::audio.setVolume(volume);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
hotkeys.append(InputHotkey("Pause Emulation").onPress([] {
|
hotkeys.append(InputHotkey("Pause Emulation").onPress([] {
|
||||||
|
@ -85,7 +122,8 @@ auto InputManager::bindHotkeys() -> void {
|
||||||
|
|
||||||
for(auto& hotkey : hotkeys) {
|
for(auto& hotkey : hotkeys) {
|
||||||
hotkey.path = string{"Hotkey/", hotkey.name}.replace(" ", "");
|
hotkey.path = string{"Hotkey/", hotkey.name}.replace(" ", "");
|
||||||
hotkey.assignment = settings(hotkey.path).text();
|
auto assignments = settings(hotkey.path).text().split(";");
|
||||||
|
for(uint index : range(BindingLimit)) hotkey.assignments[index] = assignments(index);
|
||||||
hotkey.bind();
|
hotkey.bind();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,13 @@
|
||||||
InputManager inputManager;
|
InputManager inputManager;
|
||||||
|
|
||||||
auto InputMapping::bind() -> void {
|
auto InputMapping::bind() -> void {
|
||||||
mappings.reset();
|
for(auto& binding : bindings) binding = {};
|
||||||
|
|
||||||
for(auto& item : assignment.split(logic() == Logic::AND ? "&" : "|")) {
|
for(uint index : range(BindingLimit)) {
|
||||||
auto token = item.split("/");
|
auto& assignment = assignments[index];
|
||||||
|
auto& binding = bindings[index];
|
||||||
|
|
||||||
|
auto token = assignment.split("/");
|
||||||
if(token.size() < 3) continue; //skip invalid mappings
|
if(token.size() < 3) continue; //skip invalid mappings
|
||||||
|
|
||||||
uint64 id = token[0].natural();
|
uint64 id = token[0].natural();
|
||||||
|
@ -14,41 +17,35 @@ auto InputMapping::bind() -> void {
|
||||||
uint input = token[2].natural();
|
uint input = token[2].natural();
|
||||||
string qualifier = token(3, "None");
|
string qualifier = token(3, "None");
|
||||||
|
|
||||||
Mapping mapping;
|
|
||||||
for(auto& device : inputManager.devices) {
|
for(auto& device : inputManager.devices) {
|
||||||
if(id != device->id()) continue;
|
if(id != device->id()) continue;
|
||||||
|
|
||||||
mapping.device = device;
|
binding.device = device;
|
||||||
mapping.group = group;
|
binding.group = group;
|
||||||
mapping.input = input;
|
binding.input = input;
|
||||||
mapping.qualifier = Qualifier::None;
|
binding.qualifier = Qualifier::None;
|
||||||
if(qualifier == "Lo") mapping.qualifier = Qualifier::Lo;
|
if(qualifier == "Lo") binding.qualifier = Qualifier::Lo;
|
||||||
if(qualifier == "Hi") mapping.qualifier = Qualifier::Hi;
|
if(qualifier == "Hi") binding.qualifier = Qualifier::Hi;
|
||||||
if(qualifier == "Rumble") mapping.qualifier = Qualifier::Rumble;
|
if(qualifier == "Rumble") binding.qualifier = Qualifier::Rumble;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!mapping.device) continue;
|
|
||||||
mappings.append(mapping);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
settings[path].setValue(assignment);
|
string text;
|
||||||
|
for(auto& assignment : assignments) text.append(assignment, ";");
|
||||||
|
text.trimRight(";");
|
||||||
|
settings[path].setValue(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto InputMapping::bind(string mapping) -> void {
|
auto InputMapping::bind(string mapping, uint binding) -> void {
|
||||||
if(assignment.split(logic() == Logic::AND ? "&" : "|").find(mapping)) return; //ignore if already in mappings list
|
if(binding >= BindingLimit) return;
|
||||||
if(!assignment || assignment == "None") { //create new mapping
|
assignments[binding] = mapping;
|
||||||
assignment = mapping;
|
|
||||||
} else { //add additional mapping
|
|
||||||
assignment.append(logic() == Logic::AND ? "&" : "|");
|
|
||||||
assignment.append(mapping);
|
|
||||||
}
|
|
||||||
bind();
|
bind();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto InputMapping::bind(shared_pointer<HID::Device> device, uint group, uint input, int16 oldValue, int16 newValue) -> bool {
|
auto InputMapping::bind(shared_pointer<HID::Device> device, uint group, uint input, int16 oldValue, int16 newValue, uint binding) -> bool {
|
||||||
if(device->isNull() || (device->isKeyboard() && device->group(group).input(input).name() == "Escape")) {
|
if(device->isNull() || (device->isKeyboard() && device->group(group).input(input).name() == "Escape")) {
|
||||||
return unbind(), true;
|
return unbind(binding), true;
|
||||||
}
|
}
|
||||||
|
|
||||||
string encoding = {"0x", hex(device->id()), "/", group, "/", input};
|
string encoding = {"0x", hex(device->id()), "/", group, "/", input};
|
||||||
|
@ -58,7 +55,7 @@ auto InputMapping::bind(shared_pointer<HID::Device> device, uint group, uint inp
|
||||||
|| (device->isMouse() && group == HID::Mouse::GroupID::Button)
|
|| (device->isMouse() && group == HID::Mouse::GroupID::Button)
|
||||||
|| (device->isJoypad() && group == HID::Joypad::GroupID::Button)) {
|
|| (device->isJoypad() && group == HID::Joypad::GroupID::Button)) {
|
||||||
if(newValue) {
|
if(newValue) {
|
||||||
return bind(encoding), true;
|
return bind(encoding, binding), true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,11 +63,11 @@ auto InputMapping::bind(shared_pointer<HID::Device> device, uint group, uint inp
|
||||||
|| (device->isJoypad() && group == HID::Joypad::GroupID::Hat)
|
|| (device->isJoypad() && group == HID::Joypad::GroupID::Hat)
|
||||||
|| (device->isJoypad() && group == HID::Joypad::GroupID::Trigger)) {
|
|| (device->isJoypad() && group == HID::Joypad::GroupID::Trigger)) {
|
||||||
if(newValue < -16384 && group != HID::Joypad::GroupID::Trigger) { //triggers are always hi
|
if(newValue < -16384 && group != HID::Joypad::GroupID::Trigger) { //triggers are always hi
|
||||||
return bind({encoding, "/Lo"}), true;
|
return bind({encoding, "/Lo"}, binding), true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(newValue > +16384) {
|
if(newValue > +16384) {
|
||||||
return bind({encoding, "/Hi"}), true;
|
return bind({encoding, "/Hi"}, binding), true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +77,7 @@ auto InputMapping::bind(shared_pointer<HID::Device> device, uint group, uint inp
|
||||||
|| (device->isJoypad() && group == HID::Joypad::GroupID::Axis)
|
|| (device->isJoypad() && group == HID::Joypad::GroupID::Axis)
|
||||||
|| (device->isJoypad() && group == HID::Joypad::GroupID::Hat)) {
|
|| (device->isJoypad() && group == HID::Joypad::GroupID::Hat)) {
|
||||||
if(newValue < -16384 || newValue > +16384) {
|
if(newValue < -16384 || newValue > +16384) {
|
||||||
return bind(encoding), true;
|
return bind(encoding, binding), true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +85,7 @@ auto InputMapping::bind(shared_pointer<HID::Device> device, uint group, uint inp
|
||||||
if(isRumble()) {
|
if(isRumble()) {
|
||||||
if(device->isJoypad() && group == HID::Joypad::GroupID::Button) {
|
if(device->isJoypad() && group == HID::Joypad::GroupID::Button) {
|
||||||
if(newValue) {
|
if(newValue) {
|
||||||
return bind({encoding, "/Rumble"}), true;
|
return bind({encoding, "/Rumble"}, binding), true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,9 +93,13 @@ auto InputMapping::bind(shared_pointer<HID::Device> device, uint group, uint inp
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto InputMapping::unbind() -> void {
|
auto InputMapping::unbind(uint binding) -> void {
|
||||||
mappings.reset();
|
bindings[binding] = {};
|
||||||
settings[path].setValue(assignment = "None");
|
assignments[binding] = {};
|
||||||
|
string text;
|
||||||
|
for(auto& assignment : assignments) text.append(assignment, ";");
|
||||||
|
text.trimRight(";");
|
||||||
|
settings[path].setValue(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto InputMapping::poll() -> int16 {
|
auto InputMapping::poll() -> int16 {
|
||||||
|
@ -108,13 +109,17 @@ auto InputMapping::poll() -> int16 {
|
||||||
if(result) return inputManager.turboCounter >= inputManager.turboFrequency;
|
if(result) return inputManager.turboCounter >= inputManager.turboFrequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint validBindings = 0;
|
||||||
int16 result = 0;
|
int16 result = 0;
|
||||||
|
|
||||||
for(auto& mapping : mappings) {
|
for(auto& binding : bindings) {
|
||||||
auto& device = mapping.device;
|
if(!binding.device) continue; //unbound
|
||||||
auto& group = mapping.group;
|
validBindings++;
|
||||||
auto& input = mapping.input;
|
|
||||||
auto& qualifier = mapping.qualifier;
|
auto& device = binding.device;
|
||||||
|
auto& group = binding.group;
|
||||||
|
auto& input = binding.input;
|
||||||
|
auto& qualifier = binding.qualifier;
|
||||||
auto value = device->group(group).input(input).value();
|
auto value = device->group(group).input(input).value();
|
||||||
|
|
||||||
if(isDigital()) {
|
if(isDigital()) {
|
||||||
|
@ -144,38 +149,43 @@ auto InputMapping::poll() -> int16 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isDigital() && logic() == Logic::AND) return 1;
|
if(isDigital() && logic() == Logic::AND && validBindings > 0) return 1;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto InputMapping::rumble(bool enable) -> void {
|
auto InputMapping::rumble(bool enable) -> void {
|
||||||
for(auto& mapping : mappings) {
|
for(auto& binding : bindings) {
|
||||||
input.rumble(mapping.device->id(), enable);
|
if(!binding.device) continue;
|
||||||
|
input.rumble(binding.device->id(), enable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto InputMapping::displayName() -> string {
|
//
|
||||||
if(!mappings) return "None";
|
|
||||||
|
|
||||||
string path;
|
auto InputMapping::Binding::icon() -> image {
|
||||||
for(auto& mapping : mappings) {
|
if(device && device->isKeyboard()) return Icon::Device::Keyboard;
|
||||||
path.append(mapping.device->name());
|
if(device && device->isMouse()) return Icon::Device::Mouse;
|
||||||
if(mapping.device->name() != "Keyboard" && mapping.device->name() != "Mouse") {
|
if(device && device->isJoypad()) return Icon::Device::Joypad;
|
||||||
//show device IDs to distinguish between multiple joypads
|
return {};
|
||||||
path.append("(", hex(mapping.device->id()), ")");
|
|
||||||
}
|
|
||||||
if(mapping.device->name() != "Keyboard" && mapping.device->name() != "Mouse") {
|
|
||||||
//keyboards only have one group; no need to append group name
|
|
||||||
path.append(".", mapping.device->group(mapping.group).name());
|
|
||||||
}
|
|
||||||
path.append(".", mapping.device->group(mapping.group).input(mapping.input).name());
|
|
||||||
if(mapping.qualifier == Qualifier::Lo) path.append(".Lo");
|
|
||||||
if(mapping.qualifier == Qualifier::Hi) path.append(".Hi");
|
|
||||||
if(mapping.qualifier == Qualifier::Rumble) path.append(".Rumble");
|
|
||||||
path.append(logic() == Logic::AND ? " and " : " or ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return path.trimRight(logic() == Logic::AND ? " and " : " or ", 1L);
|
auto InputMapping::Binding::name() -> string {
|
||||||
|
if(device && device->isKeyboard()) {
|
||||||
|
return device->group(group).input(input).name();
|
||||||
|
}
|
||||||
|
if(device && device->isMouse()) {
|
||||||
|
return device->group(group).input(input).name();
|
||||||
|
}
|
||||||
|
if(device && device->isJoypad()) {
|
||||||
|
string name{Hash::CRC16(string{device->id()}).digest().upcase()};
|
||||||
|
name.append(" ", device->group(group).name());
|
||||||
|
name.append(" ", device->group(group).input(input).name());
|
||||||
|
if(qualifier == Qualifier::Lo) name.append(" Lo");
|
||||||
|
if(qualifier == Qualifier::Hi) name.append(" Hi");
|
||||||
|
if(qualifier == Qualifier::Rumble) name.append(" Rumble");
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -212,7 +222,8 @@ auto InputManager::initialize() -> void {
|
||||||
inputMapping.name = input.name;
|
inputMapping.name = input.name;
|
||||||
inputMapping.type = input.type;
|
inputMapping.type = input.type;
|
||||||
inputMapping.path = string{information.name, "/", inputPort.name, "/", inputDevice.name, "/", inputMapping.name}.replace(" ", "");
|
inputMapping.path = string{information.name, "/", inputPort.name, "/", inputDevice.name, "/", inputMapping.name}.replace(" ", "");
|
||||||
inputMapping.assignment = settings(inputMapping.path).text();
|
auto assignments = settings(inputMapping.path).text().split(";");
|
||||||
|
for(uint index : range(BindingLimit)) inputMapping.assignments[index] = assignments(index);
|
||||||
inputDevice.mappings.append(inputMapping);
|
inputDevice.mappings.append(inputMapping);
|
||||||
}
|
}
|
||||||
for(uint inputID : range(inputs.size())) {
|
for(uint inputID : range(inputs.size())) {
|
||||||
|
@ -226,7 +237,8 @@ auto InputManager::initialize() -> void {
|
||||||
inputMapping.name = string{"Turbo ", input.name};
|
inputMapping.name = string{"Turbo ", input.name};
|
||||||
inputMapping.type = input.type;
|
inputMapping.type = input.type;
|
||||||
inputMapping.path = string{information.name, "/", inputPort.name, "/", inputDevice.name, "/", inputMapping.name}.replace(" ", "");
|
inputMapping.path = string{information.name, "/", inputPort.name, "/", inputDevice.name, "/", inputMapping.name}.replace(" ", "");
|
||||||
inputMapping.assignment = settings(inputMapping.path).text();
|
auto assignments = settings(inputMapping.path).text().split(";");
|
||||||
|
for(uint index : range(BindingLimit)) inputMapping.assignments[index] = assignments(index);
|
||||||
inputDevice.mappings.append(inputMapping);
|
inputDevice.mappings.append(inputMapping);
|
||||||
inputDevice.mappings[inputID].turboID = turboID;
|
inputDevice.mappings[inputID].turboID = turboID;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
|
enum : uint { BindingLimit = 4 };
|
||||||
|
|
||||||
struct InputMapping {
|
struct InputMapping {
|
||||||
auto bind() -> void;
|
auto bind() -> void;
|
||||||
auto bind(string mapping) -> void;
|
auto bind(string mapping, uint binding) -> void;
|
||||||
auto bind(shared_pointer<HID::Device> device, uint group, uint input, int16 oldValue, int16 newValue) -> bool;
|
auto bind(shared_pointer<HID::Device> device, uint group, uint input, int16 oldValue, int16 newValue, uint binding) -> bool;
|
||||||
auto unbind() -> void;
|
auto unbind(uint binding) -> void;
|
||||||
auto poll() -> int16;
|
auto poll() -> int16;
|
||||||
auto rumble(bool enable) -> void;
|
auto rumble(bool enable) -> void;
|
||||||
auto displayName() -> string;
|
|
||||||
|
|
||||||
using Type = Emulator::Interface::Input::Type;
|
using Type = Emulator::Interface::Input::Type;
|
||||||
auto isDigital() const -> bool {
|
auto isDigital() const -> bool {
|
||||||
|
@ -26,19 +27,22 @@ struct InputMapping {
|
||||||
string path; //configuration file key path
|
string path; //configuration file key path
|
||||||
string name; //input name (human readable)
|
string name; //input name (human readable)
|
||||||
uint type = 0;
|
uint type = 0;
|
||||||
string assignment = "None";
|
string assignments[BindingLimit];
|
||||||
|
|
||||||
enum class Logic : uint { AND, OR };
|
enum class Logic : uint { AND, OR };
|
||||||
enum class Qualifier : uint { None, Lo, Hi, Rumble };
|
enum class Qualifier : uint { None, Lo, Hi, Rumble };
|
||||||
virtual auto logic() const -> Logic { return Logic::OR; }
|
virtual auto logic() const -> Logic { return Logic::OR; }
|
||||||
|
|
||||||
struct Mapping {
|
struct Binding {
|
||||||
|
auto icon() -> image;
|
||||||
|
auto name() -> string;
|
||||||
|
|
||||||
shared_pointer<HID::Device> device;
|
shared_pointer<HID::Device> device;
|
||||||
uint group = 0;
|
uint group = 0;
|
||||||
uint input = 0;
|
uint input = 0;
|
||||||
Qualifier qualifier = Qualifier::None;
|
Qualifier qualifier = Qualifier::None;
|
||||||
};
|
};
|
||||||
vector<Mapping> mappings;
|
Binding bindings[BindingLimit];
|
||||||
|
|
||||||
uint3 turboCounter = 0;
|
uint3 turboCounter = 0;
|
||||||
};
|
};
|
||||||
|
@ -47,10 +51,10 @@ struct InputHotkey : InputMapping {
|
||||||
InputHotkey(string name) { this->name = name; }
|
InputHotkey(string name) { this->name = name; }
|
||||||
auto& onPress(function<void ()> press) { return this->press = press, *this; }
|
auto& onPress(function<void ()> press) { return this->press = press, *this; }
|
||||||
auto& onRelease(function<void ()> release) { return this->release = release, *this; }
|
auto& onRelease(function<void ()> release) { return this->release = release, *this; }
|
||||||
//auto logic() const -> Logic override { return Logic::AND; }
|
auto logic() const -> Logic override;
|
||||||
|
|
||||||
function<auto() -> void> press;
|
function<void ()> press;
|
||||||
function<auto() -> void> release;
|
function<void ()> release;
|
||||||
int16 state = 0;
|
int16 state = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -67,6 +71,8 @@ struct InputPort {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InputManager {
|
struct InputManager {
|
||||||
|
InputMapping::Logic hotkeyLogic = InputMapping::Logic::OR;
|
||||||
|
|
||||||
auto initialize() -> void;
|
auto initialize() -> void;
|
||||||
auto bind() -> void;
|
auto bind() -> void;
|
||||||
auto poll() -> void;
|
auto poll() -> void;
|
||||||
|
|
|
@ -102,8 +102,9 @@ auto Presentation::create() -> void {
|
||||||
inputSettings.setIcon(Icon::Device::Joypad).setText("Input ...").onActivate([&] { settingsWindow.show(2); });
|
inputSettings.setIcon(Icon::Device::Joypad).setText("Input ...").onActivate([&] { settingsWindow.show(2); });
|
||||||
hotkeySettings.setIcon(Icon::Device::Keyboard).setText("Hotkeys ...").onActivate([&] { settingsWindow.show(3); });
|
hotkeySettings.setIcon(Icon::Device::Keyboard).setText("Hotkeys ...").onActivate([&] { settingsWindow.show(3); });
|
||||||
pathSettings.setIcon(Icon::Emblem::Folder).setText("Paths ...").onActivate([&] { settingsWindow.show(4); });
|
pathSettings.setIcon(Icon::Emblem::Folder).setText("Paths ...").onActivate([&] { settingsWindow.show(4); });
|
||||||
emulatorSettings.setIcon(Icon::Action::Settings).setText("Emulator ...").onActivate([&] { settingsWindow.show(5); });
|
speedSettings.setIcon(Icon::Device::Clock).setText("Speed ...").onActivate([&] { settingsWindow.show(5); });
|
||||||
driverSettings.setIcon(Icon::Place::Settings).setText("Drivers ...").onActivate([&] { settingsWindow.show(6); });
|
emulatorSettings.setIcon(Icon::Action::Settings).setText("Emulator ...").onActivate([&] { settingsWindow.show(6); });
|
||||||
|
driverSettings.setIcon(Icon::Place::Settings).setText("Drivers ...").onActivate([&] { settingsWindow.show(7); });
|
||||||
|
|
||||||
toolsMenu.setText(tr("Tools")).setVisible(false);
|
toolsMenu.setText(tr("Tools")).setVisible(false);
|
||||||
saveState.setIcon(Icon::Action::Save).setText("Save State");
|
saveState.setIcon(Icon::Action::Save).setText("Save State");
|
||||||
|
@ -163,7 +164,7 @@ auto Presentation::create() -> void {
|
||||||
captureScreenshot.setIcon(Icon::Emblem::Image).setText("Capture Screenshot").onActivate([&] {
|
captureScreenshot.setIcon(Icon::Emblem::Image).setText("Capture Screenshot").onActivate([&] {
|
||||||
program.captureScreenshot();
|
program.captureScreenshot();
|
||||||
});
|
});
|
||||||
cheatFinder.setIcon(Icon::Edit::Find).setText("Cheat Finder ...").onActivate([&] { toolsWindow.show(0); });
|
cheatFinder.setIcon(Icon::Action::Search).setText("Cheat Finder ...").onActivate([&] { toolsWindow.show(0); });
|
||||||
cheatEditor.setIcon(Icon::Edit::Replace).setText("Cheat Editor ...").onActivate([&] { toolsWindow.show(1); });
|
cheatEditor.setIcon(Icon::Edit::Replace).setText("Cheat Editor ...").onActivate([&] { toolsWindow.show(1); });
|
||||||
stateManager.setIcon(Icon::Application::FileManager).setText("State Manager ...").onActivate([&] { toolsWindow.show(2); });
|
stateManager.setIcon(Icon::Application::FileManager).setText("State Manager ...").onActivate([&] { toolsWindow.show(2); });
|
||||||
manifestViewer.setIcon(Icon::Emblem::Text).setText("Manifest Viewer ...").onActivate([&] { toolsWindow.show(3); });
|
manifestViewer.setIcon(Icon::Emblem::Text).setText("Manifest Viewer ...").onActivate([&] { toolsWindow.show(3); });
|
||||||
|
@ -248,10 +249,6 @@ auto Presentation::create() -> void {
|
||||||
resizeWindow();
|
resizeWindow();
|
||||||
setAlignment(Alignment::Center);
|
setAlignment(Alignment::Center);
|
||||||
|
|
||||||
//start in fullscreen mode if requested ...
|
|
||||||
//perform the exclusive mode change later on inside Program::create(), after the video driver has been initialized
|
|
||||||
if(startFullScreen) setFullScreen();
|
|
||||||
|
|
||||||
#if defined(PLATFORM_MACOS)
|
#if defined(PLATFORM_MACOS)
|
||||||
Application::Cocoa::onAbout([&] { about.doActivate(); });
|
Application::Cocoa::onAbout([&] { about.doActivate(); });
|
||||||
Application::Cocoa::onActivate([&] { setFocused(); });
|
Application::Cocoa::onActivate([&] { setFocused(); });
|
||||||
|
|
|
@ -19,8 +19,6 @@ struct Presentation : Window {
|
||||||
auto addRecentGame(string location) -> void;
|
auto addRecentGame(string location) -> void;
|
||||||
auto updateShaders() -> void;
|
auto updateShaders() -> void;
|
||||||
|
|
||||||
bool startFullScreen = false;
|
|
||||||
|
|
||||||
MenuBar menuBar{this};
|
MenuBar menuBar{this};
|
||||||
Menu systemMenu{&menuBar};
|
Menu systemMenu{&menuBar};
|
||||||
MenuItem loadGame{&systemMenu};
|
MenuItem loadGame{&systemMenu};
|
||||||
|
@ -88,6 +86,7 @@ struct Presentation : Window {
|
||||||
MenuItem inputSettings{&settingsMenu};
|
MenuItem inputSettings{&settingsMenu};
|
||||||
MenuItem hotkeySettings{&settingsMenu};
|
MenuItem hotkeySettings{&settingsMenu};
|
||||||
MenuItem pathSettings{&settingsMenu};
|
MenuItem pathSettings{&settingsMenu};
|
||||||
|
MenuItem speedSettings{&settingsMenu};
|
||||||
MenuItem emulatorSettings{&settingsMenu};
|
MenuItem emulatorSettings{&settingsMenu};
|
||||||
MenuItem driverSettings{&settingsMenu};
|
MenuItem driverSettings{&settingsMenu};
|
||||||
Menu toolsMenu{&menuBar};
|
Menu toolsMenu{&menuBar};
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
auto Program::load() -> void {
|
auto Program::load() -> void {
|
||||||
unload();
|
unload();
|
||||||
|
|
||||||
if(auto configuration = string::read(locate("configuration.bml"))) {
|
emulator->configure("Hacks/CPU/Overclock", settings.emulator.hack.cpu.overclock);
|
||||||
emulator->configure(configuration);
|
emulator->configure("Hacks/PPU/Fast", settings.emulator.hack.ppu.fast);
|
||||||
emulatorSettings.updateConfiguration();
|
emulator->configure("Hacks/PPU/NoSpriteLimit", settings.emulator.hack.ppu.noSpriteLimit);
|
||||||
}
|
emulator->configure("Hacks/PPU/Mode7/Scale", settings.emulator.hack.ppu.mode7.scale);
|
||||||
|
emulator->configure("Hacks/PPU/Mode7/Perspective", settings.emulator.hack.ppu.mode7.perspective);
|
||||||
|
emulator->configure("Hacks/PPU/Mode7/Supersample", settings.emulator.hack.ppu.mode7.supersample);
|
||||||
|
emulator->configure("Hacks/PPU/Mode7/Mosaic", settings.emulator.hack.ppu.mode7.mosaic);
|
||||||
|
emulator->configure("Hacks/DSP/Fast", settings.emulator.hack.dsp.fast);
|
||||||
|
emulator->configure("Hacks/DSP/Cubic", settings.emulator.hack.dsp.cubic);
|
||||||
|
emulator->configure("Hacks/Coprocessor/DelayedSync", settings.emulator.hack.coprocessors.delayedSync);
|
||||||
|
emulator->configure("Hacks/Coprocessor/HLE", settings.emulator.hack.coprocessors.hle);
|
||||||
|
emulator->configure("Hacks/SuperFX/Overclock", settings.emulator.hack.superfx.overclock);
|
||||||
if(!emulator->load()) return;
|
if(!emulator->load()) return;
|
||||||
|
|
||||||
gameQueue = {};
|
gameQueue = {};
|
||||||
|
@ -123,7 +131,6 @@ auto Program::loadSuperFamicom(string location) -> bool {
|
||||||
superFamicom.title = heuristics.title();
|
superFamicom.title = heuristics.title();
|
||||||
superFamicom.manifest = manifest ? manifest : heuristics.manifest();
|
superFamicom.manifest = manifest ? manifest : heuristics.manifest();
|
||||||
hackPatchMemory(rom);
|
hackPatchMemory(rom);
|
||||||
hackOverclockSuperFX();
|
|
||||||
superFamicom.document = BML::unserialize(superFamicom.manifest);
|
superFamicom.document = BML::unserialize(superFamicom.manifest);
|
||||||
superFamicom.location = location;
|
superFamicom.location = location;
|
||||||
|
|
||||||
|
@ -308,9 +315,6 @@ auto Program::unload() -> void {
|
||||||
if(emulatorSettings.autoSaveStateOnUnload.checked()) {
|
if(emulatorSettings.autoSaveStateOnUnload.checked()) {
|
||||||
saveUndoState();
|
saveUndoState();
|
||||||
}
|
}
|
||||||
if(auto configuration = emulator->configuration()) {
|
|
||||||
file::write(locate("configuration.bml"), configuration);
|
|
||||||
}
|
|
||||||
emulator->unload();
|
emulator->unload();
|
||||||
showMessage("Game unloaded");
|
showMessage("Game unloaded");
|
||||||
superFamicom = {};
|
superFamicom = {};
|
||||||
|
|
|
@ -34,32 +34,3 @@ auto Program::hackPatchMemory(vector<uint8_t>& data) -> void {
|
||||||
if(data[0x4e9a] == 0x10) data[0x4e9a] = 0x80;
|
if(data[0x4e9a] == 0x10) data[0x4e9a] = 0x80;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Program::hackOverclockSuperFX() -> void {
|
|
||||||
//todo: implement a better way of detecting SuperFX games
|
|
||||||
//todo: apply multiplier changes on reset, not just on game load?
|
|
||||||
double multiplier = emulatorSettings.superFXValue.text().natural() / 100.0;
|
|
||||||
if(multiplier == 1.0) return;
|
|
||||||
|
|
||||||
auto title = superFamicom.title;
|
|
||||||
if(title == "NIDAN MORITASHOGI2") return; //ST018 uses same clock speed as SuperFX
|
|
||||||
|
|
||||||
auto document = BML::unserialize(superFamicom.manifest);
|
|
||||||
|
|
||||||
//GSU-1, GSU-2 have a 21440000hz oscillator
|
|
||||||
if(auto oscillator = document["game/board/oscillator"]) {
|
|
||||||
if(oscillator["frequency"].text() == "21440000") {
|
|
||||||
oscillator["frequency"].setValue(uint(21440000 * multiplier));
|
|
||||||
superFamicom.manifest = BML::serialize(document);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//MARIO CHIP 1 uses CPU oscillator; force it to use its own crystal to overclock it
|
|
||||||
bool marioChip1 = false;
|
|
||||||
if(title == "STAR FOX" || title == "STAR WING") marioChip1 = true;
|
|
||||||
if(marioChip1) {
|
|
||||||
document("game/board/oscillator/frequency").setValue(uint(21440000 * multiplier));
|
|
||||||
superFamicom.manifest = BML::serialize(document);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -262,7 +262,7 @@ auto Program::audioFrame(const float* samples, uint channels) -> void {
|
||||||
|
|
||||||
auto Program::inputPoll(uint port, uint device, uint input) -> int16 {
|
auto Program::inputPoll(uint port, uint device, uint input) -> int16 {
|
||||||
int16 value = 0;
|
int16 value = 0;
|
||||||
if(focused() || emulatorSettings.allowInput().checked()) {
|
if(focused() || inputSettings.allowInput().checked()) {
|
||||||
inputManager.poll();
|
inputManager.poll();
|
||||||
if(auto mapping = inputManager.mapping(port, device, input)) {
|
if(auto mapping = inputManager.mapping(port, device, input)) {
|
||||||
value = mapping->poll();
|
value = mapping->poll();
|
||||||
|
@ -282,7 +282,7 @@ auto Program::inputPoll(uint port, uint device, uint input) -> int16 {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Program::inputRumble(uint port, uint device, uint input, bool enable) -> void {
|
auto Program::inputRumble(uint port, uint device, uint input, bool enable) -> void {
|
||||||
if(focused() || emulatorSettings.allowInput().checked() || !enable) {
|
if(focused() || inputSettings.allowInput().checked() || !enable) {
|
||||||
if(auto mapping = inputManager.mapping(port, device, input)) {
|
if(auto mapping = inputManager.mapping(port, device, input)) {
|
||||||
return mapping->rumble(enable);
|
return mapping->rumble(enable);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ auto Program::create() -> void {
|
||||||
inputSettings.create();
|
inputSettings.create();
|
||||||
hotkeySettings.create();
|
hotkeySettings.create();
|
||||||
pathSettings.create();
|
pathSettings.create();
|
||||||
|
speedSettings.create();
|
||||||
emulatorSettings.create();
|
emulatorSettings.create();
|
||||||
driverSettings.create();
|
driverSettings.create();
|
||||||
|
|
||||||
|
@ -65,9 +66,7 @@ auto Program::create() -> void {
|
||||||
driverSettings.inputDriverChanged();
|
driverSettings.inputDriverChanged();
|
||||||
|
|
||||||
if(gameQueue) load();
|
if(gameQueue) load();
|
||||||
if(presentation.startFullScreen && emulator->loaded()) {
|
if(startFullScreen && emulator->loaded()) {
|
||||||
//remove the earlier fullscreen mode state, so that toggleFullscreenMode will enter fullscreen exclusive mode
|
|
||||||
presentation.setFullScreen(false);
|
|
||||||
presentation.toggleFullscreenMode();
|
presentation.toggleFullscreenMode();
|
||||||
}
|
}
|
||||||
Application::onMain({&Program::main, this});
|
Application::onMain({&Program::main, this});
|
||||||
|
|
|
@ -125,7 +125,6 @@ struct Program : Lock, Emulator::Platform {
|
||||||
//hacks.cpp
|
//hacks.cpp
|
||||||
auto hackCompatibility() -> void;
|
auto hackCompatibility() -> void;
|
||||||
auto hackPatchMemory(vector<uint8_t>& data) -> void;
|
auto hackPatchMemory(vector<uint8_t>& data) -> void;
|
||||||
auto hackOverclockSuperFX() -> void;
|
|
||||||
|
|
||||||
//filter.cpp
|
//filter.cpp
|
||||||
auto filterSelect(uint& width, uint& height, uint scale) -> Filter::Render;
|
auto filterSelect(uint& width, uint& height, uint scale) -> Filter::Render;
|
||||||
|
@ -186,6 +185,8 @@ public:
|
||||||
uint64 statusTime;
|
uint64 statusTime;
|
||||||
string statusMessage;
|
string statusMessage;
|
||||||
string statusFrameRate;
|
string statusFrameRate;
|
||||||
|
|
||||||
|
bool startFullScreen = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Program program;
|
extern Program program;
|
||||||
|
|
|
@ -6,8 +6,8 @@ auto Program::rewindMode(Rewind::Mode mode) -> void {
|
||||||
auto Program::rewindReset() -> void {
|
auto Program::rewindReset() -> void {
|
||||||
rewindMode(Rewind::Mode::Playing);
|
rewindMode(Rewind::Mode::Playing);
|
||||||
rewind.history.reset();
|
rewind.history.reset();
|
||||||
rewind.frequency = settings.emulator.rewind.frequency;
|
rewind.frequency = settings.rewind.frequency;
|
||||||
rewind.length = settings.emulator.rewind.length;
|
rewind.length = settings.rewind.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Program::rewindRun() -> void {
|
auto Program::rewindRun() -> void {
|
||||||
|
|
|
@ -21,7 +21,7 @@ auto Program::updateStatus() -> void {
|
||||||
frameRate = tr("Unloaded");
|
frameRate = tr("Unloaded");
|
||||||
} else if(presentation.pauseEmulation.checked()) {
|
} else if(presentation.pauseEmulation.checked()) {
|
||||||
frameRate = tr("Paused");
|
frameRate = tr("Paused");
|
||||||
} else if(!focused() && emulatorSettings.pauseEmulation.checked()) {
|
} else if(!focused() && inputSettings.pauseEmulation.checked()) {
|
||||||
frameRate = tr("Paused");
|
frameRate = tr("Paused");
|
||||||
} else {
|
} else {
|
||||||
frameRate = statusFrameRate;
|
frameRate = statusFrameRate;
|
||||||
|
@ -66,7 +66,7 @@ auto Program::inactive() -> bool {
|
||||||
if(locked()) return true;
|
if(locked()) return true;
|
||||||
if(!emulator->loaded()) return true;
|
if(!emulator->loaded()) return true;
|
||||||
if(presentation.pauseEmulation.checked()) return true;
|
if(presentation.pauseEmulation.checked()) return true;
|
||||||
if(!focused() && emulatorSettings.pauseEmulation.checked()) return true;
|
if(!focused() && inputSettings.pauseEmulation.checked()) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
auto AudioSettings::create() -> void {
|
auto AudioSettings::create() -> void {
|
||||||
setIcon(Icon::Device::Speaker);
|
setCollapsible();
|
||||||
setText("Audio");
|
setVisible(false);
|
||||||
|
|
||||||
layout.setPadding(5_sx);
|
|
||||||
|
|
||||||
effectsLabel.setFont(Font().setBold()).setText("Effects");
|
effectsLabel.setFont(Font().setBold()).setText("Effects");
|
||||||
effectsLayout.setSize({3, 3});
|
effectsLayout.setSize({3, 3});
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
auto DriverSettings::create() -> void {
|
auto DriverSettings::create() -> void {
|
||||||
setIcon(Icon::Place::Settings);
|
setCollapsible();
|
||||||
setText("Drivers");
|
setVisible(false);
|
||||||
|
|
||||||
layout.setPadding(5_sx);
|
|
||||||
|
|
||||||
videoLabel.setText("Video").setFont(Font().setBold());
|
videoLabel.setText("Video").setFont(Font().setBold());
|
||||||
videoLayout.setSize({2, 2});
|
videoLayout.setSize({2, 2});
|
||||||
|
@ -113,7 +111,7 @@ auto DriverSettings::videoDriverChanged() -> void {
|
||||||
videoFormatChanged();
|
videoFormatChanged();
|
||||||
videoBlockingToggle.setChecked(video.blocking()).setEnabled(video.hasBlocking());
|
videoBlockingToggle.setChecked(video.blocking()).setEnabled(video.hasBlocking());
|
||||||
videoFlushToggle.setChecked(video.flush()).setEnabled(video.hasFlush());
|
videoFlushToggle.setChecked(video.flush()).setEnabled(video.hasFlush());
|
||||||
layout.setGeometry(layout.geometry());
|
setGeometry(geometry());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto DriverSettings::videoDriverChange() -> void {
|
auto DriverSettings::videoDriverChange() -> void {
|
||||||
|
@ -143,7 +141,7 @@ auto DriverSettings::videoFormatChanged() -> void {
|
||||||
if(format == video.format()) item.setSelected();
|
if(format == video.format()) item.setSelected();
|
||||||
}
|
}
|
||||||
//videoFormatOption.setEnabled(video.hasFormat());
|
//videoFormatOption.setEnabled(video.hasFormat());
|
||||||
layout.setGeometry(layout.geometry());
|
setGeometry(geometry());
|
||||||
videoFormatChange();
|
videoFormatChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +168,7 @@ auto DriverSettings::audioDriverChanged() -> void {
|
||||||
audioExclusiveToggle.setChecked(audio.exclusive()).setEnabled(audio.hasExclusive());
|
audioExclusiveToggle.setChecked(audio.exclusive()).setEnabled(audio.hasExclusive());
|
||||||
audioBlockingToggle.setChecked(audio.blocking()).setEnabled(audio.hasBlocking());
|
audioBlockingToggle.setChecked(audio.blocking()).setEnabled(audio.hasBlocking());
|
||||||
audioDynamicToggle.setChecked(audio.dynamic()).setEnabled(audio.hasDynamic());
|
audioDynamicToggle.setChecked(audio.dynamic()).setEnabled(audio.hasDynamic());
|
||||||
layout.setGeometry(layout.geometry());
|
setGeometry(geometry());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto DriverSettings::audioDriverChange() -> void {
|
auto DriverSettings::audioDriverChange() -> void {
|
||||||
|
@ -200,7 +198,7 @@ auto DriverSettings::audioDeviceChanged() -> void {
|
||||||
if(device == audio.device()) item.setSelected();
|
if(device == audio.device()) item.setSelected();
|
||||||
}
|
}
|
||||||
//audioDeviceOption.setEnabled(audio->hasDevice());
|
//audioDeviceOption.setEnabled(audio->hasDevice());
|
||||||
layout.setGeometry(layout.geometry());
|
setGeometry(geometry());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto DriverSettings::audioDeviceChange() -> void {
|
auto DriverSettings::audioDeviceChange() -> void {
|
||||||
|
@ -219,7 +217,7 @@ auto DriverSettings::audioFrequencyChanged() -> void {
|
||||||
if(frequency == audio.frequency()) item.setSelected();
|
if(frequency == audio.frequency()) item.setSelected();
|
||||||
}
|
}
|
||||||
//audioFrequencyOption.setEnabled(audio->hasFrequency());
|
//audioFrequencyOption.setEnabled(audio->hasFrequency());
|
||||||
layout.setGeometry(layout.geometry());
|
setGeometry(geometry());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto DriverSettings::audioFrequencyChange() -> void {
|
auto DriverSettings::audioFrequencyChange() -> void {
|
||||||
|
@ -236,7 +234,7 @@ auto DriverSettings::audioLatencyChanged() -> void {
|
||||||
if(latency == audio.latency()) item.setSelected();
|
if(latency == audio.latency()) item.setSelected();
|
||||||
}
|
}
|
||||||
//audioLatencyOption.setEnabled(audio->hasLatency());
|
//audioLatencyOption.setEnabled(audio->hasLatency());
|
||||||
layout.setGeometry(layout.geometry());
|
setGeometry(geometry());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto DriverSettings::audioLatencyChange() -> void {
|
auto DriverSettings::audioLatencyChange() -> void {
|
||||||
|
@ -256,7 +254,7 @@ auto DriverSettings::inputDriverChanged() -> void {
|
||||||
}
|
}
|
||||||
inputDriverActive.setText({"Active driver: ", input.driver()});
|
inputDriverActive.setText({"Active driver: ", input.driver()});
|
||||||
inputDriverOption.doChange();
|
inputDriverOption.doChange();
|
||||||
layout.setGeometry(layout.geometry());
|
setGeometry(geometry());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto DriverSettings::inputDriverChange() -> void {
|
auto DriverSettings::inputDriverChange() -> void {
|
||||||
|
|
|
@ -1,23 +1,8 @@
|
||||||
auto EmulatorSettings::create() -> void {
|
auto EmulatorSettings::create() -> void {
|
||||||
setIcon(Icon::Action::Settings);
|
setCollapsible();
|
||||||
setText("Emulator");
|
setVisible(false);
|
||||||
|
|
||||||
layout.setPadding(5_sx);
|
|
||||||
|
|
||||||
optionsLabel.setText("Options").setFont(Font().setBold());
|
optionsLabel.setText("Options").setFont(Font().setBold());
|
||||||
inputFocusLabel.setText("When focus is lost:");
|
|
||||||
pauseEmulation.setText("Pause emulation").onActivate([&] {
|
|
||||||
settings.input.defocus = "Pause";
|
|
||||||
});
|
|
||||||
blockInput.setText("Block input").onActivate([&] {
|
|
||||||
settings.input.defocus = "Block";
|
|
||||||
});
|
|
||||||
allowInput.setText("Allow input").onActivate([&] {
|
|
||||||
settings.input.defocus = "Allow";
|
|
||||||
});
|
|
||||||
if(settings.input.defocus == "Pause") pauseEmulation.setChecked();
|
|
||||||
if(settings.input.defocus == "Block") blockInput.setChecked();
|
|
||||||
if(settings.input.defocus == "Allow") allowInput.setChecked();
|
|
||||||
warnOnUnverifiedGames.setText("Warn when loading games that have not been verified").setChecked(settings.emulator.warnOnUnverifiedGames).onToggle([&] {
|
warnOnUnverifiedGames.setText("Warn when loading games that have not been verified").setChecked(settings.emulator.warnOnUnverifiedGames).onToggle([&] {
|
||||||
settings.emulator.warnOnUnverifiedGames = warnOnUnverifiedGames.checked();
|
settings.emulator.warnOnUnverifiedGames = warnOnUnverifiedGames.checked();
|
||||||
});
|
});
|
||||||
|
@ -35,42 +20,6 @@ auto EmulatorSettings::create() -> void {
|
||||||
autoLoadStateOnLoad.setText("Auto-resume on load").setChecked(settings.emulator.autoLoadStateOnLoad).onToggle([&] {
|
autoLoadStateOnLoad.setText("Auto-resume on load").setChecked(settings.emulator.autoLoadStateOnLoad).onToggle([&] {
|
||||||
settings.emulator.autoLoadStateOnLoad = autoLoadStateOnLoad.checked();
|
settings.emulator.autoLoadStateOnLoad = autoLoadStateOnLoad.checked();
|
||||||
});
|
});
|
||||||
rewindFrequencyLabel.setText("Rewind Frequency:");
|
|
||||||
rewindFrequencyOption.append(ComboButtonItem().setText("Disabled"));
|
|
||||||
rewindFrequencyOption.append(ComboButtonItem().setText("Every 10 frames"));
|
|
||||||
rewindFrequencyOption.append(ComboButtonItem().setText("Every 20 frames"));
|
|
||||||
rewindFrequencyOption.append(ComboButtonItem().setText("Every 30 frames"));
|
|
||||||
rewindFrequencyOption.append(ComboButtonItem().setText("Every 40 frames"));
|
|
||||||
rewindFrequencyOption.append(ComboButtonItem().setText("Every 50 frames"));
|
|
||||||
rewindFrequencyOption.append(ComboButtonItem().setText("Every 60 frames"));
|
|
||||||
if(settings.emulator.rewind.frequency == 0) rewindFrequencyOption.item(0).setSelected();
|
|
||||||
if(settings.emulator.rewind.frequency == 10) rewindFrequencyOption.item(1).setSelected();
|
|
||||||
if(settings.emulator.rewind.frequency == 20) rewindFrequencyOption.item(2).setSelected();
|
|
||||||
if(settings.emulator.rewind.frequency == 30) rewindFrequencyOption.item(3).setSelected();
|
|
||||||
if(settings.emulator.rewind.frequency == 40) rewindFrequencyOption.item(4).setSelected();
|
|
||||||
if(settings.emulator.rewind.frequency == 50) rewindFrequencyOption.item(5).setSelected();
|
|
||||||
if(settings.emulator.rewind.frequency == 60) rewindFrequencyOption.item(6).setSelected();
|
|
||||||
rewindFrequencyOption.onChange([&] {
|
|
||||||
settings.emulator.rewind.frequency = rewindFrequencyOption.selected().offset() * 10;
|
|
||||||
program.rewindReset();
|
|
||||||
});
|
|
||||||
rewindLengthLabel.setText("Rewind Length:");
|
|
||||||
rewindLengthOption.append(ComboButtonItem().setText( "10 states"));
|
|
||||||
rewindLengthOption.append(ComboButtonItem().setText( "20 states"));
|
|
||||||
rewindLengthOption.append(ComboButtonItem().setText( "40 states"));
|
|
||||||
rewindLengthOption.append(ComboButtonItem().setText( "80 states"));
|
|
||||||
rewindLengthOption.append(ComboButtonItem().setText("160 states"));
|
|
||||||
rewindLengthOption.append(ComboButtonItem().setText("320 states"));
|
|
||||||
if(settings.emulator.rewind.length == 10) rewindLengthOption.item(0).setSelected();
|
|
||||||
if(settings.emulator.rewind.length == 20) rewindLengthOption.item(1).setSelected();
|
|
||||||
if(settings.emulator.rewind.length == 40) rewindLengthOption.item(2).setSelected();
|
|
||||||
if(settings.emulator.rewind.length == 80) rewindLengthOption.item(3).setSelected();
|
|
||||||
if(settings.emulator.rewind.length == 160) rewindLengthOption.item(4).setSelected();
|
|
||||||
if(settings.emulator.rewind.length == 320) rewindLengthOption.item(5).setSelected();
|
|
||||||
rewindLengthOption.onChange([&] {
|
|
||||||
settings.emulator.rewind.length = 10 << rewindLengthOption.selected().offset();
|
|
||||||
program.rewindReset();
|
|
||||||
});
|
|
||||||
optionsSpacer.setColor({192, 192, 192});
|
optionsSpacer.setColor({192, 192, 192});
|
||||||
|
|
||||||
ppuLabel.setText("PPU (video)").setFont(Font().setBold());
|
ppuLabel.setText("PPU (video)").setFont(Font().setBold());
|
||||||
|
@ -131,24 +80,5 @@ auto EmulatorSettings::create() -> void {
|
||||||
coprocessorsHLEOption.setText("Prefer HLE").setChecked(settings.emulator.hack.coprocessors.hle).onToggle([&] {
|
coprocessorsHLEOption.setText("Prefer HLE").setChecked(settings.emulator.hack.coprocessors.hle).onToggle([&] {
|
||||||
settings.emulator.hack.coprocessors.hle = coprocessorsHLEOption.checked();
|
settings.emulator.hack.coprocessors.hle = coprocessorsHLEOption.checked();
|
||||||
});
|
});
|
||||||
superFXLabel.setText("SuperFX clock speed:");
|
|
||||||
superFXValue.setAlignment(0.5);
|
|
||||||
superFXClock.setLength(71).setPosition((settings.emulator.hack.fastSuperFX - 100) / 10).onChange([&] {
|
|
||||||
settings.emulator.hack.fastSuperFX = superFXClock.position() * 10 + 100;
|
|
||||||
superFXValue.setText({settings.emulator.hack.fastSuperFX, "%"});
|
|
||||||
}).doChange();
|
|
||||||
hacksNote.setText("Note: some hack setting changes do not take effect until after reloading games.");
|
hacksNote.setText("Note: some hack setting changes do not take effect until after reloading games.");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto EmulatorSettings::updateConfiguration() -> void {
|
|
||||||
emulator->configure("Hacks/PPU/Fast", fastPPU.checked());
|
|
||||||
emulator->configure("Hacks/PPU/NoSpriteLimit", noSpriteLimit.checked());
|
|
||||||
emulator->configure("Hacks/PPU/Mode7/Scale", mode7Scale.selected().property("multiplier").natural());
|
|
||||||
emulator->configure("Hacks/PPU/Mode7/Perspective", mode7Perspective.checked());
|
|
||||||
emulator->configure("Hacks/PPU/Mode7/Supersample", mode7Supersample.checked());
|
|
||||||
emulator->configure("Hacks/PPU/Mode7/Mosaic", mode7Mosaic.checked());
|
|
||||||
emulator->configure("Hacks/DSP/Fast", fastDSP.checked());
|
|
||||||
emulator->configure("Hacks/DSP/Cubic", cubicInterpolation.checked());
|
|
||||||
emulator->configure("Hacks/Coprocessor/DelayedSync", coprocessorsDelayedSyncOption.checked());
|
|
||||||
emulator->configure("Hacks/Coprocessor/HLE", coprocessorsHLEOption.checked());
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
auto HotkeySettings::create() -> void {
|
auto HotkeySettings::create() -> void {
|
||||||
setIcon(Icon::Device::Keyboard);
|
setCollapsible();
|
||||||
setText("Hotkeys");
|
setVisible(false);
|
||||||
|
|
||||||
layout.setPadding(5_sx);
|
|
||||||
mappingList.setBatchable();
|
mappingList.setBatchable();
|
||||||
mappingList.setHeadered();
|
mappingList.setHeadered();
|
||||||
mappingList.onActivate([&] {
|
mappingList.onActivate([&](auto cell) { assignMapping(cell); });
|
||||||
if(assignButton.enabled()) assignButton.doActivate();
|
|
||||||
});
|
|
||||||
mappingList.onChange([&] {
|
mappingList.onChange([&] {
|
||||||
auto batched = mappingList.batched();
|
auto batched = mappingList.batched();
|
||||||
assignButton.setEnabled(batched.size() == 1);
|
assignButton.setEnabled(batched.size() == 1);
|
||||||
|
@ -16,12 +13,33 @@ auto HotkeySettings::create() -> void {
|
||||||
mappingList.onSize([&] {
|
mappingList.onSize([&] {
|
||||||
mappingList.resizeColumns();
|
mappingList.resizeColumns();
|
||||||
});
|
});
|
||||||
|
logicLabel.setText("Combinational logic:").setToolTip(
|
||||||
|
"Determines whether all or any mappings must be pressed to activate hotkeys.\n"
|
||||||
|
"Use 'and' logic if you want keyboard combinations such as Ctrl+F to trigger a hotkey.\n"
|
||||||
|
"Use 'or' logic if you want both a keyboard and joypad to trigger the same hotkey."
|
||||||
|
);
|
||||||
|
logicAND.setText("And").setToolTip("Every mapping must be pressed to activate a given hotkey.").onActivate([&] {
|
||||||
|
settings.input.hotkey.logic = "and";
|
||||||
|
inputManager.hotkeyLogic = InputMapping::Logic::AND;
|
||||||
|
});
|
||||||
|
logicOR.setText("Or").setToolTip("Any mapping can be pressed to activate a given hotkey.").onActivate([&] {
|
||||||
|
settings.input.hotkey.logic = "or";
|
||||||
|
inputManager.hotkeyLogic = InputMapping::Logic::OR;
|
||||||
|
});
|
||||||
|
if(settings.input.hotkey.logic == "and") logicAND.setChecked().doActivate();
|
||||||
|
if(settings.input.hotkey.logic == "or") logicOR.setChecked().doActivate();
|
||||||
|
inputSink.setFocusable();
|
||||||
assignButton.setText("Assign").onActivate([&] {
|
assignButton.setText("Assign").onActivate([&] {
|
||||||
assignMapping();
|
clearButton.doActivate();
|
||||||
|
assignMapping(mappingList.selected().cell(0));
|
||||||
});
|
});
|
||||||
clearButton.setText("Clear").onActivate([&] {
|
clearButton.setText("Clear").onActivate([&] {
|
||||||
for(auto item : mappingList.batched()) {
|
cancelMapping();
|
||||||
inputManager.hotkeys[item.offset()].unbind();
|
for(auto mapping : mappingList.batched()) {
|
||||||
|
auto& hotkey = inputManager.hotkeys[mapping.offset()];
|
||||||
|
for(uint index : range(BindingLimit)) {
|
||||||
|
hotkey.unbind(index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
refreshMappings();
|
refreshMappings();
|
||||||
});
|
});
|
||||||
|
@ -30,49 +48,60 @@ auto HotkeySettings::create() -> void {
|
||||||
auto HotkeySettings::reloadMappings() -> void {
|
auto HotkeySettings::reloadMappings() -> void {
|
||||||
mappingList.reset();
|
mappingList.reset();
|
||||||
mappingList.append(TableViewColumn().setText("Name"));
|
mappingList.append(TableViewColumn().setText("Name"));
|
||||||
mappingList.append(TableViewColumn().setText("Mapping").setExpandable());
|
for(uint index : range(BindingLimit)) {
|
||||||
|
mappingList.append(TableViewColumn().setText({"Mapping #", 1 + index}).setExpandable());
|
||||||
|
}
|
||||||
for(auto& hotkey : inputManager.hotkeys) {
|
for(auto& hotkey : inputManager.hotkeys) {
|
||||||
mappingList.append(TableViewItem()
|
TableViewItem item{&mappingList};
|
||||||
.append(TableViewCell().setText(hotkey.name).setFont(Font().setBold()))
|
item.append(TableViewCell().setText(hotkey.name).setFont(Font().setBold()));
|
||||||
.append(TableViewCell())
|
for(uint index : range(BindingLimit)) item.append(TableViewCell());
|
||||||
);
|
|
||||||
}
|
}
|
||||||
refreshMappings();
|
refreshMappings();
|
||||||
mappingList.doChange();
|
mappingList.doChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto HotkeySettings::refreshMappings() -> void {
|
auto HotkeySettings::refreshMappings() -> void {
|
||||||
uint index = 0;
|
uint item = 0;
|
||||||
for(auto& hotkey : inputManager.hotkeys) {
|
for(auto& hotkey : inputManager.hotkeys) {
|
||||||
mappingList.item(index++).cell(1).setText(hotkey.displayName());
|
for(uint index : range(BindingLimit)) {
|
||||||
|
auto cell = mappingList.item(item).cell(1 + index);
|
||||||
|
auto& binding = hotkey.bindings[index];
|
||||||
|
cell.setIcon(binding.icon());
|
||||||
|
cell.setText(binding.name());
|
||||||
}
|
}
|
||||||
|
item++;
|
||||||
|
}
|
||||||
|
Application::processEvents();
|
||||||
mappingList.resizeColumns();
|
mappingList.resizeColumns();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto HotkeySettings::assignMapping() -> void {
|
auto HotkeySettings::assignMapping(TableViewCell cell) -> void {
|
||||||
inputManager.poll(); //clear any pending events first
|
inputManager.poll(); //clear any pending events first
|
||||||
|
|
||||||
if(auto item = mappingList.selected()) {
|
for(auto mapping : mappingList.batched()) {
|
||||||
activeMapping = inputManager.hotkeys[item.offset()];
|
activeMapping = inputManager.hotkeys[mapping.offset()];
|
||||||
settingsWindow.layout.setEnabled(false);
|
activeBinding = max(0, (int)cell.offset() - 1);
|
||||||
settingsWindow.statusBar.setText({"Press a key or button to map [", activeMapping->name, "] ..."});
|
mappingList.item(mapping.offset()).cell(1 + activeBinding).setIcon(Icon::Go::Right).setText("(assign ...)");
|
||||||
|
settingsWindow.statusBar.setText({"Press a key or button for mapping# ", 1 + activeBinding, " [", activeMapping->name, "] ..."});
|
||||||
settingsWindow.setDismissable(false);
|
settingsWindow.setDismissable(false);
|
||||||
|
inputSink.setFocused();
|
||||||
|
return; //map only one input at a time
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto HotkeySettings::cancelMapping() -> void {
|
auto HotkeySettings::cancelMapping() -> void {
|
||||||
activeMapping.reset();
|
activeMapping.reset();
|
||||||
settingsWindow.statusBar.setText();
|
settingsWindow.statusBar.setText();
|
||||||
settingsWindow.layout.setEnabled();
|
|
||||||
settingsWindow.doSize();
|
settingsWindow.doSize();
|
||||||
settingsWindow.setDismissable(true);
|
settingsWindow.setDismissable(true);
|
||||||
|
mappingList.setFocused();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto HotkeySettings::inputEvent(shared_pointer<HID::Device> device, uint group, uint input, int16 oldValue, int16 newValue) -> void {
|
auto HotkeySettings::inputEvent(shared_pointer<HID::Device> device, uint group, uint input, int16 oldValue, int16 newValue) -> void {
|
||||||
if(!activeMapping) return;
|
if(!activeMapping) return;
|
||||||
if(device->isMouse()) return;
|
if(device->isMouse()) return;
|
||||||
|
|
||||||
if(activeMapping->bind(device, group, input, oldValue, newValue)) {
|
if(activeMapping->bind(device, group, input, oldValue, newValue, activeBinding)) {
|
||||||
activeMapping.reset();
|
activeMapping.reset();
|
||||||
settingsWindow.statusBar.setText("Mapping assigned.");
|
settingsWindow.statusBar.setText("Mapping assigned.");
|
||||||
refreshMappings();
|
refreshMappings();
|
||||||
|
|
|
@ -1,8 +1,22 @@
|
||||||
auto InputSettings::create() -> void {
|
auto InputSettings::create() -> void {
|
||||||
setIcon(Icon::Device::Joypad);
|
setCollapsible();
|
||||||
setText("Input");
|
setVisible(false);
|
||||||
|
|
||||||
|
inputFocusLabel.setText("When focus is lost:");
|
||||||
|
pauseEmulation.setText("Pause emulation").onActivate([&] {
|
||||||
|
settings.input.defocus = "Pause";
|
||||||
|
});
|
||||||
|
blockInput.setText("Block input").onActivate([&] {
|
||||||
|
settings.input.defocus = "Block";
|
||||||
|
});
|
||||||
|
allowInput.setText("Allow input").onActivate([&] {
|
||||||
|
settings.input.defocus = "Allow";
|
||||||
|
});
|
||||||
|
if(settings.input.defocus == "Pause") pauseEmulation.setChecked();
|
||||||
|
if(settings.input.defocus == "Block") blockInput.setChecked();
|
||||||
|
if(settings.input.defocus == "Allow") allowInput.setChecked();
|
||||||
|
separator.setColor({192, 192, 192});
|
||||||
|
|
||||||
layout.setPadding(5_sx);
|
|
||||||
portLabel.setText("Port:");
|
portLabel.setText("Port:");
|
||||||
portList.onChange([&] { reloadDevices(); });
|
portList.onChange([&] { reloadDevices(); });
|
||||||
deviceLabel.setText("Device:");
|
deviceLabel.setText("Device:");
|
||||||
|
@ -23,18 +37,23 @@ auto InputSettings::create() -> void {
|
||||||
});
|
});
|
||||||
mappingList.setBatchable();
|
mappingList.setBatchable();
|
||||||
mappingList.setHeadered();
|
mappingList.setHeadered();
|
||||||
mappingList.onActivate([&] { if(assignButton.enabled()) assignButton.doActivate(); });
|
mappingList.onActivate([&](auto cell) { assignMapping(cell); });
|
||||||
mappingList.onChange([&] { updateControls(); });
|
mappingList.onChange([&] { updateControls(); });
|
||||||
mappingList.onSize([&] { mappingList.resizeColumns(); });
|
mappingList.onSize([&] { mappingList.resizeColumns(); });
|
||||||
|
inputSink.setFocusable();
|
||||||
assignMouse1.onActivate([&] { assignMouseInput(0); });
|
assignMouse1.onActivate([&] { assignMouseInput(0); });
|
||||||
assignMouse2.onActivate([&] { assignMouseInput(1); });
|
assignMouse2.onActivate([&] { assignMouseInput(1); });
|
||||||
assignMouse3.onActivate([&] { assignMouseInput(2); });
|
assignMouse3.onActivate([&] { assignMouseInput(2); });
|
||||||
assignButton.setText("Assign").onActivate([&] {
|
assignButton.setText("Assign").onActivate([&] {
|
||||||
assignMapping();
|
clearButton.doActivate();
|
||||||
|
assignMapping(mappingList.selected().cell(0));
|
||||||
});
|
});
|
||||||
clearButton.setText("Clear").onActivate([&] {
|
clearButton.setText("Clear").onActivate([&] {
|
||||||
|
cancelMapping();
|
||||||
for(auto mapping : mappingList.batched()) {
|
for(auto mapping : mappingList.batched()) {
|
||||||
activeDevice().mappings[mapping.offset()].unbind();
|
for(uint index : range(BindingLimit)) {
|
||||||
|
activeDevice().mappings[mapping.offset()].unbind(index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
refreshMappings();
|
refreshMappings();
|
||||||
});
|
});
|
||||||
|
@ -48,7 +67,7 @@ auto InputSettings::updateControls() -> void {
|
||||||
assignMouse2.setVisible(false);
|
assignMouse2.setVisible(false);
|
||||||
assignMouse3.setVisible(false);
|
assignMouse3.setVisible(false);
|
||||||
|
|
||||||
if(batched.size() == 1) {
|
if(activeMapping) {
|
||||||
auto& input = activeDevice().mappings[batched.left().offset()];
|
auto& input = activeDevice().mappings[batched.left().offset()];
|
||||||
if(input.isDigital()) {
|
if(input.isDigital()) {
|
||||||
assignMouse1.setVisible().setText("Mouse Left");
|
assignMouse1.setVisible().setText("Mouse Left");
|
||||||
|
@ -59,6 +78,8 @@ auto InputSettings::updateControls() -> void {
|
||||||
assignMouse2.setVisible().setText("Mouse Y-axis");
|
assignMouse2.setVisible().setText("Mouse Y-axis");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
controlLayout.resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto InputSettings::activePort() -> InputPort& {
|
auto InputSettings::activePort() -> InputPort& {
|
||||||
|
@ -94,48 +115,60 @@ auto InputSettings::reloadDevices() -> void {
|
||||||
auto InputSettings::reloadMappings() -> void {
|
auto InputSettings::reloadMappings() -> void {
|
||||||
mappingList.reset();
|
mappingList.reset();
|
||||||
mappingList.append(TableViewColumn().setText("Name"));
|
mappingList.append(TableViewColumn().setText("Name"));
|
||||||
mappingList.append(TableViewColumn().setText("Mapping").setExpandable());
|
for(uint n : range(BindingLimit)) {
|
||||||
|
mappingList.append(TableViewColumn().setText({"Mapping #", 1 + n}).setExpandable());
|
||||||
|
}
|
||||||
for(auto& mapping : activeDevice().mappings) {
|
for(auto& mapping : activeDevice().mappings) {
|
||||||
mappingList.append(TableViewItem()
|
TableViewItem item{&mappingList};
|
||||||
.append(TableViewCell().setText(mapping.name).setFont(Font().setBold()))
|
item.append(TableViewCell().setText(mapping.name).setFont(Font().setBold()));
|
||||||
.append(TableViewCell())
|
for(uint n : range(BindingLimit)) item.append(TableViewCell());
|
||||||
);
|
|
||||||
}
|
}
|
||||||
refreshMappings();
|
refreshMappings();
|
||||||
updateControls();
|
updateControls();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto InputSettings::refreshMappings() -> void {
|
auto InputSettings::refreshMappings() -> void {
|
||||||
uint index = 0;
|
uint item = 0;
|
||||||
for(auto& mapping : activeDevice().mappings) {
|
for(auto& mapping : activeDevice().mappings) {
|
||||||
mappingList.item(index++).cell(1).setText(mapping.displayName());
|
for(uint index : range(BindingLimit)) {
|
||||||
|
auto cell = mappingList.item(item).cell(1 + index);
|
||||||
|
auto& binding = mapping.bindings[index];
|
||||||
|
cell.setIcon(binding.icon());
|
||||||
|
cell.setText(binding.name());
|
||||||
|
}
|
||||||
|
item++;
|
||||||
}
|
}
|
||||||
Application::processEvents();
|
Application::processEvents();
|
||||||
mappingList.resizeColumns();
|
mappingList.resizeColumns();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto InputSettings::assignMapping() -> void {
|
auto InputSettings::assignMapping(TableViewCell cell) -> void {
|
||||||
inputManager.poll(); //clear any pending events first
|
inputManager.poll(); //clear any pending events first
|
||||||
|
|
||||||
for(auto mapping : mappingList.batched()) {
|
for(auto mapping : mappingList.batched()) {
|
||||||
activeMapping = activeDevice().mappings[mapping.offset()];
|
activeMapping = activeDevice().mappings[mapping.offset()];
|
||||||
settingsWindow.layout.setEnabled(false);
|
activeBinding = max(0, (int)cell.offset() - 1);
|
||||||
settingsWindow.statusBar.setText({"Press a key or button to map [", activeMapping->name, "] ..."});
|
mappingList.item(mapping.offset()).cell(1 + activeBinding).setIcon(Icon::Go::Right).setText("(assign ...)");
|
||||||
|
settingsWindow.statusBar.setText({"Press a key or button for mapping #", 1 + activeBinding, " [", activeMapping->name, "] ..."});
|
||||||
settingsWindow.setDismissable(false);
|
settingsWindow.setDismissable(false);
|
||||||
|
updateControls();
|
||||||
|
Application::processEvents();
|
||||||
|
inputSink.setFocused();
|
||||||
|
return; //map only one input at a time
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto InputSettings::cancelMapping() -> void {
|
auto InputSettings::cancelMapping() -> void {
|
||||||
activeMapping.reset();
|
activeMapping.reset();
|
||||||
settingsWindow.statusBar.setText();
|
settingsWindow.statusBar.setText();
|
||||||
settingsWindow.layout.setEnabled();
|
|
||||||
settingsWindow.doSize();
|
settingsWindow.doSize();
|
||||||
settingsWindow.setDismissable(true);
|
settingsWindow.setDismissable(true);
|
||||||
|
mappingList.setFocused();
|
||||||
|
updateControls();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto InputSettings::assignMouseInput(uint id) -> void {
|
auto InputSettings::assignMouseInput(uint id) -> void {
|
||||||
if(auto mapping = mappingList.selected()) {
|
if(!activeMapping) return;
|
||||||
activeMapping = activeDevice().mappings[mapping.offset()];
|
|
||||||
if(auto mouse = inputManager.findMouse()) {
|
if(auto mouse = inputManager.findMouse()) {
|
||||||
if(activeMapping->isDigital()) {
|
if(activeMapping->isDigital()) {
|
||||||
return inputEvent(mouse, HID::Mouse::GroupID::Button, id, 0, 1, true);
|
return inputEvent(mouse, HID::Mouse::GroupID::Button, id, 0, 1, true);
|
||||||
|
@ -144,13 +177,12 @@ auto InputSettings::assignMouseInput(uint id) -> void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
auto InputSettings::inputEvent(shared_pointer<HID::Device> device, uint group, uint input, int16 oldValue, int16 newValue, bool allowMouseInput) -> void {
|
auto InputSettings::inputEvent(shared_pointer<HID::Device> device, uint group, uint input, int16 oldValue, int16 newValue, bool allowMouseInput) -> void {
|
||||||
if(!activeMapping) return;
|
if(!activeMapping) return;
|
||||||
if(device->isMouse() && !allowMouseInput) return;
|
if(device->isMouse() && !allowMouseInput) return;
|
||||||
|
|
||||||
if(activeMapping->bind(device, group, input, oldValue, newValue)) {
|
if(activeMapping->bind(device, group, input, oldValue, newValue, activeBinding)) {
|
||||||
activeMapping.reset();
|
activeMapping.reset();
|
||||||
settingsWindow.statusBar.setText("Mapping assigned.");
|
settingsWindow.statusBar.setText("Mapping assigned.");
|
||||||
refreshMappings();
|
refreshMappings();
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
auto PathSettings::create() -> void {
|
auto PathSettings::create() -> void {
|
||||||
setIcon(Icon::Emblem::Folder);
|
setCollapsible();
|
||||||
setText("Paths");
|
setVisible(false);
|
||||||
|
|
||||||
layout.setPadding(5_sx);
|
|
||||||
layout.setSize({4, 6});
|
layout.setSize({4, 6});
|
||||||
layout.column(0).setAlignment(1.0);
|
layout.column(0).setAlignment(1.0);
|
||||||
|
|
||||||
gamesLabel.setText("Games:");
|
gamesLabel.setText("Games:");
|
||||||
gamesPath.setEditable(false);
|
gamesPath.setEditable(false);
|
||||||
gamesAssign.setText("Assign ...").onActivate([&] {
|
gamesAssign.setText("Assign ...").onActivate([&] {
|
||||||
if(auto location = BrowserDialog().setAlignment(*settingsWindow).selectFolder()) {
|
if(auto location = BrowserDialog().selectFolder()) {
|
||||||
settings.path.games = location;
|
settings.path.games = location;
|
||||||
refreshPaths();
|
refreshPaths();
|
||||||
}
|
}
|
||||||
|
@ -22,7 +21,7 @@ auto PathSettings::create() -> void {
|
||||||
patchesLabel.setText("Patches:");
|
patchesLabel.setText("Patches:");
|
||||||
patchesPath.setEditable(false);
|
patchesPath.setEditable(false);
|
||||||
patchesAssign.setText("Assign ...").onActivate([&] {
|
patchesAssign.setText("Assign ...").onActivate([&] {
|
||||||
if(auto location = BrowserDialog().setAlignment(*settingsWindow).selectFolder()) {
|
if(auto location = BrowserDialog().selectFolder()) {
|
||||||
settings.path.patches = location;
|
settings.path.patches = location;
|
||||||
refreshPaths();
|
refreshPaths();
|
||||||
}
|
}
|
||||||
|
@ -35,7 +34,7 @@ auto PathSettings::create() -> void {
|
||||||
savesLabel.setText("Saves:");
|
savesLabel.setText("Saves:");
|
||||||
savesPath.setEditable(false);
|
savesPath.setEditable(false);
|
||||||
savesAssign.setText("Assign ...").onActivate([&] {
|
savesAssign.setText("Assign ...").onActivate([&] {
|
||||||
if(auto location = BrowserDialog().setAlignment(*settingsWindow).selectFolder()) {
|
if(auto location = BrowserDialog().selectFolder()) {
|
||||||
settings.path.saves = location;
|
settings.path.saves = location;
|
||||||
refreshPaths();
|
refreshPaths();
|
||||||
}
|
}
|
||||||
|
@ -48,7 +47,7 @@ auto PathSettings::create() -> void {
|
||||||
cheatsLabel.setText("Cheats:");
|
cheatsLabel.setText("Cheats:");
|
||||||
cheatsPath.setEditable(false);
|
cheatsPath.setEditable(false);
|
||||||
cheatsAssign.setText("Assign ...").onActivate([&] {
|
cheatsAssign.setText("Assign ...").onActivate([&] {
|
||||||
if(auto location = BrowserDialog().setAlignment(*settingsWindow).selectFolder()) {
|
if(auto location = BrowserDialog().selectFolder()) {
|
||||||
settings.path.cheats = location;
|
settings.path.cheats = location;
|
||||||
refreshPaths();
|
refreshPaths();
|
||||||
}
|
}
|
||||||
|
@ -61,7 +60,7 @@ auto PathSettings::create() -> void {
|
||||||
statesLabel.setText("States:");
|
statesLabel.setText("States:");
|
||||||
statesPath.setEditable(false);
|
statesPath.setEditable(false);
|
||||||
statesAssign.setText("Assign ...").onActivate([&] {
|
statesAssign.setText("Assign ...").onActivate([&] {
|
||||||
if(auto location = BrowserDialog().setAlignment(*settingsWindow).selectFolder()) {
|
if(auto location = BrowserDialog().selectFolder()) {
|
||||||
settings.path.states = location;
|
settings.path.states = location;
|
||||||
refreshPaths();
|
refreshPaths();
|
||||||
}
|
}
|
||||||
|
@ -74,7 +73,7 @@ auto PathSettings::create() -> void {
|
||||||
screenshotsLabel.setText("Screenshots:");
|
screenshotsLabel.setText("Screenshots:");
|
||||||
screenshotsPath.setEditable(false);
|
screenshotsPath.setEditable(false);
|
||||||
screenshotsAssign.setText("Assign ...").onActivate([&] {
|
screenshotsAssign.setText("Assign ...").onActivate([&] {
|
||||||
if(auto location = BrowserDialog().setAlignment(*settingsWindow).selectFolder()) {
|
if(auto location = BrowserDialog().selectFolder()) {
|
||||||
settings.path.screenshots = location;
|
settings.path.screenshots = location;
|
||||||
refreshPaths();
|
refreshPaths();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "input.cpp"
|
#include "input.cpp"
|
||||||
#include "hotkeys.cpp"
|
#include "hotkeys.cpp"
|
||||||
#include "paths.cpp"
|
#include "paths.cpp"
|
||||||
|
#include "speed.cpp"
|
||||||
#include "emulator.cpp"
|
#include "emulator.cpp"
|
||||||
#include "drivers.cpp"
|
#include "drivers.cpp"
|
||||||
Settings settings;
|
Settings settings;
|
||||||
|
@ -12,6 +13,7 @@ AudioSettings audioSettings;
|
||||||
InputSettings inputSettings;
|
InputSettings inputSettings;
|
||||||
HotkeySettings hotkeySettings;
|
HotkeySettings hotkeySettings;
|
||||||
PathSettings pathSettings;
|
PathSettings pathSettings;
|
||||||
|
SpeedSettings speedSettings;
|
||||||
EmulatorSettings emulatorSettings;
|
EmulatorSettings emulatorSettings;
|
||||||
DriverSettings driverSettings;
|
DriverSettings driverSettings;
|
||||||
namespace Instances { Instance<SettingsWindow> settingsWindow; }
|
namespace Instances { Instance<SettingsWindow> settingsWindow; }
|
||||||
|
@ -31,9 +33,9 @@ auto Settings::save() -> void {
|
||||||
auto Settings::process(bool load) -> void {
|
auto Settings::process(bool load) -> void {
|
||||||
if(load) {
|
if(load) {
|
||||||
//initialize non-static default settings
|
//initialize non-static default settings
|
||||||
video.driver = ruby::Video::safestDriver();
|
video.driver = ruby::Video::optimalDriver();
|
||||||
audio.driver = ruby::Audio::safestDriver();
|
audio.driver = ruby::Audio::optimalDriver();
|
||||||
input.driver = ruby::Input::safestDriver();
|
input.driver = ruby::Input::optimalDriver();
|
||||||
}
|
}
|
||||||
|
|
||||||
#define bind(type, path, name) \
|
#define bind(type, path, name) \
|
||||||
|
@ -52,7 +54,6 @@ auto Settings::process(bool load) -> void {
|
||||||
bind(natural, "Video/Luminance", video.luminance);
|
bind(natural, "Video/Luminance", video.luminance);
|
||||||
bind(natural, "Video/Saturation", video.saturation);
|
bind(natural, "Video/Saturation", video.saturation);
|
||||||
bind(natural, "Video/Gamma", video.gamma);
|
bind(natural, "Video/Gamma", video.gamma);
|
||||||
bind(boolean, "Video/FastForwardFrameSkip", video.fastForwardFrameSkip);
|
|
||||||
bind(boolean, "Video/Snow", video.snow);
|
bind(boolean, "Video/Snow", video.snow);
|
||||||
|
|
||||||
bind(text, "Video/Output", video.output);
|
bind(text, "Video/Output", video.output);
|
||||||
|
@ -79,6 +80,7 @@ auto Settings::process(bool load) -> void {
|
||||||
bind(natural, "Input/Frequency", input.frequency);
|
bind(natural, "Input/Frequency", input.frequency);
|
||||||
bind(text, "Input/Defocus", input.defocus);
|
bind(text, "Input/Defocus", input.defocus);
|
||||||
bind(natural, "Input/Turbo/Frequency", input.turbo.frequency);
|
bind(natural, "Input/Turbo/Frequency", input.turbo.frequency);
|
||||||
|
bind(text, "Input/Hotkey/Logic", input.hotkey.logic);
|
||||||
|
|
||||||
bind(text, "Path/Games", path.games);
|
bind(text, "Path/Games", path.games);
|
||||||
bind(text, "Path/Patches", path.patches);
|
bind(text, "Path/Patches", path.patches);
|
||||||
|
@ -93,13 +95,20 @@ auto Settings::process(bool load) -> void {
|
||||||
bind(text, "Path/Recent/SufamiTurboA", path.recent.sufamiTurboA);
|
bind(text, "Path/Recent/SufamiTurboA", path.recent.sufamiTurboA);
|
||||||
bind(text, "Path/Recent/SufamiTurboB", path.recent.sufamiTurboB);
|
bind(text, "Path/Recent/SufamiTurboB", path.recent.sufamiTurboB);
|
||||||
|
|
||||||
|
bind(natural, "FastForward/FrameSkip", fastForward.frameSkip);
|
||||||
|
bind(natural, "FastForward/Limiter", fastForward.limiter);
|
||||||
|
bind(boolean, "FastForward/Mute", fastForward.mute);
|
||||||
|
|
||||||
|
bind(natural, "Rewind/Frequency", rewind.frequency);
|
||||||
|
bind(natural, "Rewind/Length", rewind.length);
|
||||||
|
bind(boolean, "Rewind/Mute", rewind.mute);
|
||||||
|
|
||||||
bind(boolean, "Emulator/WarnOnUnverifiedGames", emulator.warnOnUnverifiedGames);
|
bind(boolean, "Emulator/WarnOnUnverifiedGames", emulator.warnOnUnverifiedGames);
|
||||||
bind(boolean, "Emulator/AutoSaveMemory/Enable", emulator.autoSaveMemory.enable);
|
bind(boolean, "Emulator/AutoSaveMemory/Enable", emulator.autoSaveMemory.enable);
|
||||||
bind(natural, "Emulator/AutoSaveMemory/Interval", emulator.autoSaveMemory.interval);
|
bind(natural, "Emulator/AutoSaveMemory/Interval", emulator.autoSaveMemory.interval);
|
||||||
bind(boolean, "Emulator/AutoSaveStateOnUnload", emulator.autoSaveStateOnUnload);
|
bind(boolean, "Emulator/AutoSaveStateOnUnload", emulator.autoSaveStateOnUnload);
|
||||||
bind(boolean, "Emulator/AutoLoadStateOnLoad", emulator.autoLoadStateOnLoad);
|
bind(boolean, "Emulator/AutoLoadStateOnLoad", emulator.autoLoadStateOnLoad);
|
||||||
bind(natural, "Emulator/Rewind/Frequency", emulator.rewind.frequency);
|
bind(natural, "Emulator/Hack/CPU/Overclock", emulator.hack.cpu.overclock);
|
||||||
bind(natural, "Emulator/Rewind/Length", emulator.rewind.length);
|
|
||||||
bind(boolean, "Emulator/Hack/PPU/Fast", emulator.hack.ppu.fast);
|
bind(boolean, "Emulator/Hack/PPU/Fast", emulator.hack.ppu.fast);
|
||||||
bind(boolean, "Emulator/Hack/PPU/NoSpriteLimit", emulator.hack.ppu.noSpriteLimit);
|
bind(boolean, "Emulator/Hack/PPU/NoSpriteLimit", emulator.hack.ppu.noSpriteLimit);
|
||||||
bind(natural, "Emulator/Hack/PPU/Mode7/Scale", emulator.hack.ppu.mode7.scale);
|
bind(natural, "Emulator/Hack/PPU/Mode7/Scale", emulator.hack.ppu.mode7.scale);
|
||||||
|
@ -110,7 +119,8 @@ auto Settings::process(bool load) -> void {
|
||||||
bind(boolean, "Emulator/Hack/DSP/Cubic", emulator.hack.dsp.cubic);
|
bind(boolean, "Emulator/Hack/DSP/Cubic", emulator.hack.dsp.cubic);
|
||||||
bind(boolean, "Emulator/Hack/Coprocessors/DelayedSync", emulator.hack.coprocessors.delayedSync);
|
bind(boolean, "Emulator/Hack/Coprocessors/DelayedSync", emulator.hack.coprocessors.delayedSync);
|
||||||
bind(boolean, "Emulator/Hack/Coprocessors/HLE", emulator.hack.coprocessors.hle);
|
bind(boolean, "Emulator/Hack/Coprocessors/HLE", emulator.hack.coprocessors.hle);
|
||||||
bind(natural, "Emulator/Hack/FastSuperFX", emulator.hack.fastSuperFX);
|
bind(natural, "Emulator/Hack/SA1/Overclock", emulator.hack.sa1.overclock);
|
||||||
|
bind(natural, "Emulator/Hack/SuperFX/Overclock", emulator.hack.superfx.overclock);
|
||||||
bind(boolean, "Emulator/Cheats/Enable", emulator.cheats.enable);
|
bind(boolean, "Emulator/Cheats/Enable", emulator.cheats.enable);
|
||||||
|
|
||||||
bind(boolean, "General/StatusBar", general.statusBar);
|
bind(boolean, "General/StatusBar", general.statusBar);
|
||||||
|
@ -121,19 +131,61 @@ auto Settings::process(bool load) -> void {
|
||||||
#undef bind
|
#undef bind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SettingsHome : VerticalLayout {
|
||||||
|
SettingsHome() {
|
||||||
|
setCollapsible();
|
||||||
|
setVisible(false);
|
||||||
|
image icon{Resource::Icon};
|
||||||
|
auto data = icon.data();
|
||||||
|
for(uint y : range(icon.height())) {
|
||||||
|
for(uint x : range(icon.width())) {
|
||||||
|
auto pixel = icon.read(data);
|
||||||
|
auto a = pixel >> 24 & 255;
|
||||||
|
auto r = pixel >> 16 & 255;
|
||||||
|
auto g = pixel >> 8 & 255;
|
||||||
|
auto b = pixel >> 0 & 255;
|
||||||
|
a = a * 0.25;
|
||||||
|
icon.write(data, a << 24 | r << 16 | g << 8 | b << 0);
|
||||||
|
data += icon.stride();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canvas.setIcon(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Canvas canvas{this, Size{~0, ~0}};
|
||||||
|
} settingsHome;
|
||||||
|
|
||||||
auto SettingsWindow::create() -> void {
|
auto SettingsWindow::create() -> void {
|
||||||
layout.setPadding(5_sx);
|
layout.setPadding(5_sx);
|
||||||
panel.append(videoSettings);
|
panelList.append(ListViewItem().setText("Video").setIcon(Icon::Device::Display));
|
||||||
panel.append(audioSettings);
|
panelList.append(ListViewItem().setText("Audio").setIcon(Icon::Device::Speaker));
|
||||||
panel.append(inputSettings);
|
panelList.append(ListViewItem().setText("Input").setIcon(Icon::Device::Joypad));
|
||||||
panel.append(hotkeySettings);
|
panelList.append(ListViewItem().setText("Hotkeys").setIcon(Icon::Device::Keyboard));
|
||||||
panel.append(pathSettings);
|
panelList.append(ListViewItem().setText("Paths").setIcon(Icon::Emblem::Folder));
|
||||||
panel.append(emulatorSettings);
|
panelList.append(ListViewItem().setText("Speed").setIcon(Icon::Device::Clock));
|
||||||
panel.append(driverSettings);
|
panelList.append(ListViewItem().setText("Emulator").setIcon(Icon::Action::Settings));
|
||||||
|
panelList.append(ListViewItem().setText("Drivers").setIcon(Icon::Place::Settings));
|
||||||
|
panelList.onChange([&] {
|
||||||
|
if(auto item = panelList.selected()) {
|
||||||
|
show(item.offset());
|
||||||
|
} else {
|
||||||
|
show(-1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
panelContainer.append(settingsHome, Size{~0, ~0});
|
||||||
|
panelContainer.append(videoSettings, Size{~0, ~0});
|
||||||
|
panelContainer.append(audioSettings, Size{~0, ~0});
|
||||||
|
panelContainer.append(inputSettings, Size{~0, ~0});
|
||||||
|
panelContainer.append(hotkeySettings, Size{~0, ~0});
|
||||||
|
panelContainer.append(pathSettings, Size{~0, ~0});
|
||||||
|
panelContainer.append(speedSettings, Size{~0, ~0});
|
||||||
|
panelContainer.append(emulatorSettings, Size{~0, ~0});
|
||||||
|
panelContainer.append(driverSettings, Size{~0, ~0});
|
||||||
statusBar.setFont(Font().setBold());
|
statusBar.setFont(Font().setBold());
|
||||||
|
|
||||||
setTitle("Settings");
|
setTitle("Settings");
|
||||||
setSize({600_sx, 400_sx});
|
setSize({680_sx, 400_sx});
|
||||||
setAlignment({0.0, 1.0});
|
setAlignment({0.0, 1.0});
|
||||||
setDismissable();
|
setDismissable();
|
||||||
|
|
||||||
|
@ -153,8 +205,27 @@ auto SettingsWindow::setVisible(bool visible) -> SettingsWindow& {
|
||||||
return Window::setVisible(visible), *this;
|
return Window::setVisible(visible), *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SettingsWindow::show(uint index) -> void {
|
auto SettingsWindow::show(int index) -> void {
|
||||||
panel.item(index).setSelected();
|
settingsHome.setVisible(false);
|
||||||
|
videoSettings.setVisible(false);
|
||||||
|
audioSettings.setVisible(false);
|
||||||
|
inputSettings.setVisible(false);
|
||||||
|
hotkeySettings.setVisible(false);
|
||||||
|
pathSettings.setVisible(false);
|
||||||
|
speedSettings.setVisible(false);
|
||||||
|
emulatorSettings.setVisible(false);
|
||||||
|
driverSettings.setVisible(false);
|
||||||
|
panelList.item(index).setSelected();
|
||||||
|
if(index ==-1) settingsHome.setVisible(true);
|
||||||
|
if(index == 0) videoSettings.setVisible(true);
|
||||||
|
if(index == 1) audioSettings.setVisible(true);
|
||||||
|
if(index == 2) inputSettings.setVisible(true);
|
||||||
|
if(index == 3) hotkeySettings.setVisible(true);
|
||||||
|
if(index == 4) pathSettings.setVisible(true);
|
||||||
|
if(index == 5) speedSettings.setVisible(true);
|
||||||
|
if(index == 6) emulatorSettings.setVisible(true);
|
||||||
|
if(index == 7) driverSettings.setVisible(true);
|
||||||
|
panelContainer.resize();
|
||||||
setVisible();
|
setVisible();
|
||||||
setFocused();
|
setFocused();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ struct Settings : Markup::Node {
|
||||||
uint luminance = 100;
|
uint luminance = 100;
|
||||||
uint saturation = 100;
|
uint saturation = 100;
|
||||||
uint gamma = 150;
|
uint gamma = 150;
|
||||||
bool fastForwardFrameSkip = true;
|
|
||||||
bool snow = false;
|
bool snow = false;
|
||||||
|
|
||||||
string output = "Scale";
|
string output = "Scale";
|
||||||
|
@ -50,6 +49,9 @@ struct Settings : Markup::Node {
|
||||||
struct Turbo {
|
struct Turbo {
|
||||||
uint frequency = 4;
|
uint frequency = 4;
|
||||||
} turbo;
|
} turbo;
|
||||||
|
struct Hotkey {
|
||||||
|
string logic = "or";
|
||||||
|
} hotkey;
|
||||||
} input;
|
} input;
|
||||||
|
|
||||||
struct Path {
|
struct Path {
|
||||||
|
@ -68,6 +70,18 @@ struct Settings : Markup::Node {
|
||||||
} recent;
|
} recent;
|
||||||
} path;
|
} path;
|
||||||
|
|
||||||
|
struct FastForward {
|
||||||
|
uint frameSkip = 9;
|
||||||
|
uint limiter = 0;
|
||||||
|
bool mute = false;
|
||||||
|
} fastForward;
|
||||||
|
|
||||||
|
struct Rewind {
|
||||||
|
uint frequency = 0;
|
||||||
|
uint length = 80;
|
||||||
|
bool mute = false;
|
||||||
|
} rewind;
|
||||||
|
|
||||||
struct Emulator {
|
struct Emulator {
|
||||||
bool warnOnUnverifiedGames = false;
|
bool warnOnUnverifiedGames = false;
|
||||||
struct AutoSaveMemory {
|
struct AutoSaveMemory {
|
||||||
|
@ -76,11 +90,10 @@ struct Settings : Markup::Node {
|
||||||
} autoSaveMemory;
|
} autoSaveMemory;
|
||||||
bool autoSaveStateOnUnload = false;
|
bool autoSaveStateOnUnload = false;
|
||||||
bool autoLoadStateOnLoad = false;
|
bool autoLoadStateOnLoad = false;
|
||||||
struct Rewind {
|
|
||||||
uint frequency = 0;
|
|
||||||
uint length = 80;
|
|
||||||
} rewind;
|
|
||||||
struct Hack {
|
struct Hack {
|
||||||
|
struct CPU {
|
||||||
|
uint overclock = 100;
|
||||||
|
} cpu;
|
||||||
struct PPU {
|
struct PPU {
|
||||||
bool fast = true;
|
bool fast = true;
|
||||||
bool noSpriteLimit = false;
|
bool noSpriteLimit = false;
|
||||||
|
@ -99,7 +112,12 @@ struct Settings : Markup::Node {
|
||||||
bool delayedSync = true;
|
bool delayedSync = true;
|
||||||
bool hle = true;
|
bool hle = true;
|
||||||
} coprocessors;
|
} coprocessors;
|
||||||
uint fastSuperFX = 100;
|
struct SA1 {
|
||||||
|
uint overclock = 100;
|
||||||
|
} sa1;
|
||||||
|
struct SuperFX {
|
||||||
|
uint overclock = 100;
|
||||||
|
} superfx;
|
||||||
} hack;
|
} hack;
|
||||||
struct Cheats {
|
struct Cheats {
|
||||||
bool enable = true;
|
bool enable = true;
|
||||||
|
@ -114,13 +132,12 @@ struct Settings : Markup::Node {
|
||||||
} general;
|
} general;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VideoSettings : TabFrameItem {
|
struct VideoSettings : VerticalLayout {
|
||||||
auto create() -> void;
|
auto create() -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VerticalLayout layout{this};
|
Label colorAdjustmentLabel{this, Size{~0, 0}, 2};
|
||||||
Label colorAdjustmentLabel{&layout, Size{~0, 0}, 2};
|
TableLayout colorLayout{this, Size{~0, 0}};
|
||||||
TableLayout colorLayout{&layout, Size{~0, 0}};
|
|
||||||
Label luminanceLabel{&colorLayout, Size{0, 0}};
|
Label luminanceLabel{&colorLayout, Size{0, 0}};
|
||||||
Label luminanceValue{&colorLayout, Size{50_sx, 0}};
|
Label luminanceValue{&colorLayout, Size{50_sx, 0}};
|
||||||
HorizontalSlider luminanceSlider{&colorLayout, Size{~0, 0}};
|
HorizontalSlider luminanceSlider{&colorLayout, Size{~0, 0}};
|
||||||
|
@ -133,17 +150,15 @@ private:
|
||||||
Label gammaValue{&colorLayout, Size{50_sx, 0}};
|
Label gammaValue{&colorLayout, Size{50_sx, 0}};
|
||||||
HorizontalSlider gammaSlider{&colorLayout, Size{~0, 0}};
|
HorizontalSlider gammaSlider{&colorLayout, Size{~0, 0}};
|
||||||
//
|
//
|
||||||
CheckLabel fastForwardFrameSkip{&layout, Size{~0, 0}};
|
CheckLabel snowOption{this, Size{~0, 0}};
|
||||||
CheckLabel snowOption{&layout, Size{~0, 0}};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AudioSettings : TabFrameItem {
|
struct AudioSettings : VerticalLayout {
|
||||||
auto create() -> void;
|
auto create() -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VerticalLayout layout{this};
|
Label effectsLabel{this, Size{~0, 0}, 2};
|
||||||
Label effectsLabel{&layout, Size{~0, 0}, 2};
|
TableLayout effectsLayout{this, Size{~0, 0}};
|
||||||
TableLayout effectsLayout{&layout, Size{~0, 0}};
|
|
||||||
Label skewLabel{&effectsLayout, Size{0, 0}};
|
Label skewLabel{&effectsLayout, Size{0, 0}};
|
||||||
Label skewValue{&effectsLayout, Size{50_sx, 0}};
|
Label skewValue{&effectsLayout, Size{50_sx, 0}};
|
||||||
HorizontalSlider skewSlider{&effectsLayout, Size{~0, 0}};
|
HorizontalSlider skewSlider{&effectsLayout, Size{~0, 0}};
|
||||||
|
@ -157,7 +172,7 @@ private:
|
||||||
HorizontalSlider balanceSlider{&effectsLayout, Size{~0, 0}};
|
HorizontalSlider balanceSlider{&effectsLayout, Size{~0, 0}};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InputSettings : TabFrameItem {
|
struct InputSettings : VerticalLayout {
|
||||||
auto create() -> void;
|
auto create() -> void;
|
||||||
auto updateControls() -> void;
|
auto updateControls() -> void;
|
||||||
auto activePort() -> InputPort&;
|
auto activePort() -> InputPort&;
|
||||||
|
@ -166,61 +181,72 @@ struct InputSettings : TabFrameItem {
|
||||||
auto reloadDevices() -> void;
|
auto reloadDevices() -> void;
|
||||||
auto reloadMappings() -> void;
|
auto reloadMappings() -> void;
|
||||||
auto refreshMappings() -> void;
|
auto refreshMappings() -> void;
|
||||||
auto assignMapping() -> void;
|
auto assignMapping(TableViewCell cell) -> void;
|
||||||
auto cancelMapping() -> void;
|
auto cancelMapping() -> void;
|
||||||
auto assignMouseInput(uint id) -> void;
|
auto assignMouseInput(uint id) -> void;
|
||||||
auto inputEvent(shared_pointer<HID::Device> device, uint group, uint input, int16 oldValue, int16 newValue, bool allowMouseInput = false) -> void;
|
auto inputEvent(shared_pointer<HID::Device> device, uint group, uint input, int16 oldValue, int16 newValue, bool allowMouseInput = false) -> void;
|
||||||
|
|
||||||
maybe<InputMapping&> activeMapping;
|
maybe<InputMapping&> activeMapping;
|
||||||
|
uint activeBinding = 0;
|
||||||
|
|
||||||
private:
|
public:
|
||||||
Timer timer;
|
Timer timer;
|
||||||
|
|
||||||
VerticalLayout layout{this};
|
HorizontalLayout inputFocusLayout{this, Size{~0, 0}};
|
||||||
HorizontalLayout selectionLayout{&layout, Size{~0, 0}};
|
Label inputFocusLabel{&inputFocusLayout, Size{0, 0}};
|
||||||
|
RadioLabel pauseEmulation{&inputFocusLayout, Size{0, 0}};
|
||||||
|
RadioLabel blockInput{&inputFocusLayout, Size{0, 0}};
|
||||||
|
RadioLabel allowInput{&inputFocusLayout, Size{0, 0}};
|
||||||
|
Group inputFocusGroup{&pauseEmulation, &blockInput, &allowInput};
|
||||||
|
Canvas separator{this, Size{~0, 1}};
|
||||||
|
HorizontalLayout selectionLayout{this, Size{~0, 0}};
|
||||||
Label portLabel{&selectionLayout, Size{0, 0}};
|
Label portLabel{&selectionLayout, Size{0, 0}};
|
||||||
ComboButton portList{&selectionLayout, Size{~0, 0}};
|
ComboButton portList{&selectionLayout, Size{~0, 0}};
|
||||||
Label deviceLabel{&selectionLayout, Size{0, 0}};
|
Label deviceLabel{&selectionLayout, Size{0, 0}};
|
||||||
ComboButton deviceList{&selectionLayout, Size{~0, 0}};
|
ComboButton deviceList{&selectionLayout, Size{~0, 0}};
|
||||||
Label turboLabel{&selectionLayout, Size{0, 0}};
|
Label turboLabel{&selectionLayout, Size{0, 0}};
|
||||||
ComboButton turboList{&selectionLayout, Size{0, 0}};
|
ComboButton turboList{&selectionLayout, Size{0, 0}};
|
||||||
TableView mappingList{&layout, Size{~0, ~0}};
|
TableView mappingList{this, Size{~0, ~0}};
|
||||||
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
HorizontalLayout controlLayout{this, Size{~0, 0}};
|
||||||
Button assignMouse1{&controlLayout, Size{100_sx, 0}};
|
Button assignMouse1{&controlLayout, Size{100_sx, 0}};
|
||||||
Button assignMouse2{&controlLayout, Size{100_sx, 0}};
|
Button assignMouse2{&controlLayout, Size{100_sx, 0}};
|
||||||
Button assignMouse3{&controlLayout, Size{100_sx, 0}};
|
Button assignMouse3{&controlLayout, Size{100_sx, 0}};
|
||||||
Widget controlSpacer{&controlLayout, Size{~0, 0}};
|
Canvas inputSink{&controlLayout, Size{~0, ~0}};
|
||||||
Button assignButton{&controlLayout, Size{80_sx, 0}};
|
Button assignButton{&controlLayout, Size{80_sx, 0}};
|
||||||
Button clearButton{&controlLayout, Size{80_sx, 0}};
|
Button clearButton{&controlLayout, Size{80_sx, 0}};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HotkeySettings : TabFrameItem {
|
struct HotkeySettings : VerticalLayout {
|
||||||
auto create() -> void;
|
auto create() -> void;
|
||||||
auto reloadMappings() -> void;
|
auto reloadMappings() -> void;
|
||||||
auto refreshMappings() -> void;
|
auto refreshMappings() -> void;
|
||||||
auto assignMapping() -> void;
|
auto assignMapping(TableViewCell cell) -> void;
|
||||||
auto cancelMapping() -> void;
|
auto cancelMapping() -> void;
|
||||||
auto inputEvent(shared_pointer<HID::Device> device, uint group, uint input, int16 oldValue, int16 newValue) -> void;
|
auto inputEvent(shared_pointer<HID::Device> device, uint group, uint input, int16 oldValue, int16 newValue) -> void;
|
||||||
|
|
||||||
maybe<InputMapping&> activeMapping;
|
maybe<InputMapping&> activeMapping;
|
||||||
|
uint activeBinding = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Timer timer;
|
Timer timer;
|
||||||
|
|
||||||
VerticalLayout layout{this};
|
TableView mappingList{this, Size{~0, ~0}};
|
||||||
TableView mappingList{&layout, Size{~0, ~0}};
|
HorizontalLayout controlLayout{this, Size{~0, 0}};
|
||||||
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
Label logicLabel{&controlLayout, Size{0, 0}};
|
||||||
Widget controlSpacer{&controlLayout, Size{~0, 0}};
|
RadioLabel logicAND{&controlLayout, Size{0, 0}};
|
||||||
|
RadioLabel logicOR{&controlLayout, Size{0, 0}};
|
||||||
|
Group logicGroup{&logicAND, &logicOR};
|
||||||
|
Canvas inputSink{&controlLayout, Size{~0, ~0}};
|
||||||
Button assignButton{&controlLayout, Size{80_sx, 0}};
|
Button assignButton{&controlLayout, Size{80_sx, 0}};
|
||||||
Button clearButton{&controlLayout, Size{80_sx, 0}};
|
Button clearButton{&controlLayout, Size{80_sx, 0}};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PathSettings : TabFrameItem {
|
struct PathSettings : VerticalLayout {
|
||||||
auto create() -> void;
|
auto create() -> void;
|
||||||
auto refreshPaths() -> void;
|
auto refreshPaths() -> void;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TableLayout layout{this};
|
TableLayout layout{this, Size{~0, ~0}};
|
||||||
Label gamesLabel{&layout, Size{0, 0}};
|
Label gamesLabel{&layout, Size{0, 0}};
|
||||||
LineEdit gamesPath{&layout, Size{~0, 0}};
|
LineEdit gamesPath{&layout, Size{~0, 0}};
|
||||||
Button gamesAssign{&layout, Size{80_sx, 0}};
|
Button gamesAssign{&layout, Size{80_sx, 0}};
|
||||||
|
@ -252,57 +278,73 @@ public:
|
||||||
Button screenshotsReset{&layout, Size{80_sx, 0}};
|
Button screenshotsReset{&layout, Size{80_sx, 0}};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EmulatorSettings : TabFrameItem {
|
struct SpeedSettings : VerticalLayout {
|
||||||
auto create() -> void;
|
auto create() -> void;
|
||||||
auto updateConfiguration() -> void;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VerticalLayout layout{this};
|
Label overclockingLabel{this, Size{~0, 0}, 2};
|
||||||
Label optionsLabel{&layout, Size{~0, 0}, 2};
|
TableLayout overclockingLayout{this, Size{~0, 0}};
|
||||||
HorizontalLayout inputFocusLayout{&layout, Size{~0, 0}};
|
Label cpuLabel{&overclockingLayout, Size{0, 0}};
|
||||||
Label inputFocusLabel{&inputFocusLayout, Size{0, 0}};
|
Label cpuValue{&overclockingLayout, Size{50_sx, 0}};
|
||||||
RadioLabel pauseEmulation{&inputFocusLayout, Size{0, 0}};
|
HorizontalSlider cpuClock{&overclockingLayout, Size{~0, 0}};
|
||||||
RadioLabel blockInput{&inputFocusLayout, Size{0, 0}};
|
//
|
||||||
RadioLabel allowInput{&inputFocusLayout, Size{0, 0}};
|
Label sa1Label{&overclockingLayout, Size{0, 0}};
|
||||||
Group inputFocusGroup{&pauseEmulation, &blockInput, &allowInput};
|
Label sa1Value{&overclockingLayout, Size{50_sx, 0}};
|
||||||
CheckLabel warnOnUnverifiedGames{&layout, Size{~0, 0}};
|
HorizontalSlider sa1Clock{&overclockingLayout, Size{~0, 0}};
|
||||||
CheckLabel autoSaveMemory{&layout, Size{~0, 0}};
|
//
|
||||||
HorizontalLayout autoStateLayout{&layout, Size{~0, 0}};
|
Label sfxLabel{&overclockingLayout, Size{0, 0}};
|
||||||
CheckLabel autoSaveStateOnUnload{&autoStateLayout, Size{0, 0}};
|
Label sfxValue{&overclockingLayout, Size{50_sx, 0}};
|
||||||
CheckLabel autoLoadStateOnLoad{&autoStateLayout, Size{0, 0}};
|
HorizontalSlider sfxClock{&overclockingLayout, Size{~0, 0}};
|
||||||
HorizontalLayout rewindLayout{&layout, Size{~0, 0}};
|
Label fastForwardLabel{this, Size{~0, 0}, 2};
|
||||||
|
HorizontalLayout fastForwardLayout{this, Size{~0, 0}};
|
||||||
|
Label frameSkipLabel{&fastForwardLayout, Size{0, 0}};
|
||||||
|
ComboButton frameSkipAmount{&fastForwardLayout, Size{0, 0}};
|
||||||
|
Label limiterLabel{&fastForwardLayout, Size{0, 0}};
|
||||||
|
ComboButton limiterAmount{&fastForwardLayout, Size{0, 0}};
|
||||||
|
CheckLabel fastForwardMute{this, Size{0, 0}};
|
||||||
|
Label rewindLabel{this, Size{~0, 0}, 2};
|
||||||
|
HorizontalLayout rewindLayout{this, Size{~0, 0}};
|
||||||
Label rewindFrequencyLabel{&rewindLayout, Size{0, 0}};
|
Label rewindFrequencyLabel{&rewindLayout, Size{0, 0}};
|
||||||
ComboButton rewindFrequencyOption{&rewindLayout, Size{0, 0}};
|
ComboButton rewindFrequencyOption{&rewindLayout, Size{0, 0}};
|
||||||
Label rewindLengthLabel{&rewindLayout, Size{0, 0}};
|
Label rewindLengthLabel{&rewindLayout, Size{0, 0}};
|
||||||
ComboButton rewindLengthOption{&rewindLayout, Size{0, 0}};
|
ComboButton rewindLengthOption{&rewindLayout, Size{0, 0}};
|
||||||
Canvas optionsSpacer{&layout, Size{~0, 1}};
|
CheckLabel rewindMute{this, Size{0, 0}};
|
||||||
Label ppuLabel{&layout, Size{~0, 0}, 2};
|
};
|
||||||
HorizontalLayout ppuLayout{&layout, Size{~0, 0}};
|
|
||||||
|
struct EmulatorSettings : VerticalLayout {
|
||||||
|
auto create() -> void;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Label optionsLabel{this, Size{~0, 0}, 2};
|
||||||
|
CheckLabel warnOnUnverifiedGames{this, Size{~0, 0}};
|
||||||
|
CheckLabel autoSaveMemory{this, Size{~0, 0}};
|
||||||
|
HorizontalLayout autoStateLayout{this, Size{~0, 0}};
|
||||||
|
CheckLabel autoSaveStateOnUnload{&autoStateLayout, Size{0, 0}};
|
||||||
|
CheckLabel autoLoadStateOnLoad{&autoStateLayout, Size{0, 0}};
|
||||||
|
Canvas optionsSpacer{this, Size{~0, 1}};
|
||||||
|
Label ppuLabel{this, Size{~0, 0}, 2};
|
||||||
|
HorizontalLayout ppuLayout{this, Size{~0, 0}};
|
||||||
CheckLabel fastPPU{&ppuLayout, Size{0, 0}};
|
CheckLabel fastPPU{&ppuLayout, Size{0, 0}};
|
||||||
CheckLabel noSpriteLimit{&ppuLayout, Size{0, 0}};
|
CheckLabel noSpriteLimit{&ppuLayout, Size{0, 0}};
|
||||||
Label mode7Label{&layout, Size{~0, 0}, 2};
|
Label mode7Label{this, Size{~0, 0}, 2};
|
||||||
HorizontalLayout mode7Layout{&layout, Size{~0, 0}};
|
HorizontalLayout mode7Layout{this, Size{~0, 0}};
|
||||||
Label mode7ScaleLabel{&mode7Layout, Size{0, 0}};
|
Label mode7ScaleLabel{&mode7Layout, Size{0, 0}};
|
||||||
ComboButton mode7Scale{&mode7Layout, Size{0, 0}};
|
ComboButton mode7Scale{&mode7Layout, Size{0, 0}};
|
||||||
CheckLabel mode7Perspective{&mode7Layout, Size{0, 0}};
|
CheckLabel mode7Perspective{&mode7Layout, Size{0, 0}};
|
||||||
CheckLabel mode7Supersample{&mode7Layout, Size{0, 0}};
|
CheckLabel mode7Supersample{&mode7Layout, Size{0, 0}};
|
||||||
CheckLabel mode7Mosaic{&mode7Layout, Size{0, 0}};
|
CheckLabel mode7Mosaic{&mode7Layout, Size{0, 0}};
|
||||||
Label dspLabel{&layout, Size{~0, 0}, 2};
|
Label dspLabel{this, Size{~0, 0}, 2};
|
||||||
HorizontalLayout dspLayout{&layout, Size{~0, 0}};
|
HorizontalLayout dspLayout{this, Size{~0, 0}};
|
||||||
CheckLabel fastDSP{&dspLayout, Size{0, 0}};
|
CheckLabel fastDSP{&dspLayout, Size{0, 0}};
|
||||||
CheckLabel cubicInterpolation{&dspLayout, Size{0, 0}};
|
CheckLabel cubicInterpolation{&dspLayout, Size{0, 0}};
|
||||||
Label coprocessorLabel{&layout, Size{~0, 0}, 2};
|
Label coprocessorLabel{this, Size{~0, 0}, 2};
|
||||||
HorizontalLayout coprocessorsLayout{&layout, Size{~0, 0}};
|
HorizontalLayout coprocessorsLayout{this, Size{~0, 0}};
|
||||||
CheckLabel coprocessorsDelayedSyncOption{&coprocessorsLayout, Size{0, 0}};
|
CheckLabel coprocessorsDelayedSyncOption{&coprocessorsLayout, Size{0, 0}};
|
||||||
CheckLabel coprocessorsHLEOption{&coprocessorsLayout, Size{0, 0}};
|
CheckLabel coprocessorsHLEOption{&coprocessorsLayout, Size{0, 0}};
|
||||||
HorizontalLayout superFXLayout{&layout, Size{~0, 0}};
|
Label hacksNote{this, Size{~0, 0}};
|
||||||
Label superFXLabel{&superFXLayout, Size{0, 0}};
|
|
||||||
Label superFXValue{&superFXLayout, Size{50_sx, 0}};
|
|
||||||
HorizontalSlider superFXClock{&superFXLayout, Size{~0, 0}};
|
|
||||||
Label hacksNote{&layout, Size{~0, 0}};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DriverSettings : TabFrameItem {
|
struct DriverSettings : VerticalLayout {
|
||||||
auto create() -> void;
|
auto create() -> void;
|
||||||
auto videoDriverChanged() -> void;
|
auto videoDriverChanged() -> void;
|
||||||
auto videoDriverChange() -> void;
|
auto videoDriverChange() -> void;
|
||||||
|
@ -320,9 +362,8 @@ struct DriverSettings : TabFrameItem {
|
||||||
auto inputDriverChange() -> void;
|
auto inputDriverChange() -> void;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VerticalLayout layout{this};
|
Label videoLabel{this, Size{~0, 0}, 2};
|
||||||
Label videoLabel{&layout, Size{~0, 0}, 2};
|
TableLayout videoLayout{this, Size{~0, 0}};
|
||||||
TableLayout videoLayout{&layout, Size{~0, 0}};
|
|
||||||
Label videoDriverLabel{&videoLayout, Size{0, 0}};
|
Label videoDriverLabel{&videoLayout, Size{0, 0}};
|
||||||
HorizontalLayout videoDriverLayout{&videoLayout, Size{~0, 0}};
|
HorizontalLayout videoDriverLayout{&videoLayout, Size{~0, 0}};
|
||||||
ComboButton videoDriverOption{&videoDriverLayout, Size{0, 0}};
|
ComboButton videoDriverOption{&videoDriverLayout, Size{0, 0}};
|
||||||
|
@ -331,12 +372,12 @@ public:
|
||||||
Label videoFormatLabel{&videoLayout, Size{0, 0}};
|
Label videoFormatLabel{&videoLayout, Size{0, 0}};
|
||||||
HorizontalLayout videoPropertyLayout{&videoLayout, Size{~0, 0}};
|
HorizontalLayout videoPropertyLayout{&videoLayout, Size{~0, 0}};
|
||||||
ComboButton videoFormatOption{&videoPropertyLayout, Size{0, 0}};
|
ComboButton videoFormatOption{&videoPropertyLayout, Size{0, 0}};
|
||||||
HorizontalLayout videoToggleLayout{&layout, Size{~0, 0}};
|
HorizontalLayout videoToggleLayout{this, Size{~0, 0}};
|
||||||
CheckLabel videoBlockingToggle{&videoToggleLayout, Size{0, 0}};
|
CheckLabel videoBlockingToggle{&videoToggleLayout, Size{0, 0}};
|
||||||
CheckLabel videoFlushToggle{&videoToggleLayout, Size{0, 0}};
|
CheckLabel videoFlushToggle{&videoToggleLayout, Size{0, 0}};
|
||||||
Canvas videoSpacer{&layout, Size{~0, 1}};
|
Canvas videoSpacer{this, Size{~0, 1}};
|
||||||
Label audioLabel{&layout, Size{~0, 0}, 2};
|
Label audioLabel{this, Size{~0, 0}, 2};
|
||||||
TableLayout audioLayout{&layout, Size{~0, 0}};
|
TableLayout audioLayout{this, Size{~0, 0}};
|
||||||
Label audioDriverLabel{&audioLayout, Size{0, 0}};
|
Label audioDriverLabel{&audioLayout, Size{0, 0}};
|
||||||
HorizontalLayout audioDriverLayout{&audioLayout, Size{~0, 0}};
|
HorizontalLayout audioDriverLayout{&audioLayout, Size{~0, 0}};
|
||||||
ComboButton audioDriverOption{&audioDriverLayout, Size{0, 0}};
|
ComboButton audioDriverOption{&audioDriverLayout, Size{0, 0}};
|
||||||
|
@ -349,13 +390,13 @@ public:
|
||||||
ComboButton audioFrequencyOption{&audioPropertyLayout, Size{0, 0}};
|
ComboButton audioFrequencyOption{&audioPropertyLayout, Size{0, 0}};
|
||||||
Label audioLatencyLabel{&audioPropertyLayout, Size{0, 0}};
|
Label audioLatencyLabel{&audioPropertyLayout, Size{0, 0}};
|
||||||
ComboButton audioLatencyOption{&audioPropertyLayout, Size{0, 0}};
|
ComboButton audioLatencyOption{&audioPropertyLayout, Size{0, 0}};
|
||||||
HorizontalLayout audioToggleLayout{&layout, Size{~0, 0}};
|
HorizontalLayout audioToggleLayout{this, Size{~0, 0}};
|
||||||
CheckLabel audioExclusiveToggle{&audioToggleLayout, Size{0, 0}};
|
CheckLabel audioExclusiveToggle{&audioToggleLayout, Size{0, 0}};
|
||||||
CheckLabel audioBlockingToggle{&audioToggleLayout, Size{0, 0}};
|
CheckLabel audioBlockingToggle{&audioToggleLayout, Size{0, 0}};
|
||||||
CheckLabel audioDynamicToggle{&audioToggleLayout, Size{0, 0}};
|
CheckLabel audioDynamicToggle{&audioToggleLayout, Size{0, 0}};
|
||||||
Canvas audioSpacer{&layout, Size{~0, 1}};
|
Canvas audioSpacer{this, Size{~0, 1}};
|
||||||
Label inputLabel{&layout, Size{~0, 0}, 2};
|
Label inputLabel{this, Size{~0, 0}, 2};
|
||||||
TableLayout inputLayout{&layout, Size{~0, 0}};
|
TableLayout inputLayout{this, Size{~0, 0}};
|
||||||
Label inputDriverLabel{&inputLayout, Size{0, 0}};
|
Label inputDriverLabel{&inputLayout, Size{0, 0}};
|
||||||
HorizontalLayout inputDriverLayout{&inputLayout, Size{~0, 0}};
|
HorizontalLayout inputDriverLayout{&inputLayout, Size{~0, 0}};
|
||||||
ComboButton inputDriverOption{&inputDriverLayout, Size{0, 0}};
|
ComboButton inputDriverOption{&inputDriverLayout, Size{0, 0}};
|
||||||
|
@ -363,14 +404,16 @@ public:
|
||||||
Label inputDriverActive{&inputDriverLayout, Size{0, 0}};
|
Label inputDriverActive{&inputDriverLayout, Size{0, 0}};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SettingsWindow : Window {
|
struct SettingsWindow : Window, Lock {
|
||||||
auto create() -> void;
|
auto create() -> void;
|
||||||
auto setVisible(bool visible = true) -> SettingsWindow&;
|
auto setVisible(bool visible = true) -> SettingsWindow&;
|
||||||
auto show(uint index) -> void;
|
auto show(int index) -> void;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VerticalLayout layout{this};
|
VerticalLayout layout{this};
|
||||||
TabFrame panel{&layout, Size{~0, ~0}};
|
HorizontalLayout panelLayout{&layout, Size{~0, ~0}};
|
||||||
|
ListView panelList{&panelLayout, Size{120_sx, ~0}};
|
||||||
|
VerticalLayout panelContainer{&panelLayout, Size{~0, ~0}};
|
||||||
StatusBar statusBar{this};
|
StatusBar statusBar{this};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -380,6 +423,7 @@ extern AudioSettings audioSettings;
|
||||||
extern InputSettings inputSettings;
|
extern InputSettings inputSettings;
|
||||||
extern HotkeySettings hotkeySettings;
|
extern HotkeySettings hotkeySettings;
|
||||||
extern PathSettings pathSettings;
|
extern PathSettings pathSettings;
|
||||||
|
extern SpeedSettings speedSettings;
|
||||||
extern EmulatorSettings emulatorSettings;
|
extern EmulatorSettings emulatorSettings;
|
||||||
extern DriverSettings driverSettings;
|
extern DriverSettings driverSettings;
|
||||||
namespace Instances { extern Instance<SettingsWindow> settingsWindow; }
|
namespace Instances { extern Instance<SettingsWindow> settingsWindow; }
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
auto SpeedSettings::create() -> void {
|
||||||
|
setCollapsible();
|
||||||
|
setVisible(false);
|
||||||
|
|
||||||
|
overclockingLabel.setText("Overclocking").setFont(Font().setBold());
|
||||||
|
|
||||||
|
overclockingLayout.setSize({3, 3});
|
||||||
|
overclockingLayout.column(0).setAlignment(1.0);
|
||||||
|
overclockingLayout.column(1).setAlignment(0.5);
|
||||||
|
|
||||||
|
cpuLabel.setText("CPU:");
|
||||||
|
cpuClock.setLength(301).setPosition((settings.emulator.hack.cpu.overclock - 100)).onChange([&] {
|
||||||
|
settings.emulator.hack.cpu.overclock = cpuClock.position() + 100;
|
||||||
|
emulator->configure("Hacks/CPU/Overclock", settings.emulator.hack.cpu.overclock);
|
||||||
|
cpuValue.setText({settings.emulator.hack.cpu.overclock, "%"});
|
||||||
|
}).doChange();
|
||||||
|
|
||||||
|
sa1Label.setText("SA-1:");
|
||||||
|
sa1Clock.setLength(301).setPosition((settings.emulator.hack.sa1.overclock - 100)).onChange([&] {
|
||||||
|
settings.emulator.hack.sa1.overclock = sa1Clock.position() + 100;
|
||||||
|
emulator->configure("Hacks/SA1/Overclock", settings.emulator.hack.sa1.overclock);
|
||||||
|
sa1Value.setText({settings.emulator.hack.sa1.overclock, "%"});
|
||||||
|
}).doChange();
|
||||||
|
|
||||||
|
sfxLabel.setText("SuperFX:");
|
||||||
|
sfxClock.setLength(141).setPosition((settings.emulator.hack.superfx.overclock - 100) / 5).onChange([&] {
|
||||||
|
settings.emulator.hack.superfx.overclock = sfxClock.position() * 5 + 100;
|
||||||
|
emulator->configure("Hacks/SuperFX/Overclock", settings.emulator.hack.superfx.overclock);
|
||||||
|
sfxValue.setText({settings.emulator.hack.superfx.overclock, "%"});
|
||||||
|
}).doChange();
|
||||||
|
|
||||||
|
fastForwardLabel.setText("Fast Forward").setFont(Font().setBold());
|
||||||
|
|
||||||
|
frameSkipLabel.setText("Frame skip:").setToolTip({
|
||||||
|
"Set how many frames to skip while fast forwarding.\n"
|
||||||
|
"Frame skipping allows a higher maximum fast forwarding frame rate."
|
||||||
|
});
|
||||||
|
|
||||||
|
frameSkipAmount.append(ComboButtonItem().setText("None"));
|
||||||
|
frameSkipAmount.append(ComboButtonItem().setText("1 frame"));
|
||||||
|
frameSkipAmount.append(ComboButtonItem().setText("2 frames"));
|
||||||
|
frameSkipAmount.append(ComboButtonItem().setText("3 frames"));
|
||||||
|
frameSkipAmount.append(ComboButtonItem().setText("4 frames"));
|
||||||
|
frameSkipAmount.append(ComboButtonItem().setText("5 frames"));
|
||||||
|
frameSkipAmount.append(ComboButtonItem().setText("6 frames"));
|
||||||
|
frameSkipAmount.append(ComboButtonItem().setText("7 frames"));
|
||||||
|
frameSkipAmount.append(ComboButtonItem().setText("8 frames"));
|
||||||
|
frameSkipAmount.append(ComboButtonItem().setText("9 frames"));
|
||||||
|
frameSkipAmount.item(settings.fastForward.frameSkip).setSelected();
|
||||||
|
frameSkipAmount.onChange([&] {
|
||||||
|
settings.fastForward.frameSkip = frameSkipAmount.selected().offset();
|
||||||
|
});
|
||||||
|
|
||||||
|
limiterLabel.setText("Limiter:").setToolTip({
|
||||||
|
"Set the maximum speed when fast forwarding."
|
||||||
|
});
|
||||||
|
|
||||||
|
limiterAmount.append(ComboButtonItem().setText("None"));
|
||||||
|
limiterAmount.append(ComboButtonItem().setText("200%"));
|
||||||
|
limiterAmount.append(ComboButtonItem().setText("300%"));
|
||||||
|
limiterAmount.append(ComboButtonItem().setText("400%"));
|
||||||
|
limiterAmount.append(ComboButtonItem().setText("500%"));
|
||||||
|
limiterAmount.append(ComboButtonItem().setText("600%"));
|
||||||
|
limiterAmount.append(ComboButtonItem().setText("700%"));
|
||||||
|
limiterAmount.append(ComboButtonItem().setText("800%"));
|
||||||
|
if(settings.fastForward.limiter == 0) limiterAmount.item(0).setSelected();
|
||||||
|
if(settings.fastForward.limiter == 2) limiterAmount.item(1).setSelected();
|
||||||
|
if(settings.fastForward.limiter == 3) limiterAmount.item(2).setSelected();
|
||||||
|
if(settings.fastForward.limiter == 4) limiterAmount.item(3).setSelected();
|
||||||
|
if(settings.fastForward.limiter == 5) limiterAmount.item(4).setSelected();
|
||||||
|
if(settings.fastForward.limiter == 6) limiterAmount.item(5).setSelected();
|
||||||
|
if(settings.fastForward.limiter == 7) limiterAmount.item(6).setSelected();
|
||||||
|
if(settings.fastForward.limiter == 8) limiterAmount.item(7).setSelected();
|
||||||
|
limiterAmount.onChange([&] {
|
||||||
|
auto index = limiterAmount.selected().offset();
|
||||||
|
if(index == 0) settings.fastForward.limiter = 0;
|
||||||
|
if(index == 1) settings.fastForward.limiter = 2;
|
||||||
|
if(index == 2) settings.fastForward.limiter = 3;
|
||||||
|
if(index == 3) settings.fastForward.limiter = 4;
|
||||||
|
if(index == 4) settings.fastForward.limiter = 5;
|
||||||
|
if(index == 5) settings.fastForward.limiter = 6;
|
||||||
|
if(index == 6) settings.fastForward.limiter = 7;
|
||||||
|
if(index == 7) settings.fastForward.limiter = 8;
|
||||||
|
});
|
||||||
|
|
||||||
|
fastForwardMute.setText("Mute while fast forwarding").setChecked(settings.fastForward.mute).onToggle([&] {
|
||||||
|
settings.fastForward.mute = fastForwardMute.checked();
|
||||||
|
});
|
||||||
|
|
||||||
|
rewindLabel.setText("Rewind").setFont(Font().setBold());
|
||||||
|
|
||||||
|
rewindFrequencyLabel.setText("Frequency:");
|
||||||
|
rewindFrequencyOption.append(ComboButtonItem().setText("Disabled"));
|
||||||
|
rewindFrequencyOption.append(ComboButtonItem().setText("Every 10 frames"));
|
||||||
|
rewindFrequencyOption.append(ComboButtonItem().setText("Every 20 frames"));
|
||||||
|
rewindFrequencyOption.append(ComboButtonItem().setText("Every 30 frames"));
|
||||||
|
rewindFrequencyOption.append(ComboButtonItem().setText("Every 40 frames"));
|
||||||
|
rewindFrequencyOption.append(ComboButtonItem().setText("Every 50 frames"));
|
||||||
|
rewindFrequencyOption.append(ComboButtonItem().setText("Every 60 frames"));
|
||||||
|
if(settings.rewind.frequency == 0) rewindFrequencyOption.item(0).setSelected();
|
||||||
|
if(settings.rewind.frequency == 10) rewindFrequencyOption.item(1).setSelected();
|
||||||
|
if(settings.rewind.frequency == 20) rewindFrequencyOption.item(2).setSelected();
|
||||||
|
if(settings.rewind.frequency == 30) rewindFrequencyOption.item(3).setSelected();
|
||||||
|
if(settings.rewind.frequency == 40) rewindFrequencyOption.item(4).setSelected();
|
||||||
|
if(settings.rewind.frequency == 50) rewindFrequencyOption.item(5).setSelected();
|
||||||
|
if(settings.rewind.frequency == 60) rewindFrequencyOption.item(6).setSelected();
|
||||||
|
rewindFrequencyOption.onChange([&] {
|
||||||
|
settings.rewind.frequency = rewindFrequencyOption.selected().offset() * 10;
|
||||||
|
program.rewindReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
rewindLengthLabel.setText("Length:");
|
||||||
|
rewindLengthOption.append(ComboButtonItem().setText( "10 states"));
|
||||||
|
rewindLengthOption.append(ComboButtonItem().setText( "20 states"));
|
||||||
|
rewindLengthOption.append(ComboButtonItem().setText( "40 states"));
|
||||||
|
rewindLengthOption.append(ComboButtonItem().setText( "80 states"));
|
||||||
|
rewindLengthOption.append(ComboButtonItem().setText("160 states"));
|
||||||
|
rewindLengthOption.append(ComboButtonItem().setText("320 states"));
|
||||||
|
if(settings.rewind.length == 10) rewindLengthOption.item(0).setSelected();
|
||||||
|
if(settings.rewind.length == 20) rewindLengthOption.item(1).setSelected();
|
||||||
|
if(settings.rewind.length == 40) rewindLengthOption.item(2).setSelected();
|
||||||
|
if(settings.rewind.length == 80) rewindLengthOption.item(3).setSelected();
|
||||||
|
if(settings.rewind.length == 160) rewindLengthOption.item(4).setSelected();
|
||||||
|
if(settings.rewind.length == 320) rewindLengthOption.item(5).setSelected();
|
||||||
|
rewindLengthOption.onChange([&] {
|
||||||
|
settings.rewind.length = 10 << rewindLengthOption.selected().offset();
|
||||||
|
program.rewindReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
rewindMute.setText("Mute while rewinding").setChecked(settings.rewind.mute).onToggle([&] {
|
||||||
|
settings.rewind.mute = rewindMute.checked();
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,8 +1,6 @@
|
||||||
auto VideoSettings::create() -> void {
|
auto VideoSettings::create() -> void {
|
||||||
setIcon(Icon::Device::Display);
|
setCollapsible();
|
||||||
setText("Video");
|
setVisible(false);
|
||||||
|
|
||||||
layout.setPadding(5_sx);
|
|
||||||
|
|
||||||
colorAdjustmentLabel.setFont(Font().setBold()).setText("Color Adjustment");
|
colorAdjustmentLabel.setFont(Font().setBold()).setText("Color Adjustment");
|
||||||
colorLayout.setSize({3, 3});
|
colorLayout.setSize({3, 3});
|
||||||
|
@ -32,13 +30,6 @@ auto VideoSettings::create() -> void {
|
||||||
program.updateVideoPalette();
|
program.updateVideoPalette();
|
||||||
}).doChange();
|
}).doChange();
|
||||||
|
|
||||||
fastForwardFrameSkip.setText("Skip frames while fast forwarding").setChecked(settings.video.fastForwardFrameSkip).setToolTip({
|
|
||||||
"When using the fast forward hotkey, this option will enable a frame skip of 9.\n"
|
|
||||||
"Frame skipping while fast forwarding allows a higher maximum frame skipping frame rate."
|
|
||||||
}).onToggle([&] {
|
|
||||||
settings.video.fastForwardFrameSkip = fastForwardFrameSkip.checked();
|
|
||||||
});
|
|
||||||
|
|
||||||
snowOption.setText("Draw snow effect when idle").setChecked(settings.video.snow).onToggle([&] {
|
snowOption.setText("Draw snow effect when idle").setChecked(settings.video.snow).onToggle([&] {
|
||||||
settings.video.snow = snowOption.checked();
|
settings.video.snow = snowOption.checked();
|
||||||
presentation.updateProgramIcon();
|
presentation.updateProgramIcon();
|
||||||
|
|
|
@ -110,14 +110,13 @@ auto CheatWindow::doAccept() -> void {
|
||||||
//
|
//
|
||||||
|
|
||||||
auto CheatEditor::create() -> void {
|
auto CheatEditor::create() -> void {
|
||||||
setIcon(Icon::Edit::Replace);
|
setCollapsible();
|
||||||
setText("Cheat Editor");
|
setVisible(false);
|
||||||
|
|
||||||
layout.setPadding(5_sx);
|
|
||||||
cheatList.setBatchable();
|
cheatList.setBatchable();
|
||||||
cheatList.setHeadered();
|
cheatList.setHeadered();
|
||||||
cheatList.setSortable();
|
cheatList.setSortable();
|
||||||
cheatList.onActivate([&] {
|
cheatList.onActivate([&](auto cell) {
|
||||||
//kind of a hack: toggling a cheat code twice quickly (onToggle) will call onActivate.
|
//kind of a hack: toggling a cheat code twice quickly (onToggle) will call onActivate.
|
||||||
//do not trigger the CheatWindow unless it's been at least two seconds since a cheat code was last toggled on or off.
|
//do not trigger the CheatWindow unless it's been at least two seconds since a cheat code was last toggled on or off.
|
||||||
if(chrono::timestamp() - activateTimeout < 2) return;
|
if(chrono::timestamp() - activateTimeout < 2) return;
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
auto CheatFinder::create() -> void {
|
auto CheatFinder::create() -> void {
|
||||||
setIcon(Icon::Edit::Find);
|
setCollapsible();
|
||||||
setText("Cheat Finder");
|
setVisible(false);
|
||||||
|
|
||||||
layout.setPadding(5_sx);
|
|
||||||
searchList.setHeadered();
|
searchList.setHeadered();
|
||||||
searchValue.onActivate([&] { eventScan(); });
|
searchValue.onActivate([&] { eventScan(); });
|
||||||
searchLabel.setText("Value:");
|
searchLabel.setText("Value:");
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
auto ManifestViewer::create() -> void {
|
auto ManifestViewer::create() -> void {
|
||||||
setIcon(Icon::Emblem::Text);
|
setCollapsible();
|
||||||
setText("Manifest Viewer");
|
setVisible(false);
|
||||||
|
|
||||||
layout.setPadding(5_sx);
|
|
||||||
manifestLabel.setText("Manifest:");
|
manifestLabel.setText("Manifest:");
|
||||||
manifestOption.onChange([&] { selectManifest(); });
|
manifestOption.onChange([&] { selectManifest(); });
|
||||||
manifestSpacer.setColor({192, 192, 192});
|
manifestSpacer.setColor({192, 192, 192});
|
||||||
|
|
|
@ -47,15 +47,14 @@ auto StateWindow::doAccept() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto StateManager::create() -> void {
|
auto StateManager::create() -> void {
|
||||||
setIcon(Icon::Application::FileManager);
|
setCollapsible();
|
||||||
setText("State Manager");
|
setVisible(false);
|
||||||
|
|
||||||
layout.setPadding(5_sx);
|
|
||||||
stateLayout.setAlignment(0.0);
|
stateLayout.setAlignment(0.0);
|
||||||
stateList.setBatchable();
|
stateList.setBatchable();
|
||||||
stateList.setHeadered();
|
stateList.setHeadered();
|
||||||
stateList.setSortable();
|
stateList.setSortable();
|
||||||
stateList.onActivate([&] { loadButton.doActivate(); });
|
stateList.onActivate([&](auto cell) { loadButton.doActivate(); });
|
||||||
stateList.onChange([&] { updateSelection(); });
|
stateList.onChange([&] { updateSelection(); });
|
||||||
stateList.onSort([&](TableViewColumn column) {
|
stateList.onSort([&](TableViewColumn column) {
|
||||||
column.setSorting(column.sorting() == Sort::Ascending ? Sort::Descending : Sort::Ascending);
|
column.setSorting(column.sorting() == Sort::Ascending ? Sort::Descending : Sort::Ascending);
|
||||||
|
@ -68,8 +67,9 @@ auto StateManager::create() -> void {
|
||||||
categoryOption.append(ComboButtonItem().setText("Managed States").setProperty("type", "Managed/"));
|
categoryOption.append(ComboButtonItem().setText("Managed States").setProperty("type", "Managed/"));
|
||||||
categoryOption.append(ComboButtonItem().setText("Quick States").setProperty("type", "Quick/"));
|
categoryOption.append(ComboButtonItem().setText("Quick States").setProperty("type", "Quick/"));
|
||||||
categoryOption.onChange([&] { loadStates(); });
|
categoryOption.onChange([&] { loadStates(); });
|
||||||
statePreviewSeparator.setColor({192, 192, 192});
|
statePreviewSeparator1.setColor({192, 192, 192});
|
||||||
statePreviewLabel.setFont(Font().setBold()).setText("Preview");
|
statePreviewLabel.setFont(Font().setBold()).setText("Preview");
|
||||||
|
statePreviewSeparator2.setColor({192, 192, 192});
|
||||||
loadButton.setText("Load").onActivate([&] {
|
loadButton.setText("Load").onActivate([&] {
|
||||||
if(auto item = stateList.selected()) program.loadState(item.property("name"));
|
if(auto item = stateList.selected()) program.loadState(item.property("name"));
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,11 +4,11 @@
|
||||||
#include "state-manager.cpp"
|
#include "state-manager.cpp"
|
||||||
#include "manifest-viewer.cpp"
|
#include "manifest-viewer.cpp"
|
||||||
namespace Instances { Instance<CheatDatabase> cheatDatabase; }
|
namespace Instances { Instance<CheatDatabase> cheatDatabase; }
|
||||||
|
CheatFinder cheatFinder;
|
||||||
CheatDatabase& cheatDatabase = Instances::cheatDatabase();
|
CheatDatabase& cheatDatabase = Instances::cheatDatabase();
|
||||||
namespace Instances { Instance<CheatWindow> cheatWindow; }
|
namespace Instances { Instance<CheatWindow> cheatWindow; }
|
||||||
CheatWindow& cheatWindow = Instances::cheatWindow();
|
CheatWindow& cheatWindow = Instances::cheatWindow();
|
||||||
CheatEditor cheatEditor;
|
CheatEditor cheatEditor;
|
||||||
CheatFinder cheatFinder;
|
|
||||||
namespace Instances { Instance<StateWindow> stateWindow; }
|
namespace Instances { Instance<StateWindow> stateWindow; }
|
||||||
StateWindow& stateWindow = Instances::stateWindow();
|
StateWindow& stateWindow = Instances::stateWindow();
|
||||||
StateManager stateManager;
|
StateManager stateManager;
|
||||||
|
@ -16,20 +16,52 @@ ManifestViewer manifestViewer;
|
||||||
namespace Instances { Instance<ToolsWindow> toolsWindow; }
|
namespace Instances { Instance<ToolsWindow> toolsWindow; }
|
||||||
ToolsWindow& toolsWindow = Instances::toolsWindow();
|
ToolsWindow& toolsWindow = Instances::toolsWindow();
|
||||||
|
|
||||||
|
struct ToolsHome : VerticalLayout {
|
||||||
|
ToolsHome() {
|
||||||
|
setCollapsible();
|
||||||
|
setVisible(false);
|
||||||
|
image icon{Resource::Icon};
|
||||||
|
auto data = icon.data();
|
||||||
|
for(uint y : range(icon.height())) {
|
||||||
|
for(uint x : range(icon.width())) {
|
||||||
|
auto pixel = icon.read(data);
|
||||||
|
auto a = pixel >> 24 & 255;
|
||||||
|
auto r = pixel >> 16 & 255;
|
||||||
|
auto g = pixel >> 8 & 255;
|
||||||
|
auto b = pixel >> 0 & 255;
|
||||||
|
a = a * 0.25;
|
||||||
|
icon.write(data, a << 24 | r << 16 | g << 8 | b << 0);
|
||||||
|
data += icon.stride();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canvas.setIcon(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Canvas canvas{this, Size{~0, ~0}};
|
||||||
|
} toolsHome;
|
||||||
|
|
||||||
auto ToolsWindow::create() -> void {
|
auto ToolsWindow::create() -> void {
|
||||||
layout.setPadding(5_sx);
|
layout.setPadding(5_sx);
|
||||||
panel.append(cheatFinder);
|
panelList.append(ListViewItem().setText("Cheat Finder").setIcon(Icon::Action::Search));
|
||||||
panel.append(cheatEditor);
|
panelList.append(ListViewItem().setText("Cheat Editor").setIcon(Icon::Edit::Replace));
|
||||||
panel.append(stateManager);
|
panelList.append(ListViewItem().setText("State Manager").setIcon(Icon::Application::FileManager));
|
||||||
panel.append(manifestViewer);
|
panelList.append(ListViewItem().setText("Manifest Viewer").setIcon(Icon::Emblem::Text));
|
||||||
panel.onChange([&] {
|
panelList.onChange([&] {
|
||||||
uint offset = panel.selected().offset();
|
if(auto item = panelList.selected()) {
|
||||||
if(offset != 1) cheatDatabase.setVisible(false), cheatWindow.setVisible(false);
|
show(item.offset());
|
||||||
if(offset != 2) stateWindow.setVisible(false);
|
} else {
|
||||||
|
show(-1);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
panelContainer.append(toolsHome, Size{~0, ~0});
|
||||||
|
panelContainer.append(cheatFinder, Size{~0, ~0});
|
||||||
|
panelContainer.append(cheatEditor, Size{~0, ~0});
|
||||||
|
panelContainer.append(stateManager, Size{~0, ~0});
|
||||||
|
panelContainer.append(manifestViewer, Size{~0, ~0});
|
||||||
|
|
||||||
setTitle("Tools");
|
setTitle("Tools");
|
||||||
setSize({720_sx, 480_sx});
|
setSize({720_sx, 400_sx});
|
||||||
setAlignment({1.0, 1.0});
|
setAlignment({1.0, 1.0});
|
||||||
setDismissable();
|
setDismissable();
|
||||||
|
|
||||||
|
@ -50,8 +82,21 @@ auto ToolsWindow::setVisible(bool visible) -> ToolsWindow& {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ToolsWindow::show(uint index) -> void {
|
auto ToolsWindow::show(int index) -> void {
|
||||||
panel.item(index).setSelected();
|
toolsHome.setVisible(false);
|
||||||
|
cheatFinder.setVisible(false);
|
||||||
|
cheatEditor.setVisible(false);
|
||||||
|
stateManager.setVisible(false);
|
||||||
|
manifestViewer.setVisible(false);
|
||||||
|
panelList.item(index).setSelected();
|
||||||
|
if(index ==-1) toolsHome.setVisible(true);
|
||||||
|
if(index == 0) cheatFinder.setVisible(true);
|
||||||
|
if(index == 1) cheatEditor.setVisible(true);
|
||||||
|
if(index == 2) stateManager.setVisible(true);
|
||||||
|
if(index == 3) manifestViewer.setVisible(true);
|
||||||
|
if(index != 1) cheatDatabase.setVisible(false), cheatWindow.setVisible(false);
|
||||||
|
if(index != 2) stateWindow.setVisible(false);
|
||||||
|
panelContainer.resize();
|
||||||
setVisible();
|
setVisible();
|
||||||
setFocused();
|
setFocused();
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ struct CheatCandidate {
|
||||||
uint32_t span;
|
uint32_t span;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CheatFinder : TabFrameItem {
|
struct CheatFinder : VerticalLayout {
|
||||||
auto create() -> void;
|
auto create() -> void;
|
||||||
auto restart() -> void;
|
auto restart() -> void;
|
||||||
auto refresh() -> void;
|
auto refresh() -> void;
|
||||||
|
@ -18,9 +18,8 @@ struct CheatFinder : TabFrameItem {
|
||||||
public:
|
public:
|
||||||
vector<CheatCandidate> candidates;
|
vector<CheatCandidate> candidates;
|
||||||
|
|
||||||
VerticalLayout layout{this};
|
TableView searchList{this, Size{~0, ~0}};
|
||||||
TableView searchList{&layout, Size{~0, ~0}};
|
HorizontalLayout controlLayout{this, Size{~0, 0}};
|
||||||
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
|
||||||
Label searchLabel{&controlLayout, Size{0, 0}};
|
Label searchLabel{&controlLayout, Size{0, 0}};
|
||||||
LineEdit searchValue{&controlLayout, Size{~0, 0}};
|
LineEdit searchValue{&controlLayout, Size{~0, 0}};
|
||||||
ComboButton searchSize{&controlLayout, Size{0, 0}};
|
ComboButton searchSize{&controlLayout, Size{0, 0}};
|
||||||
|
@ -79,7 +78,7 @@ public:
|
||||||
Button cancelButton{&controlLayout, Size{80_sx, 0}};
|
Button cancelButton{&controlLayout, Size{80_sx, 0}};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CheatEditor : TabFrameItem {
|
struct CheatEditor : VerticalLayout {
|
||||||
auto create() -> void;
|
auto create() -> void;
|
||||||
auto refresh() -> void;
|
auto refresh() -> void;
|
||||||
auto addCheat(Cheat cheat) -> void;
|
auto addCheat(Cheat cheat) -> void;
|
||||||
|
@ -93,9 +92,8 @@ public:
|
||||||
vector<Cheat> cheats;
|
vector<Cheat> cheats;
|
||||||
uint64_t activateTimeout = 0;
|
uint64_t activateTimeout = 0;
|
||||||
|
|
||||||
VerticalLayout layout{this};
|
TableView cheatList{this, Size{~0, ~0}};
|
||||||
TableView cheatList{&layout, Size{~0, ~0}};
|
HorizontalLayout controlLayout{this, Size{~0, 0}};
|
||||||
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
|
||||||
Button findCheatsButton{&controlLayout, Size{120_sx, 0}};
|
Button findCheatsButton{&controlLayout, Size{120_sx, 0}};
|
||||||
Widget spacer{&controlLayout, Size{~0, 0}};
|
Widget spacer{&controlLayout, Size{~0, 0}};
|
||||||
CheckLabel enableCheats{&controlLayout, Size{0, 0}};
|
CheckLabel enableCheats{&controlLayout, Size{0, 0}};
|
||||||
|
@ -121,7 +119,7 @@ public:
|
||||||
Button cancelButton{&controlLayout, Size{80_sx, 0}};
|
Button cancelButton{&controlLayout, Size{80_sx, 0}};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StateManager : TabFrameItem, Lock {
|
struct StateManager : VerticalLayout, Lock {
|
||||||
auto create() -> void;
|
auto create() -> void;
|
||||||
auto type() const -> string;
|
auto type() const -> string;
|
||||||
auto loadStates() -> void;
|
auto loadStates() -> void;
|
||||||
|
@ -139,17 +137,18 @@ public:
|
||||||
DateDescending,
|
DateDescending,
|
||||||
} sortBy = SortBy::NameAscending;
|
} sortBy = SortBy::NameAscending;
|
||||||
|
|
||||||
VerticalLayout layout{this};
|
HorizontalLayout stateLayout{this, Size{~0, ~0}};
|
||||||
HorizontalLayout stateLayout{&layout, Size{~0, ~0}};
|
|
||||||
TableView stateList{&stateLayout, Size{~0, ~0}};
|
TableView stateList{&stateLayout, Size{~0, ~0}};
|
||||||
VerticalLayout previewLayout{&stateLayout, Size{0, ~0}};
|
VerticalLayout previewLayout{&stateLayout, Size{0, ~0}};
|
||||||
HorizontalLayout categoryLayout{&previewLayout, Size{~0, 0}};
|
HorizontalLayout categoryLayout{&previewLayout, Size{~0, 0}};
|
||||||
Label categoryLabel{&categoryLayout, Size{0, 0}};
|
Label categoryLabel{&categoryLayout, Size{0, 0}};
|
||||||
ComboButton categoryOption{&categoryLayout, Size{~0, 0}};
|
ComboButton categoryOption{&categoryLayout, Size{~0, 0}};
|
||||||
Canvas statePreviewSeparator{&previewLayout, Size{~0, 1}};
|
Canvas statePreviewSeparator1{&previewLayout, Size{~0, 1}};
|
||||||
Label statePreviewLabel{&previewLayout, Size{~0, 0}};
|
Label statePreviewLabel{&previewLayout, Size{~0, 0}};
|
||||||
Canvas statePreview{&previewLayout, Size{256, 224}};
|
Canvas statePreview{&previewLayout, Size{256, 224}};
|
||||||
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
Widget stateSpacer{&previewLayout, Size{~0, ~0}};
|
||||||
|
Canvas statePreviewSeparator2{&previewLayout, Size{~0, 1}};
|
||||||
|
HorizontalLayout controlLayout{this, Size{~0, 0}};
|
||||||
Button loadButton{&controlLayout, Size{80_sx, 0}};
|
Button loadButton{&controlLayout, Size{80_sx, 0}};
|
||||||
Button saveButton{&controlLayout, Size{80_sx, 0}};
|
Button saveButton{&controlLayout, Size{80_sx, 0}};
|
||||||
Widget spacer{&controlLayout, Size{~0, 0}};
|
Widget spacer{&controlLayout, Size{~0, 0}};
|
||||||
|
@ -158,35 +157,36 @@ public:
|
||||||
Button removeButton{&controlLayout, Size{80_sx, 0}};
|
Button removeButton{&controlLayout, Size{80_sx, 0}};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ManifestViewer : TabFrameItem {
|
struct ManifestViewer : VerticalLayout {
|
||||||
auto create() -> void;
|
auto create() -> void;
|
||||||
auto loadManifest() -> void;
|
auto loadManifest() -> void;
|
||||||
auto selectManifest() -> void;
|
auto selectManifest() -> void;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VerticalLayout layout{this};
|
HorizontalLayout manifestLayout{this, Size{~0, 0}};
|
||||||
HorizontalLayout manifestLayout{&layout, Size{~0, 0}};
|
|
||||||
Label manifestLabel{&manifestLayout, Size{0, 0}};
|
Label manifestLabel{&manifestLayout, Size{0, 0}};
|
||||||
ComboButton manifestOption{&manifestLayout, Size{~0, 0}};
|
ComboButton manifestOption{&manifestLayout, Size{~0, 0}};
|
||||||
Canvas manifestSpacer{&layout, Size{~0, 1}};
|
Canvas manifestSpacer{this, Size{~0, 1}};
|
||||||
HorizontalLayout informationLayout{&layout, Size{~0, 0}};
|
HorizontalLayout informationLayout{this, Size{~0, 0}};
|
||||||
Canvas typeIcon{&informationLayout, Size{16, 16}};
|
Canvas typeIcon{&informationLayout, Size{16, 16}};
|
||||||
Label nameLabel{&informationLayout, Size{~0, 0}};
|
Label nameLabel{&informationLayout, Size{~0, 0}};
|
||||||
#if 0 && defined(Hiro_SourceEdit)
|
#if 0 && defined(Hiro_SourceEdit)
|
||||||
SourceEdit manifestView{&layout, Size{~0, ~0}};
|
SourceEdit manifestView{this, Size{~0, ~0}};
|
||||||
#else
|
#else
|
||||||
TextEdit manifestView{&layout, Size{~0, ~0}};
|
TextEdit manifestView{this, Size{~0, ~0}};
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ToolsWindow : Window {
|
struct ToolsWindow : Window {
|
||||||
auto create() -> void;
|
auto create() -> void;
|
||||||
auto setVisible(bool visible = true) -> ToolsWindow&;
|
auto setVisible(bool visible = true) -> ToolsWindow&;
|
||||||
auto show(uint index) -> void;
|
auto show(int index) -> void;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VerticalLayout layout{this};
|
VerticalLayout layout{this};
|
||||||
TabFrame panel{&layout, Size{~0, ~0}};
|
HorizontalLayout panelLayout{&layout, Size{~0, ~0}};
|
||||||
|
ListView panelList{&panelLayout, Size{160_sx, ~0}};
|
||||||
|
VerticalLayout panelContainer{&panelLayout, Size{~0, ~0}};
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace Instances { extern Instance<CheatDatabase> cheatDatabase; }
|
namespace Instances { extern Instance<CheatDatabase> cheatDatabase; }
|
||||||
|
|
|
@ -37,24 +37,24 @@ ifneq ($(filter $(platform),linux bsd),)
|
||||||
|
|
||||||
ifeq ($(hiro),gtk2)
|
ifeq ($(hiro),gtk2)
|
||||||
hiro.flags = $(flags.cpp) -DHIRO_GTK=2 $(shell pkg-config --cflags gtk+-2.0 gtksourceview-2.0)
|
hiro.flags = $(flags.cpp) -DHIRO_GTK=2 $(shell pkg-config --cflags gtk+-2.0 gtksourceview-2.0)
|
||||||
hiro.options = -lX11 $(shell pkg-config --libs gtk+-2.0 gtksourceview-2.0)
|
hiro.options = -L/usr/local/lib -lX11 $(shell pkg-config --libs gtk+-2.0 gtksourceview-2.0)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(hiro),gtk3)
|
ifeq ($(hiro),gtk3)
|
||||||
hiro.flags = $(flags.cpp) -DHIRO_GTK=3 $(shell pkg-config --cflags gtk+-3.0 gtksourceview-3.0) -Wno-deprecated-declarations
|
hiro.flags = $(flags.cpp) -DHIRO_GTK=3 $(shell pkg-config --cflags gtk+-3.0 gtksourceview-3.0) -Wno-deprecated-declarations
|
||||||
hiro.options = -lX11 $(shell pkg-config --libs gtk+-3.0 gtksourceview-3.0)
|
hiro.options = -L/usr/local/lib -lX11 $(shell pkg-config --libs gtk+-3.0 gtksourceview-3.0)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(hiro),qt4)
|
ifeq ($(hiro),qt4)
|
||||||
moc = /usr/local/lib/qt4/bin/moc
|
moc = /usr/local/lib/qt4/bin/moc
|
||||||
hiro.flags = $(flags.cpp) -DHIRO_QT=4 $(shell pkg-config --cflags QtCore QtGui)
|
hiro.flags = $(flags.cpp) -DHIRO_QT=4 $(shell pkg-config --cflags QtCore QtGui)
|
||||||
hiro.options = -lX11 $(shell pkg-config --libs QtCore QtGui)
|
hiro.options = -L/usr/local/lib -lX11 $(shell pkg-config --libs QtCore QtGui)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(hiro),qt5)
|
ifeq ($(hiro),qt5)
|
||||||
moc = /usr/local/lib/qt5/bin/moc
|
moc = /usr/local/lib/qt5/bin/moc
|
||||||
hiro.flags = $(flags.cpp) -DHIRO_QT=5 -fPIC $(shell pkg-config --cflags Qt5Core Qt5Gui Qt5Widgets)
|
hiro.flags = $(flags.cpp) -DHIRO_QT=5 -fPIC $(shell pkg-config --cflags Qt5Core Qt5Gui Qt5Widgets)
|
||||||
hiro.options = -lX11 $(shell pkg-config --libs Qt5Core Qt5Gui Qt5Widgets)
|
hiro.options = -L/usr/local/lib -lX11 $(shell pkg-config --libs Qt5Core Qt5Gui Qt5Widgets)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
|
@ -111,7 +111,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
-(IBAction) activate:(id)sender {
|
-(IBAction) activate:(id)sender {
|
||||||
tableView->doActivate();
|
tableView->doActivate({});
|
||||||
}
|
}
|
||||||
|
|
||||||
-(IBAction) doubleAction:(id)sender {
|
-(IBAction) doubleAction:(id)sender {
|
||||||
|
|
|
@ -700,7 +700,7 @@ struct TableView : sTableView {
|
||||||
auto column(uint position) const { return self().column(position); }
|
auto column(uint position) const { return self().column(position); }
|
||||||
auto columnCount() const { return self().columnCount(); }
|
auto columnCount() const { return self().columnCount(); }
|
||||||
auto columns() const { return self().columns(); }
|
auto columns() const { return self().columns(); }
|
||||||
auto doActivate() const { return self().doActivate(); }
|
auto doActivate(sTableViewCell cell) const { return self().doActivate(cell); }
|
||||||
auto doChange() const { return self().doChange(); }
|
auto doChange() const { return self().doChange(); }
|
||||||
auto doContext() const { return self().doContext(); }
|
auto doContext() const { return self().doContext(); }
|
||||||
auto doEdit(sTableViewCell cell) const { return self().doEdit(cell); }
|
auto doEdit(sTableViewCell cell) const { return self().doEdit(cell); }
|
||||||
|
@ -711,7 +711,7 @@ struct TableView : sTableView {
|
||||||
auto item(unsigned position) const { return self().item(position); }
|
auto item(unsigned position) const { return self().item(position); }
|
||||||
auto itemCount() const { return self().itemCount(); }
|
auto itemCount() const { return self().itemCount(); }
|
||||||
auto items() const { return self().items(); }
|
auto items() const { return self().items(); }
|
||||||
auto onActivate(const function<void ()>& callback = {}) { return self().onActivate(callback), *this; }
|
auto onActivate(const function<void (TableViewCell)>& callback = {}) { return self().onActivate(callback), *this; }
|
||||||
auto onChange(const function<void ()>& callback = {}) { return self().onChange(callback), *this; }
|
auto onChange(const function<void ()>& callback = {}) { return self().onChange(callback), *this; }
|
||||||
auto onContext(const function<void ()>& callback = {}) { return self().onContext(callback), *this; }
|
auto onContext(const function<void ()>& callback = {}) { return self().onContext(callback), *this; }
|
||||||
auto onEdit(const function<void (TableViewCell)>& callback = {}) { return self().onEdit(callback), *this; }
|
auto onEdit(const function<void (TableViewCell)>& callback = {}) { return self().onEdit(callback), *this; }
|
||||||
|
|
|
@ -65,8 +65,8 @@ auto mTableView::columns() const -> vector<TableViewColumn> {
|
||||||
return columns;
|
return columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto mTableView::doActivate() const -> void {
|
auto mTableView::doActivate(sTableViewCell cell) const -> void {
|
||||||
if(state.onActivate) return state.onActivate();
|
if(state.onActivate) return state.onActivate(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto mTableView::doChange() const -> void {
|
auto mTableView::doChange() const -> void {
|
||||||
|
@ -112,7 +112,7 @@ auto mTableView::items() const -> vector<TableViewItem> {
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto mTableView::onActivate(const function<void ()>& callback) -> type& {
|
auto mTableView::onActivate(const function<void (TableViewCell)>& callback) -> type& {
|
||||||
state.onActivate = callback;
|
state.onActivate = callback;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ struct mTableView : mWidget {
|
||||||
auto column(uint position) const -> TableViewColumn;
|
auto column(uint position) const -> TableViewColumn;
|
||||||
auto columnCount() const -> uint;
|
auto columnCount() const -> uint;
|
||||||
auto columns() const -> vector<TableViewColumn>;
|
auto columns() const -> vector<TableViewColumn>;
|
||||||
auto doActivate() const -> void;
|
auto doActivate(sTableViewCell cell) const -> void;
|
||||||
auto doChange() const -> void;
|
auto doChange() const -> void;
|
||||||
auto doContext() const -> void;
|
auto doContext() const -> void;
|
||||||
auto doEdit(sTableViewCell cell) const -> void;
|
auto doEdit(sTableViewCell cell) const -> void;
|
||||||
|
@ -24,7 +24,7 @@ struct mTableView : mWidget {
|
||||||
auto item(uint position) const -> TableViewItem;
|
auto item(uint position) const -> TableViewItem;
|
||||||
auto itemCount() const -> uint;
|
auto itemCount() const -> uint;
|
||||||
auto items() const -> vector<TableViewItem>;
|
auto items() const -> vector<TableViewItem>;
|
||||||
auto onActivate(const function<void ()>& callback = {}) -> type&;
|
auto onActivate(const function<void (TableViewCell)>& callback = {}) -> type&;
|
||||||
auto onChange(const function<void ()>& callback = {}) -> type&;
|
auto onChange(const function<void ()>& callback = {}) -> type&;
|
||||||
auto onContext(const function<void ()>& callback = {}) -> type&;
|
auto onContext(const function<void ()>& callback = {}) -> type&;
|
||||||
auto onEdit(const function<void (TableViewCell)>& callback = {}) -> type&;
|
auto onEdit(const function<void (TableViewCell)>& callback = {}) -> type&;
|
||||||
|
@ -59,7 +59,7 @@ struct mTableView : mWidget {
|
||||||
Color foregroundColor;
|
Color foregroundColor;
|
||||||
bool headered = false;
|
bool headered = false;
|
||||||
vector<sTableViewItem> items;
|
vector<sTableViewItem> items;
|
||||||
function<void ()> onActivate;
|
function<void (TableViewCell)> onActivate;
|
||||||
function<void ()> onChange;
|
function<void ()> onChange;
|
||||||
function<void ()> onContext;
|
function<void ()> onContext;
|
||||||
function<void (TableViewCell)> onEdit;
|
function<void (TableViewCell)> onEdit;
|
||||||
|
|
|
@ -11,7 +11,7 @@ struct BrowserDialogWindow {
|
||||||
auto isFolder(const string& name) -> bool;
|
auto isFolder(const string& name) -> bool;
|
||||||
auto isMatch(const string& name) -> bool;
|
auto isMatch(const string& name) -> bool;
|
||||||
auto run() -> BrowserDialog::Response;
|
auto run() -> BrowserDialog::Response;
|
||||||
auto setPath(string path) -> void;
|
auto setPath(string path, const string& contains = "") -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Window window;
|
Window window;
|
||||||
|
@ -24,7 +24,8 @@ private:
|
||||||
Button pathUp{&pathLayout, Size{0, 0}, 0};
|
Button pathUp{&pathLayout, Size{0, 0}, 0};
|
||||||
ListView view{&layout, Size{~0, ~0}, 5_sx};
|
ListView view{&layout, Size{~0, ~0}, 5_sx};
|
||||||
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
||||||
ComboButton filterList{&controlLayout, Size{0, 0}, 5_sx};
|
ComboButton filterList{&controlLayout, Size{0, 0}, 0};
|
||||||
|
Button searchButton{&controlLayout, Size{0, 0}, 0};
|
||||||
LineEdit fileName{&controlLayout, Size{~0, 0}, 5_sx};
|
LineEdit fileName{&controlLayout, Size{~0, 0}, 5_sx};
|
||||||
ComboButton optionList{&controlLayout, Size{0, 0}, 5_sx};
|
ComboButton optionList{&controlLayout, Size{0, 0}, 5_sx};
|
||||||
Button acceptButton{&controlLayout, Size{80_sx, 0}, 5_sx};
|
Button acceptButton{&controlLayout, Size{80_sx, 0}, 5_sx};
|
||||||
|
@ -212,12 +213,17 @@ auto BrowserDialogWindow::run() -> BrowserDialog::Response {
|
||||||
optionList.append(ComboButtonItem().setText(option));
|
optionList.append(ComboButtonItem().setText(option));
|
||||||
}
|
}
|
||||||
optionList.doChange(); //updates response.option to point to the default (first) option
|
optionList.doChange(); //updates response.option to point to the default (first) option
|
||||||
fileName.setVisible(state.action == "saveFile").onActivate([&] { accept(); }).onChange([&] {
|
image iconSearch{Icon::Action::Search};
|
||||||
|
iconSearch.scale(16_sx, 16_sy);
|
||||||
|
searchButton.setIcon(iconSearch).setBordered(false).onActivate([&] { setPath(state.path, fileName.text()); });
|
||||||
|
fileName.onActivate([&] {
|
||||||
|
if(state.action == "saveFile") return accept();
|
||||||
|
setPath(state.path, fileName.text());
|
||||||
|
}).onChange([&] {
|
||||||
auto name = fileName.text();
|
auto name = fileName.text();
|
||||||
acceptButton.setEnabled(name && !isFolder(name));
|
if(state.action == "saveFile") acceptButton.setEnabled(name && !isFolder(name));
|
||||||
fileName.setBackgroundColor(acceptButton.enabled() ? Color{} : Color{255, 224, 224});
|
|
||||||
});
|
});
|
||||||
acceptButton.onActivate([&] { accept(); });
|
acceptButton.setEnabled(false).onActivate([&] { accept(); });
|
||||||
if(state.action.beginsWith("open")) acceptButton.setText(tr("Open"));
|
if(state.action.beginsWith("open")) acceptButton.setText(tr("Open"));
|
||||||
if(state.action.beginsWith("save")) acceptButton.setText(tr("Save"));
|
if(state.action.beginsWith("save")) acceptButton.setText(tr("Save"));
|
||||||
if(state.action.beginsWith("select")) acceptButton.setText(tr("Select"));
|
if(state.action.beginsWith("select")) acceptButton.setText(tr("Select"));
|
||||||
|
@ -326,7 +332,7 @@ auto BrowserDialogWindow::run() -> BrowserDialog::Response {
|
||||||
window.setAlignment(state.relativeTo, state.alignment);
|
window.setAlignment(state.relativeTo, state.alignment);
|
||||||
window.setDismissable();
|
window.setDismissable();
|
||||||
window.setVisible();
|
window.setVisible();
|
||||||
view.setFocused();
|
fileName.setFocused();
|
||||||
Application::processEvents();
|
Application::processEvents();
|
||||||
view->resizeColumns();
|
view->resizeColumns();
|
||||||
window.setModal();
|
window.setModal();
|
||||||
|
@ -335,7 +341,7 @@ auto BrowserDialogWindow::run() -> BrowserDialog::Response {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto BrowserDialogWindow::setPath(string path) -> void {
|
auto BrowserDialogWindow::setPath(string path, const string& contains) -> void {
|
||||||
path.transform("\\", "/");
|
path.transform("\\", "/");
|
||||||
if((path || Path::root() == "/") && !path.endsWith("/")) path.append("/");
|
if((path || Path::root() == "/") && !path.endsWith("/")) path.append("/");
|
||||||
pathName.setText(state.path = path);
|
pathName.setText(state.path = path);
|
||||||
|
@ -365,13 +371,14 @@ auto BrowserDialogWindow::setPath(string path) -> void {
|
||||||
if(state.action == "openFolder") continue;
|
if(state.action == "openFolder") continue;
|
||||||
}
|
}
|
||||||
if(!isMatch(content)) continue;
|
if(!isMatch(content)) continue;
|
||||||
|
if(contains && !content.ifind(contains)) continue;
|
||||||
if(!showHiddenOption.checked() && file::hidden({state.path, content})) continue;
|
if(!showHiddenOption.checked() && file::hidden({state.path, content})) continue;
|
||||||
view.append(ListViewItem().setText(content).setIcon(isFolder ? (image)Icon::Action::Open : (image)Icon::Emblem::File));
|
view.append(ListViewItem().setText(content).setIcon(isFolder ? (image)Icon::Action::Open : (image)Icon::Emblem::File));
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::processEvents();
|
Application::processEvents();
|
||||||
view->resizeColumns(); //todo: on Windows, adding items may add vertical scrollbar; this hack corrects column width
|
view->resizeColumns(); //todo: on Windows, adding items may add vertical scrollbar; this hack corrects column width
|
||||||
view.setFocused().doChange();
|
view.doChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#if defined(Hiro_ListView)
|
#if defined(Hiro_ListView)
|
||||||
|
|
||||||
mListView::mListView() {
|
mListView::mListView() {
|
||||||
mTableView::onActivate([&] { doActivate(); });
|
mTableView::onActivate([&](auto) { doActivate(); });
|
||||||
mTableView::onChange([&] { doChange(); });
|
mTableView::onChange([&] { doChange(); });
|
||||||
mTableView::onContext([&] { doContext(); });
|
mTableView::onContext([&] { doContext(); });
|
||||||
mTableView::onToggle([&](TableViewCell cell) {
|
mTableView::onToggle([&](TableViewCell cell) {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace hiro {
|
namespace hiro {
|
||||||
|
|
||||||
static auto TableView_activate(GtkTreeView*, GtkTreePath*, GtkTreeViewColumn*, pTableView* p) -> void { return p->_doActivate(); }
|
static auto TableView_activate(GtkTreeView*, GtkTreePath* gtkRow, GtkTreeViewColumn* gtkColumn, pTableView* p) -> void { return p->_doActivate(gtkRow, gtkColumn); }
|
||||||
static auto TableView_buttonEvent(GtkTreeView* treeView, GdkEventButton* event, pTableView* p) -> signed { return p->_doEvent(event); }
|
static auto TableView_buttonEvent(GtkTreeView* treeView, GdkEventButton* event, pTableView* p) -> signed { return p->_doEvent(event); }
|
||||||
static auto TableView_change(GtkTreeSelection*, pTableView* p) -> void { return p->_doChange(); }
|
static auto TableView_change(GtkTreeSelection*, pTableView* p) -> void { return p->_doChange(); }
|
||||||
static auto TableView_edit(GtkCellRendererText* renderer, const char* path, const char* text, pTableView* p) -> void { return p->_doEdit(renderer, path, text); }
|
static auto TableView_edit(GtkCellRendererText* renderer, const char* path, const char* text, pTableView* p) -> void { return p->_doEdit(renderer, path, text); }
|
||||||
|
@ -238,8 +238,26 @@ auto pTableView::_createModel() -> void {
|
||||||
gtk_tree_view_set_model(gtkTreeView, gtkTreeModel);
|
gtk_tree_view_set_model(gtkTreeView, gtkTreeModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pTableView::_doActivate() -> void {
|
auto pTableView::_doActivate(GtkTreePath* gtkRow, GtkTreeViewColumn* gtkColumn) -> void {
|
||||||
if(!locked()) self().doActivate();
|
if(locked()) return;
|
||||||
|
|
||||||
|
if(gtkRow && gtkColumn) {
|
||||||
|
auto path = gtk_tree_path_to_string(gtkRow);
|
||||||
|
auto item = self().item(toNatural(path));
|
||||||
|
auto cell = item.cell(0);
|
||||||
|
for(auto& column : state().columns) {
|
||||||
|
if(auto self = column->self()) {
|
||||||
|
if(self->gtkColumn == gtkColumn) {
|
||||||
|
cell = item.cell(column->offset());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_free(path);
|
||||||
|
self().doActivate(cell);
|
||||||
|
} else {
|
||||||
|
self().doActivate({});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pTableView::_doChange() -> void {
|
auto pTableView::_doChange() -> void {
|
||||||
|
|
|
@ -25,7 +25,7 @@ struct pTableView : pWidget {
|
||||||
auto _cellWidth(uint row, uint column) -> uint;
|
auto _cellWidth(uint row, uint column) -> uint;
|
||||||
auto _columnWidth(uint column) -> uint;
|
auto _columnWidth(uint column) -> uint;
|
||||||
auto _createModel() -> void;
|
auto _createModel() -> void;
|
||||||
auto _doActivate() -> void;
|
auto _doActivate(GtkTreePath* = nullptr, GtkTreeViewColumn* = nullptr) -> void;
|
||||||
auto _doChange() -> void;
|
auto _doChange() -> void;
|
||||||
auto _doContext() -> void;
|
auto _doContext() -> void;
|
||||||
auto _doDataFunc(GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeIter* iter) -> void;
|
auto _doDataFunc(GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeIter* iter) -> void;
|
||||||
|
|
|
@ -252,7 +252,7 @@ public:
|
||||||
auto showEvent(QShowEvent*) -> void override;
|
auto showEvent(QShowEvent*) -> void override;
|
||||||
pTableView& p;
|
pTableView& p;
|
||||||
public slots:
|
public slots:
|
||||||
void onActivate();
|
void onActivate(QTreeWidgetItem* item, int column);
|
||||||
void onChange();
|
void onChange();
|
||||||
void onContext();
|
void onContext();
|
||||||
void onSort(int column);
|
void onSort(int column);
|
||||||
|
|
|
@ -1328,20 +1328,19 @@ static const uint qt_meta_data_hiro__QtTableView[] = {
|
||||||
0, // signalCount
|
0, // signalCount
|
||||||
|
|
||||||
// slots: signature, parameters, type, tag, flags
|
// slots: signature, parameters, type, tag, flags
|
||||||
18, 31, 31, 31, 0x0a,
|
18, 51, 63, 63, 0x0a,
|
||||||
32, 31, 31, 31, 0x0a,
|
64, 63, 63, 63, 0x0a,
|
||||||
43, 31, 31, 31, 0x0a,
|
75, 63, 63, 63, 0x0a,
|
||||||
55, 67, 31, 31, 0x0a,
|
87, 99, 63, 63, 0x0a,
|
||||||
74, 105, 31, 31, 0x0a,
|
106, 51, 63, 63, 0x0a,
|
||||||
|
|
||||||
0 // eod
|
0 // eod
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char qt_meta_stringdata_hiro__QtTableView[] = {
|
static const char qt_meta_stringdata_hiro__QtTableView[] = {
|
||||||
"hiro::QtTableView\0onActivate()\0\0"
|
"hiro::QtTableView\0onActivate(QTreeWidgetItem*,int)\0"
|
||||||
"onChange()\0onContext()\0onSort(int)\0"
|
"item,column\0\0onChange()\0onContext()\0"
|
||||||
"column\0onToggle(QTreeWidgetItem*,int)\0"
|
"onSort(int)\0column\0onToggle(QTreeWidgetItem*,int)\0"
|
||||||
"item,column\0"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void hiro::QtTableView::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
|
void hiro::QtTableView::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
|
||||||
|
@ -1350,7 +1349,7 @@ void hiro::QtTableView::qt_static_metacall(QObject *_o, QMetaObject::Call _c, in
|
||||||
Q_ASSERT(staticMetaObject.cast(_o));
|
Q_ASSERT(staticMetaObject.cast(_o));
|
||||||
QtTableView *_t = static_cast<QtTableView *>(_o);
|
QtTableView *_t = static_cast<QtTableView *>(_o);
|
||||||
switch (_id) {
|
switch (_id) {
|
||||||
case 0: _t->onActivate(); break;
|
case 0: _t->onActivate((*reinterpret_cast< QTreeWidgetItem*(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break;
|
||||||
case 1: _t->onChange(); break;
|
case 1: _t->onChange(); break;
|
||||||
case 2: _t->onContext(); break;
|
case 2: _t->onContext(); break;
|
||||||
case 3: _t->onSort((*reinterpret_cast< int(*)>(_a[1]))); break;
|
case 3: _t->onSort((*reinterpret_cast< int(*)>(_a[1]))); break;
|
||||||
|
|
|
@ -17,7 +17,7 @@ auto pTableView::construct() -> void {
|
||||||
qtTableViewDelegate = new QtTableViewDelegate(*this);
|
qtTableViewDelegate = new QtTableViewDelegate(*this);
|
||||||
qtTableView->setItemDelegate(qtTableViewDelegate);
|
qtTableView->setItemDelegate(qtTableViewDelegate);
|
||||||
|
|
||||||
qtTableView->connect(qtTableView, SIGNAL(itemActivated(QTreeWidgetItem*, int)), SLOT(onActivate()));
|
qtTableView->connect(qtTableView, SIGNAL(itemActivated(QTreeWidgetItem*, int)), SLOT(onActivate(QTreeWidgetItem*, int)));
|
||||||
qtTableView->connect(qtTableView, SIGNAL(itemSelectionChanged()), SLOT(onChange()));
|
qtTableView->connect(qtTableView, SIGNAL(itemSelectionChanged()), SLOT(onChange()));
|
||||||
qtTableView->connect(qtTableView, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(onContext()));
|
qtTableView->connect(qtTableView, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(onContext()));
|
||||||
qtTableView->connect(qtTableView->header(), SIGNAL(sectionClicked(int)), SLOT(onSort(int)));
|
qtTableView->connect(qtTableView->header(), SIGNAL(sectionClicked(int)), SLOT(onSort(int)));
|
||||||
|
@ -186,8 +186,20 @@ auto pTableView::_widthOfCell(unsigned _row, unsigned _column) -> unsigned {
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto QtTableView::onActivate() -> void {
|
auto QtTableView::onActivate(QTreeWidgetItem* qtItem, int column) -> void {
|
||||||
if(!p.locked()) p.self().doActivate();
|
if(p.locked()) return;
|
||||||
|
|
||||||
|
for(auto& item : p.state().items) {
|
||||||
|
if(auto self = item->self()) {
|
||||||
|
if(qtItem == self->qtItem) {
|
||||||
|
if(auto cell = item->cell(column)) {
|
||||||
|
return p.self().doActivate(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.self().doActivate({});
|
||||||
}
|
}
|
||||||
|
|
||||||
auto QtTableView::onChange() -> void {
|
auto QtTableView::onChange() -> void {
|
||||||
|
|
|
@ -453,7 +453,7 @@ static auto CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT ms
|
||||||
}
|
}
|
||||||
|
|
||||||
case AppMessage::TableView_onActivate: {
|
case AppMessage::TableView_onActivate: {
|
||||||
if(auto tableView = (mTableView*)lparam) tableView->doActivate();
|
if(auto tableView = (mTableView*)lparam) tableView->doActivate({});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,11 @@ auto locate(string name) -> string {
|
||||||
string location = {Path::program(), name};
|
string location = {Path::program(), name};
|
||||||
if(inode::exists(location)) return location;
|
if(inode::exists(location)) return location;
|
||||||
|
|
||||||
directory::create({Path::userData(), "bsnes/"});
|
location = {Path::userData(), "bsnes/"};
|
||||||
return {Path::userData(), "bsnes/", name};
|
if(inode::exists(location)) return location;
|
||||||
|
|
||||||
|
directory::create({Path::userSettings(), "bsnes/"});
|
||||||
|
return {Path::userSettings(), "bsnes/", name};
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "settings.cpp"
|
#include "settings.cpp"
|
||||||
|
|
|
@ -74,7 +74,7 @@ struct VideoXShm : VideoDriver {
|
||||||
auto output(uint width = 0, uint height = 0) -> void override {
|
auto output(uint width = 0, uint height = 0) -> void override {
|
||||||
uint windowWidth, windowHeight;
|
uint windowWidth, windowHeight;
|
||||||
size(windowWidth, windowHeight);
|
size(windowWidth, windowHeight);
|
||||||
if(!_image) return;
|
if(!_image || !_inputBuffer || !_outputBuffer) return;
|
||||||
|
|
||||||
if(!width) width = _outputWidth;
|
if(!width) width = _outputWidth;
|
||||||
if(!height) height = _outputHeight;
|
if(!height) height = _outputHeight;
|
||||||
|
|
Loading…
Reference in New Issue