mirror of https://github.com/bsnes-emu/bsnes.git
Update to v096r08 release.
byuu says: Changelog: - FC: scanline emulation support added - SFC: balanced profile compiles again - SFC: performance profile compiles again - GB,GBC: more fixes to pass blargg's 07, 08, 11 APU tests - tomoko: added input loss { pause, allow-input } options - tomoko: refactored settings video menu options to { Video Scale, Video Emulation, Video Shader } - icarus: connected { About, Preferences, Quit } application menu options
This commit is contained in:
parent
cec33c1d0f
commit
12df278c5b
|
@ -6,7 +6,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "096.07";
|
||||
static const string Version = "096.08";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -169,16 +169,19 @@ auto Interface::cheatSet(const lstring& list) -> void {
|
|||
|
||||
auto Interface::cap(const string& name) -> bool {
|
||||
if(name == "Color Emulation") return true;
|
||||
if(name == "Scanline Emulation") return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::get(const string& name) -> any {
|
||||
if(name == "Color Emulation") return settings.colorEmulation;
|
||||
if(name == "Scanline Emulation") return settings.scanlineEmulation;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Interface::set(const string& name, const any& value) -> bool {
|
||||
if(name == "Color Emulation" && value.is<bool>()) return settings.colorEmulation = value.get<bool>(), true;
|
||||
if(name == "Scanline Emulation" && value.is<bool>()) return settings.scanlineEmulation = value.get<bool>(), true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ private:
|
|||
|
||||
struct Settings {
|
||||
bool colorEmulation = true;
|
||||
bool scanlineEmulation = true;
|
||||
};
|
||||
|
||||
extern Interface* interface;
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace Famicom {
|
|||
Video video;
|
||||
|
||||
Video::Video() {
|
||||
output = new uint32[256 * 240];
|
||||
output = new uint32[256 * 480];
|
||||
paletteStandard = new uint32[1 << 9];
|
||||
paletteEmulation = new uint32[1 << 9];
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ Video::~Video() {
|
|||
}
|
||||
|
||||
auto Video::reset() -> void {
|
||||
memory::fill(output, 256 * 240);
|
||||
memory::fill(output, 256 * 480);
|
||||
|
||||
for(auto color : range(1 << 9)) {
|
||||
paletteStandard[color] = generateColor(color, 2.0, 0.0, 1.0, 1.0, 2.2);
|
||||
|
@ -30,15 +30,28 @@ auto Video::reset() -> void {
|
|||
auto Video::refresh() -> void {
|
||||
auto palette = settings.colorEmulation ? paletteEmulation : paletteStandard;
|
||||
|
||||
for(uint y = 0; y < 240; y++) {
|
||||
auto source = ppu.buffer + y * 256;
|
||||
auto target = output + y * 256;
|
||||
for(uint x = 0; x < 256; x++) {
|
||||
*target++ = palette[*source++];
|
||||
if(settings.scanlineEmulation) {
|
||||
for(uint y = 0; y < 240; y++) {
|
||||
auto source = ppu.buffer + y * 256;
|
||||
auto targetLo = output + y * 512;
|
||||
auto targetHi = output + y * 512 + 256;
|
||||
for(uint x = 0; x < 256; x++) {
|
||||
auto color = palette[*source++];
|
||||
*targetLo++ = color;
|
||||
*targetHi++ = (255 << 24) | ((color & 0xfefefe) >> 1);
|
||||
}
|
||||
}
|
||||
interface->videoRefresh(output, 256 * sizeof(uint32), 256, 480);
|
||||
} else {
|
||||
for(uint y = 0; y < 240; y++) {
|
||||
auto source = ppu.buffer + y * 256;
|
||||
auto target = output + y * 256;
|
||||
for(uint x = 0; x < 256; x++) {
|
||||
*target++ = palette[*source++];
|
||||
}
|
||||
}
|
||||
interface->videoRefresh(output, 256 * sizeof(uint32), 256, 240);
|
||||
}
|
||||
|
||||
interface->videoRefresh(output, 4 * 256, 256, 240);
|
||||
}
|
||||
|
||||
auto Video::generateColor(
|
||||
|
|
|
@ -79,7 +79,6 @@ auto APU::power() -> void {
|
|||
}
|
||||
|
||||
auto APU::mmio_read(uint16 addr) -> uint8 {
|
||||
//if(!master.enable && addr != 0xff26) return 0xff;
|
||||
if(addr >= 0xff10 && addr <= 0xff14) return square1.read(addr);
|
||||
if(addr >= 0xff15 && addr <= 0xff19) return square2.read(addr);
|
||||
if(addr >= 0xff1a && addr <= 0xff1e) return wave.read(addr);
|
||||
|
@ -90,7 +89,18 @@ auto APU::mmio_read(uint16 addr) -> uint8 {
|
|||
}
|
||||
|
||||
auto APU::mmio_write(uint16 addr, uint8 data) -> void {
|
||||
if(!master.enable && addr != 0xff26) return;
|
||||
if(!master.enable) {
|
||||
bool valid = addr == 0xff26; //NR52
|
||||
if(!system.cgb()) {
|
||||
//NRx1 length is writable only on DMG/SGB; not on CGB
|
||||
if(addr == 0xff11) valid = true, data &= 0x3f; //NR11; duty is not writable (remains 0)
|
||||
if(addr == 0xff16) valid = true, data &= 0x3f; //NR21; duty is not writable (remains 0)
|
||||
if(addr == 0xff1b) valid = true; //NR31
|
||||
if(addr == 0xff20) valid = true; //NR41
|
||||
}
|
||||
if(!valid) return;
|
||||
}
|
||||
|
||||
if(addr >= 0xff10 && addr <= 0xff14) return square1.write(addr, data);
|
||||
if(addr >= 0xff15 && addr <= 0xff19) return square2.write(addr, data);
|
||||
if(addr >= 0xff1a && addr <= 0xff1e) return wave.write(addr, data);
|
||||
|
|
|
@ -68,10 +68,10 @@ auto APU::Master::read(uint16 addr) -> uint8 {
|
|||
|
||||
auto APU::Master::write(uint16 addr, uint8 data) -> void {
|
||||
if(addr == 0xff24) { //NR50
|
||||
leftEnable = data & 0x80;
|
||||
leftVolume = (data >> 4) & 7;
|
||||
rightEnable = data & 0x08;
|
||||
rightVolume = (data >> 0) & 7;
|
||||
leftEnable = (uint1)(data >> 7);
|
||||
leftVolume = (uint3)(data >> 4);
|
||||
rightEnable = (uint1)(data >> 3);
|
||||
rightVolume = (uint3)(data >> 0);
|
||||
}
|
||||
|
||||
if(addr == 0xff25) { //NR51
|
||||
|
@ -86,14 +86,19 @@ auto APU::Master::write(uint16 addr, uint8 data) -> void {
|
|||
}
|
||||
|
||||
if(addr == 0xff26) { //NR52
|
||||
enable = data & 0x80;
|
||||
if(!enable) {
|
||||
//power(bool) resets length counters when true (eg for CGB only)
|
||||
apu.square1.power(system.cgb());
|
||||
apu.square2.power(system.cgb());
|
||||
apu.wave.power(system.cgb());
|
||||
apu.noise.power(system.cgb());
|
||||
power();
|
||||
if(enable != (bool)(data & 0x80)) {
|
||||
enable = data & 0x80;
|
||||
|
||||
if(!enable) {
|
||||
//power(bool) resets length counters when true (eg for CGB only)
|
||||
apu.square1.power(system.cgb());
|
||||
apu.square2.power(system.cgb());
|
||||
apu.wave.power(system.cgb());
|
||||
apu.noise.power(system.cgb());
|
||||
power();
|
||||
} else {
|
||||
apu.phase = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ auto APU::Noise::run() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
uint4 sample = (lfsr & 1) ? (uint4)0 : volume;
|
||||
uint4 sample = lfsr & 1 ? 0 : (uint)volume;
|
||||
if(!enable) sample = 0;
|
||||
|
||||
output = sample;
|
||||
|
@ -30,7 +30,7 @@ auto APU::Noise::clockLength() -> void {
|
|||
|
||||
auto APU::Noise::clockEnvelope() -> void {
|
||||
if(enable && envelopeFrequency && --envelopePeriod == 0) {
|
||||
envelopePeriod = envelopeFrequency;
|
||||
envelopePeriod = envelopeFrequency ? (uint)envelopeFrequency : 8;
|
||||
if(envelopeDirection == 0 && volume > 0) volume--;
|
||||
if(envelopeDirection == 1 && volume < 15) volume++;
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ auto APU::Noise::write(uint16 addr, uint8 data) -> void {
|
|||
if(initialize) {
|
||||
enable = dacEnable();
|
||||
lfsr = -1;
|
||||
envelopePeriod = envelopeFrequency;
|
||||
envelopePeriod = envelopeFrequency ? (uint)envelopeFrequency : 8;
|
||||
volume = envelopeVolume;
|
||||
|
||||
if(!length) {
|
||||
|
|
|
@ -14,7 +14,7 @@ auto APU::Square1::run() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
uint4 sample = (dutyOutput ? volume : (uint4)0);
|
||||
uint4 sample = dutyOutput ? (uint)volume : 0;
|
||||
if(!enable) sample = 0;
|
||||
|
||||
output = sample;
|
||||
|
@ -54,7 +54,7 @@ auto APU::Square1::clockSweep() -> void {
|
|||
|
||||
auto APU::Square1::clockEnvelope() -> void {
|
||||
if(enable && envelopeFrequency && --envelopePeriod == 0) {
|
||||
envelopePeriod = envelopeFrequency;
|
||||
envelopePeriod = envelopeFrequency ? (uint)envelopeFrequency : 8;
|
||||
if(envelopeDirection == 0 && volume > 0) volume--;
|
||||
if(envelopeDirection == 1 && volume < 15) volume++;
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ auto APU::Square1::write(uint16 addr, uint8 data) -> void {
|
|||
if(initialize) {
|
||||
enable = dacEnable();
|
||||
period = 2 * (2048 - frequency);
|
||||
envelopePeriod = envelopeFrequency;
|
||||
envelopePeriod = envelopeFrequency ? (uint)envelopeFrequency : 8;
|
||||
volume = envelopeVolume;
|
||||
|
||||
if(!length) {
|
||||
|
|
|
@ -14,7 +14,7 @@ auto APU::Square2::run() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
uint4 sample = (dutyOutput ? volume : (uint4)0);
|
||||
uint4 sample = dutyOutput ? (uint)volume : 0;
|
||||
if(!enable) sample = 0;
|
||||
|
||||
output = sample;
|
||||
|
@ -28,7 +28,7 @@ auto APU::Square2::clockLength() -> void {
|
|||
|
||||
auto APU::Square2::clockEnvelope() -> void {
|
||||
if(enable && envelopeFrequency && --envelopePeriod == 0) {
|
||||
envelopePeriod = envelopeFrequency;
|
||||
envelopePeriod = envelopeFrequency ? (uint)envelopeFrequency : 8;
|
||||
if(envelopeDirection == 0 && volume > 0) volume--;
|
||||
if(envelopeDirection == 1 && volume < 15) volume++;
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ auto APU::Square2::write(uint16 addr, uint8 data) -> void {
|
|||
if(initialize) {
|
||||
enable = dacEnable();
|
||||
period = 2 * (2048 - frequency);
|
||||
envelopePeriod = envelopeFrequency;
|
||||
envelopePeriod = envelopeFrequency ? (uint)envelopeFrequency : 8;
|
||||
volume = envelopeVolume;
|
||||
|
||||
if(!length) {
|
||||
|
|
|
@ -10,7 +10,7 @@ auto APU::Wave::run() -> void {
|
|||
|
||||
static const uint shift[] = {4, 0, 1, 2}; //0%, 100%, 50%, 25%
|
||||
uint4 sample = patternSample >> shift[volume];
|
||||
if(enable == false) sample = 0;
|
||||
if(!enable) sample = 0;
|
||||
|
||||
output = sample;
|
||||
}
|
||||
|
|
|
@ -60,7 +60,6 @@ auto CPU::scanline() -> void {
|
|||
synchronizeSMP();
|
||||
synchronizePPU();
|
||||
synchronizeCoprocessors();
|
||||
system.scanline();
|
||||
|
||||
if(vcounter() == 0) hdma_init();
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ PPU ppu;
|
|||
#include "serialization.cpp"
|
||||
|
||||
PPU::PPU() {
|
||||
surface = new uint32[512 * 512];
|
||||
output = surface + 16 * 512;
|
||||
output = new uint32[512 * 512]();
|
||||
output += 16 * 512; //overscan offset
|
||||
|
||||
alloc_tiledata_cache();
|
||||
|
||||
|
@ -38,7 +38,8 @@ PPU::PPU() {
|
|||
}
|
||||
|
||||
PPU::~PPU() {
|
||||
delete[] surface;
|
||||
output -= 16 * 512;
|
||||
delete[] output;
|
||||
free_tiledata_cache();
|
||||
}
|
||||
|
||||
|
@ -128,6 +129,10 @@ auto PPU::scanline() -> void {
|
|||
if(!regs.mosaic_countdown) regs.mosaic_countdown = regs.mosaic_size + 1;
|
||||
regs.mosaic_countdown--;
|
||||
}
|
||||
|
||||
if(line == 241) {
|
||||
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
||||
}
|
||||
}
|
||||
|
||||
auto PPU::render_scanline() -> void {
|
||||
|
@ -139,8 +144,6 @@ auto PPU::render_scanline() -> void {
|
|||
}
|
||||
|
||||
auto PPU::frame() -> void {
|
||||
system.frame();
|
||||
|
||||
if(field() == 0) {
|
||||
display.interlace = regs.interlace;
|
||||
regs.scanlines = (regs.overscan == false) ? 224 : 239;
|
||||
|
@ -375,7 +378,7 @@ auto PPU::power() -> void {
|
|||
auto PPU::reset() -> void {
|
||||
create(Enter, system.cpuFrequency());
|
||||
PPUcounter::reset();
|
||||
memset(surface, 0, 512 * 512 * sizeof(uint32));
|
||||
memory::fill(output, 512 * 480 * sizeof(uint32));
|
||||
|
||||
frame();
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ struct PPU : Thread, public PPUcounter {
|
|||
|
||||
alwaysinline auto interlace() const -> bool { return display.interlace; }
|
||||
alwaysinline auto overscan() const -> bool { return display.overscan; }
|
||||
alwaysinline auto hires() const -> bool { return (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6); }
|
||||
|
||||
auto render_line() -> void;
|
||||
auto update_oam_status() -> void;
|
||||
|
@ -42,7 +41,6 @@ struct PPU : Thread, public PPUcounter {
|
|||
uint8 oam[544];
|
||||
uint8 cgram[512];
|
||||
|
||||
uint32* surface;
|
||||
uint32* output;
|
||||
|
||||
uint ppu1_version = 1;
|
||||
|
|
|
@ -85,32 +85,23 @@ inline auto PPU::get_pixel_swap(uint32 x) -> uint16 {
|
|||
}
|
||||
|
||||
inline auto PPU::render_line_output() -> void {
|
||||
uint32* ptr = (uint32*)output + (line * 1024) + ((interlace() && field()) ? 512 : 0);
|
||||
uint32 curr, prev;
|
||||
auto ptr = (uint32*)output + (line * 1024) + ((interlace() && field()) ? 512 : 0);
|
||||
|
||||
if(!regs.pseudo_hires && regs.bg_mode != 5 && regs.bg_mode != 6) {
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
curr = (regs.display_brightness << 15) | get_pixel_normal(x);
|
||||
*ptr++ = curr;
|
||||
for(uint x = 0; x < 256; x++) {
|
||||
uint color = (regs.display_brightness << 15) | get_pixel_normal(x);
|
||||
*ptr++ = color;
|
||||
*ptr++ = color;
|
||||
}
|
||||
} else {
|
||||
for(unsigned x = 0, prev = 0; x < 256; x++) {
|
||||
//blending is disabled below, as this should be done via video filtering
|
||||
//blending code is left for reference purposes
|
||||
|
||||
curr = (regs.display_brightness << 15) | get_pixel_swap(x);
|
||||
*ptr++ = curr; //(prev + curr - ((prev ^ curr) & 0x0421)) >> 1;
|
||||
//prev = curr;
|
||||
|
||||
curr = (regs.display_brightness << 15) | get_pixel_normal(x);
|
||||
*ptr++ = curr; //(prev + curr - ((prev ^ curr) & 0x0421)) >> 1;
|
||||
//prev = curr;
|
||||
for(uint x = 0; x < 256; x++) {
|
||||
*ptr++ = (regs.display_brightness << 15) | get_pixel_swap(x);
|
||||
*ptr++ = (regs.display_brightness << 15) | get_pixel_normal(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline auto PPU::render_line_clear() -> void {
|
||||
uint32* ptr = (uint32*)output + (line * 1024) + ((interlace() && field()) ? 512 : 0);
|
||||
uint width = (!regs.pseudo_hires && regs.bg_mode != 5 && regs.bg_mode != 6) ? 256 : 512;
|
||||
memset(ptr, 0, width * 2 * sizeof(uint32));
|
||||
auto ptr = (uint32*)output + (line * 1024) + ((interlace() && field()) ? 512 : 0);
|
||||
memory::fill(ptr, 512 * sizeof(uint32));
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ auto PPU::latch_counters() -> void {
|
|||
|
||||
auto PPU::interlace() const -> bool { return display.interlace; }
|
||||
auto PPU::overscan() const -> bool { return display.overscan; }
|
||||
auto PPU::hires() const -> bool { return regs.pseudo_hires || regs.bgmode == 5 || regs.bgmode == 6; }
|
||||
|
||||
auto PPU::get_vram_addr() -> uint16 {
|
||||
uint16 addr = regs.vram_addr;
|
||||
|
|
|
@ -20,8 +20,8 @@ bg3(*this, Background::ID::BG3),
|
|||
bg4(*this, Background::ID::BG4),
|
||||
sprite(*this),
|
||||
screen(*this) {
|
||||
surface = new uint32[512 * 512];
|
||||
output = surface + 16 * 512;
|
||||
output = new uint32[512 * 512]();
|
||||
output += 16 * 512; //overscan offset
|
||||
display.width = 256;
|
||||
display.height = 224;
|
||||
display.frameskip = 0;
|
||||
|
@ -29,7 +29,8 @@ screen(*this) {
|
|||
}
|
||||
|
||||
PPU::~PPU() {
|
||||
delete[] surface;
|
||||
output -= 16 * 512;
|
||||
delete[] output;
|
||||
}
|
||||
|
||||
auto PPU::step(uint clocks) -> void {
|
||||
|
@ -86,15 +87,18 @@ auto PPU::render_scanline() -> void {
|
|||
}
|
||||
|
||||
auto PPU::scanline() -> void {
|
||||
display.width = !hires() ? 256 : 512;
|
||||
display.width = 512;
|
||||
display.height = !overscan() ? 225 : 240;
|
||||
if(vcounter() == 0) frame();
|
||||
if(vcounter() == display.height && regs.display_disable == false) sprite.address_reset();
|
||||
|
||||
if(vcounter() == 241) {
|
||||
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
||||
}
|
||||
}
|
||||
|
||||
auto PPU::frame() -> void {
|
||||
sprite.frame();
|
||||
system.frame();
|
||||
display.interlace = regs.interlace;
|
||||
display.overscan = regs.overscan;
|
||||
display.framecounter = display.frameskip == 0 ? 0 : (display.framecounter + 1) % display.frameskip;
|
||||
|
@ -118,7 +122,7 @@ auto PPU::power() -> void {
|
|||
auto PPU::reset() -> void {
|
||||
create(Enter, system.cpuFrequency());
|
||||
PPUcounter::reset();
|
||||
memset(surface, 0, 512 * 512 * sizeof(uint32));
|
||||
memset(output, 0, 512 * 480 * sizeof(uint32));
|
||||
mmio_reset();
|
||||
display.interlace = false;
|
||||
display.overscan = false;
|
||||
|
|
|
@ -10,7 +10,6 @@ struct PPU : Thread, public PPUcounter {
|
|||
auto latch_counters() -> void;
|
||||
auto interlace() const -> bool;
|
||||
auto overscan() const -> bool;
|
||||
auto hires() const -> bool;
|
||||
|
||||
auto enter() -> void;
|
||||
auto enable() -> void;
|
||||
|
@ -29,7 +28,6 @@ struct PPU : Thread, public PPUcounter {
|
|||
uint8 cgram[512];
|
||||
|
||||
private:
|
||||
uint32* surface;
|
||||
uint32* output;
|
||||
|
||||
#include "mmio/mmio.hpp"
|
||||
|
|
|
@ -56,9 +56,9 @@ auto PPU::Screen::scanline() -> void {
|
|||
}
|
||||
|
||||
auto PPU::Screen::render_black() -> void {
|
||||
uint32* data = self.output + self.vcounter() * 1024;
|
||||
auto data = self.output + self.vcounter() * 1024;
|
||||
if(self.interlace() && self.field()) data += 512;
|
||||
memset(data, 0, self.display.width << 2);
|
||||
memory::fill(data, 512 * sizeof(uint32));
|
||||
}
|
||||
|
||||
auto PPU::Screen::get_pixel_main(uint x) -> uint16 {
|
||||
|
@ -116,12 +116,14 @@ auto PPU::Screen::get_pixel_sub(uint x) -> uint16 {
|
|||
}
|
||||
|
||||
auto PPU::Screen::render() -> void {
|
||||
uint32* data = self.output + self.vcounter() * 1024;
|
||||
auto data = self.output + self.vcounter() * 1024;
|
||||
if(self.interlace() && self.field()) data += 512;
|
||||
|
||||
if(!self.regs.pseudo_hires && self.regs.bgmode != 5 && self.regs.bgmode != 6) {
|
||||
for(uint i = 0; i < 256; i++) {
|
||||
data[i] = self.regs.display_brightness << 15 | get_pixel_main(i);
|
||||
uint32 color = self.regs.display_brightness << 15 | get_pixel_main(i);
|
||||
*data++ = color;
|
||||
*data++ = color;
|
||||
}
|
||||
} else {
|
||||
for(uint i = 0; i < 256; i++) {
|
||||
|
|
|
@ -42,7 +42,6 @@ auto CPU::scanline() -> void {
|
|||
synchronizeSMP();
|
||||
synchronizePPU();
|
||||
synchronizeCoprocessors();
|
||||
system.scanline();
|
||||
|
||||
if(vcounter() == 0) {
|
||||
//HDMA init triggers once every frame
|
||||
|
|
|
@ -6,10 +6,6 @@ auto PPU::overscan() const -> bool {
|
|||
return display.overscan;
|
||||
}
|
||||
|
||||
auto PPU::hires() const -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto PPU::latch_counters() -> void {
|
||||
cpu.synchronizePPU();
|
||||
regs.hcounter = hdot();
|
||||
|
|
|
@ -139,10 +139,13 @@ auto PPU::scanline() -> void {
|
|||
sprite.scanline();
|
||||
window.scanline();
|
||||
screen.scanline();
|
||||
|
||||
if(vcounter() == 241) {
|
||||
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
||||
}
|
||||
}
|
||||
|
||||
auto PPU::frame() -> void {
|
||||
system.frame();
|
||||
sprite.frame();
|
||||
|
||||
display.interlace = regs.interlace;
|
||||
|
|
|
@ -10,7 +10,6 @@ struct PPU : Thread, public PPUcounter {
|
|||
auto latch_counters() -> void;
|
||||
auto interlace() const -> bool;
|
||||
auto overscan() const -> bool;
|
||||
auto hires() const -> bool;
|
||||
|
||||
auto enter() -> void;
|
||||
auto enable() -> void;
|
||||
|
|
|
@ -37,9 +37,6 @@ auto Audio::coprocessor_sample(int16 lsample, int16 rsample) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto Audio::init() -> void {
|
||||
}
|
||||
|
||||
auto Audio::flush() -> void {
|
||||
while(dsp_length > 0 && cop_length > 0) {
|
||||
uint32 dsp_sample = dsp_buffer[dsp_rdoffset];
|
||||
|
|
|
@ -3,7 +3,6 @@ struct Audio {
|
|||
auto coprocessor_frequency(double frequency) -> void;
|
||||
auto sample(int16 lsample, int16 rsample) -> void;
|
||||
auto coprocessor_sample(int16 lsample, int16 rsample) -> void;
|
||||
auto init() -> void;
|
||||
|
||||
private:
|
||||
auto flush() -> void;
|
||||
|
|
|
@ -89,9 +89,6 @@ auto System::init() -> void {
|
|||
|
||||
bsmemory.init();
|
||||
|
||||
video.init();
|
||||
audio.init();
|
||||
|
||||
device.connect(0, configuration.controllerPort1);
|
||||
device.connect(1, configuration.controllerPort2);
|
||||
}
|
||||
|
@ -249,12 +246,4 @@ auto System::reset() -> void {
|
|||
device.connect(1, configuration.controllerPort2);
|
||||
}
|
||||
|
||||
auto System::scanline() -> void {
|
||||
video.scanline();
|
||||
if(cpu.vcounter() == 241) scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
||||
}
|
||||
|
||||
auto System::frame() -> void {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,9 +19,6 @@ struct System : property<System> {
|
|||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
auto frame() -> void;
|
||||
auto scanline() -> void;
|
||||
|
||||
//return *active* system information (settings are cached upon power-on)
|
||||
readonly<Region> region;
|
||||
readonly<Device::ID> expansionPort;
|
||||
|
|
|
@ -32,9 +32,9 @@ auto Video::reset() -> void {
|
|||
paletteStandard[color] = (255 << 24) | (R << 16) | (G << 8) | (B << 0);
|
||||
}
|
||||
|
||||
{ uint R = L * gamma_ramp[r];
|
||||
uint G = L * gamma_ramp[g];
|
||||
uint B = L * gamma_ramp[b];
|
||||
{ uint R = L * gammaRamp[r];
|
||||
uint G = L * gammaRamp[g];
|
||||
uint B = L * gammaRamp[b];
|
||||
paletteEmulation[color] = (255 << 24) | (R << 16) | (G << 8) | (B << 0);
|
||||
}
|
||||
}
|
||||
|
@ -110,36 +110,7 @@ auto Video::refresh() -> void {
|
|||
|
||||
drawCursors();
|
||||
|
||||
#if defined(PROFILE_ACCURACY)
|
||||
interface->videoRefresh(output - (ppu.overscan() ? 0 : 7 * 1024), 512 * sizeof(uint32), 512, 480);
|
||||
#endif
|
||||
|
||||
#if defined(PROFILE_BALANCED) || defined(PROFILE_PERFORMANCE)
|
||||
if(hires) {
|
||||
//normalize line widths
|
||||
auto data = (uint32*)output;
|
||||
if(ppu.interlace() && ppu.field()) data += 512;
|
||||
|
||||
for(uint y = 0; y < 240; y++) {
|
||||
if(line_width[y] == 512) continue;
|
||||
uint32* buffer = data + y * 1024;
|
||||
for(int x = 255; x >= 0; x--) {
|
||||
buffer[(x * 2) + 0] = buffer[(x * 2) + 1] = buffer[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//overscan: when disabled, shift image down (by scrolling video buffer up) to center image onscreen
|
||||
//(memory before ppu.output is filled with black scanlines)
|
||||
interface->videoRefresh(
|
||||
output - (ppu.overscan() ? 0 : 7 * 1024),
|
||||
4 * (1024 >> ppu.interlace()),
|
||||
256 << hires,
|
||||
240 << ppu.interlace()
|
||||
);
|
||||
|
||||
hires = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
//internal
|
||||
|
@ -152,7 +123,6 @@ auto Video::drawCursor(uint32 color, int x, int y) -> void {
|
|||
int vy = y + cy - 7;
|
||||
if(vy <= 0 || vy >= 240) continue; //do not draw offscreen
|
||||
|
||||
bool hires = (line_width[vy] == 512);
|
||||
for(int cx = 0; cx < 15; cx++) {
|
||||
int vx = x + cx - 7;
|
||||
if(vx < 0 || vx >= 256) continue; //do not draw offscreen
|
||||
|
@ -160,14 +130,10 @@ auto Video::drawCursor(uint32 color, int x, int y) -> void {
|
|||
if(pixel == 0) continue;
|
||||
uint32 pixelcolor = pixel == 1 ? 0xff000000 : color;
|
||||
|
||||
if(!hires) {
|
||||
*(data + vy * 1024 + vx) = pixelcolor;
|
||||
} else {
|
||||
*(data + vy * 1024 + vx * 2 + 0) = pixelcolor;
|
||||
*(data + vy * 1024 + vx * 2 + 1) = pixelcolor;
|
||||
*(data + vy * 1024 + 512 + vx * 2 + 0) = pixelcolor;
|
||||
*(data + vy * 1024 + 512 + vx * 2 + 1) = pixelcolor;
|
||||
}
|
||||
*(data + vy * 1024 + vx * 2 + 0) = pixelcolor;
|
||||
*(data + vy * 1024 + vx * 2 + 1) = pixelcolor;
|
||||
*(data + vy * 1024 + 512 + vx * 2 + 0) = pixelcolor;
|
||||
*(data + vy * 1024 + 512 + vx * 2 + 1) = pixelcolor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -192,21 +158,7 @@ auto Video::drawCursors() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto Video::scanline() -> void {
|
||||
uint y = cpu.vcounter();
|
||||
if(y >= 240) return;
|
||||
|
||||
hires |= ppu.hires();
|
||||
uint width = ppu.hires() ? 512 : 256;
|
||||
line_width[y] = width;
|
||||
}
|
||||
|
||||
auto Video::init() -> void {
|
||||
hires = false;
|
||||
for(auto& n : line_width) n = 256;
|
||||
}
|
||||
|
||||
const uint8 Video::gamma_ramp[32] = {
|
||||
const uint8 Video::gammaRamp[32] = {
|
||||
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
|
||||
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
|
||||
0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0,
|
||||
|
|
|
@ -10,17 +10,10 @@ struct Video {
|
|||
uint32* paletteEmulation = nullptr;
|
||||
|
||||
private:
|
||||
bool hires;
|
||||
uint line_width[240];
|
||||
|
||||
auto update() -> void;
|
||||
auto scanline() -> void;
|
||||
auto init() -> void;
|
||||
|
||||
auto drawCursor(uint32 color, int x, int y) -> void;
|
||||
auto drawCursors() -> void;
|
||||
|
||||
static const uint8 gamma_ramp[32];
|
||||
static const uint8 gammaRamp[32];
|
||||
static const uint8 cursor[15 * 15];
|
||||
|
||||
friend class System;
|
||||
|
|
|
@ -41,6 +41,8 @@ Settings::Settings() {
|
|||
set("Audio/Resampler", "Sinc");
|
||||
|
||||
set("Input/Driver", ruby::Input::optimalDriver());
|
||||
set("Input/FocusLoss/Pause", false);
|
||||
set("Input/FocusLoss/AllowInput", false);
|
||||
|
||||
set("Timing/Video", 60.0);
|
||||
set("Timing/Audio", 48000.0);
|
||||
|
|
|
@ -63,32 +63,33 @@ Presentation::Presentation() {
|
|||
settings["Video/AspectCorrection"].setValue(aspectCorrection.checked());
|
||||
resizeViewport();
|
||||
});
|
||||
videoFilterMenu.setText("Video Filter");
|
||||
if(settings["Video/Filter"].text() == "None") videoFilterNone.setChecked();
|
||||
if(settings["Video/Filter"].text() == "Blur") videoFilterBlur.setChecked();
|
||||
videoFilterNone.setText("None").onActivate([&] {
|
||||
settings["Video/Filter"].setValue("None");
|
||||
program->updateVideoFilter();
|
||||
});
|
||||
videoFilterBlur.setText("Blur").onActivate([&] {
|
||||
settings["Video/Filter"].setValue("Blur");
|
||||
program->updateVideoFilter();
|
||||
});
|
||||
blurEmulation.setText("Blur Emulation").setChecked(settings["Video/BlurEmulation"].boolean()).onToggle([&] {
|
||||
videoEmulationMenu.setText("Video Emulation");
|
||||
blurEmulation.setText("Blurring").setChecked(settings["Video/BlurEmulation"].boolean()).onToggle([&] {
|
||||
settings["Video/BlurEmulation"].setValue(blurEmulation.checked());
|
||||
if(emulator) emulator->set("Blur Emulation", blurEmulation.checked());
|
||||
});
|
||||
colorEmulation.setText("Color Emulation").setChecked(settings["Video/ColorEmulation"].boolean()).onToggle([&] {
|
||||
colorEmulation.setText("Colors").setChecked(settings["Video/ColorEmulation"].boolean()).onToggle([&] {
|
||||
settings["Video/ColorEmulation"].setValue(colorEmulation.checked());
|
||||
if(emulator) emulator->set("Color Emulation", colorEmulation.checked());
|
||||
});
|
||||
scanlineEmulation.setText("Scanline Emulation").setChecked(settings["Video/ScanlineEmulation"].boolean()).onToggle([&] {
|
||||
scanlineEmulation.setText("Scanlines").setChecked(settings["Video/ScanlineEmulation"].boolean()).onToggle([&] {
|
||||
settings["Video/ScanlineEmulation"].setValue(scanlineEmulation.checked());
|
||||
if(emulator) emulator->set("Scanline Emulation", scanlineEmulation.checked());
|
||||
});
|
||||
maskOverscan.setText("Mask Overscan").setChecked(settings["Video/Overscan/Mask"].boolean()).onToggle([&] {
|
||||
settings["Video/Overscan/Mask"].setValue(maskOverscan.checked());
|
||||
});
|
||||
videoShaderMenu.setText("Video Shader");
|
||||
if(settings["Video/Shader"].text() == "None") videoShaderNone.setChecked();
|
||||
if(settings["Video/Shader"].text() == "Blur") videoShaderBlur.setChecked();
|
||||
videoShaderNone.setText("None").onActivate([&] {
|
||||
settings["Video/Shader"].setValue("None");
|
||||
program->updateVideoShader();
|
||||
});
|
||||
videoShaderBlur.setText("Blur").onActivate([&] {
|
||||
settings["Video/Shader"].setValue("Blur");
|
||||
program->updateVideoShader();
|
||||
});
|
||||
loadShaders();
|
||||
synchronizeVideo.setText("Synchronize Video").setChecked(settings["Video/Synchronize"].boolean()).onToggle([&] {
|
||||
settings["Video/Synchronize"].setValue(synchronizeVideo.checked());
|
||||
|
@ -127,6 +128,9 @@ Presentation::Presentation() {
|
|||
manifestViewer.setText("Manifest Viewer").onActivate([&] { toolsManager->show(2); });
|
||||
|
||||
helpMenu.setText("Help");
|
||||
documentation.setText("Documentation ...").onActivate([&] {
|
||||
invoke("http://doc.byuu.org/higan/");
|
||||
});
|
||||
about.setText("About ...").onActivate([&] {
|
||||
MessageDialog().setParent(*this).setTitle("About higan ...").setText({
|
||||
Emulator::Name, " v", Emulator::Version, " (", Emulator::Profile, ")\n\n",
|
||||
|
@ -149,11 +153,14 @@ Presentation::Presentation() {
|
|||
resizeViewport();
|
||||
setCentered();
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
Application::Windows::onModalChange([](bool modal) { if(modal && audio) audio->clear(); });
|
||||
#endif
|
||||
|
||||
#if defined(PLATFORM_MACOSX)
|
||||
showConfigurationSeparator.setVisible(false);
|
||||
showConfiguration.setVisible(false);
|
||||
helpMenu.setVisible(false);
|
||||
|
||||
about.setVisible(false);
|
||||
Application::Cocoa::onAbout([&] { about.doActivate(); });
|
||||
Application::Cocoa::onActivate([&] { setFocused(); });
|
||||
Application::Cocoa::onPreferences([&] { showConfiguration.doActivate(); });
|
||||
|
@ -271,31 +278,23 @@ auto Presentation::drawSplashScreen() -> void {
|
|||
|
||||
auto Presentation::loadShaders() -> void {
|
||||
if(settings["Video/Driver"].text() != "OpenGL") {
|
||||
videoShaderMenu.setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
auto pathname = locate({localpath(), "higan/"}, "Video Shaders/");
|
||||
for(auto shader : directory::folders(pathname, "*.shader")) {
|
||||
if(videoShaders.objectCount() == 2) videoShaderMenu.append(MenuSeparator());
|
||||
MenuRadioItem item{&videoShaderMenu};
|
||||
item.setText(string{shader}.rtrim(".shader/", 1L)).onActivate([=] {
|
||||
settings["Video/Shader"].setValue({pathname, shader});
|
||||
program->updateVideoFilter();
|
||||
program->updateVideoShader();
|
||||
});
|
||||
videoShaders.append(item);
|
||||
}
|
||||
|
||||
videoShaderMenu.setText("Video Shaders");
|
||||
videoShaderNone.setChecked().setText("None").onActivate([=] {
|
||||
settings["Video/Shader"].setValue("None");
|
||||
program->updateVideoFilter();
|
||||
});
|
||||
|
||||
for(auto object : videoShaders.objects()) {
|
||||
if(auto radioItem = dynamic_cast<mMenuRadioItem*>(object.data())) {
|
||||
if(settings["Video/Shader"].text() == string{pathname, radioItem->text(), ".shader/"}) {
|
||||
radioItem->setChecked();
|
||||
}
|
||||
for(auto radioItem : videoShaders.objects<MenuRadioItem>()) {
|
||||
if(settings["Video/Shader"].text() == string{pathname, radioItem.text(), ".shader/"}) {
|
||||
radioItem.setChecked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,18 +27,15 @@ struct Presentation : Window {
|
|||
Group videoScales{&videoScaleSmall, &videoScaleMedium, &videoScaleLarge};
|
||||
MenuSeparator videoScaleSeparator{&videoScaleMenu};
|
||||
MenuCheckItem aspectCorrection{&videoScaleMenu};
|
||||
Menu videoFilterMenu{&settingsMenu};
|
||||
MenuRadioItem videoFilterNone{&videoFilterMenu};
|
||||
MenuRadioItem videoFilterBlur{&videoFilterMenu};
|
||||
Group videoFilters{&videoFilterNone, &videoFilterBlur};
|
||||
MenuSeparator videoFilterSeparator{&videoFilterMenu};
|
||||
MenuCheckItem blurEmulation{&videoFilterMenu};
|
||||
MenuCheckItem colorEmulation{&videoFilterMenu};
|
||||
MenuCheckItem scanlineEmulation{&videoFilterMenu};
|
||||
MenuCheckItem maskOverscan{&videoFilterMenu};
|
||||
Menu videoEmulationMenu{&settingsMenu};
|
||||
MenuCheckItem blurEmulation{&videoEmulationMenu};
|
||||
MenuCheckItem colorEmulation{&videoEmulationMenu};
|
||||
MenuCheckItem scanlineEmulation{&videoEmulationMenu};
|
||||
MenuCheckItem maskOverscan{&videoEmulationMenu};
|
||||
Menu videoShaderMenu{&settingsMenu};
|
||||
MenuRadioItem videoShaderNone{&videoShaderMenu};
|
||||
Group videoShaders{&videoShaderNone};
|
||||
MenuRadioItem videoShaderBlur{&videoShaderMenu};
|
||||
Group videoShaders{&videoShaderNone, &videoShaderBlur};
|
||||
MenuSeparator videoSettingsSeparator{&settingsMenu};
|
||||
MenuCheckItem synchronizeVideo{&settingsMenu};
|
||||
MenuCheckItem synchronizeAudio{&settingsMenu};
|
||||
|
@ -64,6 +61,7 @@ struct Presentation : Window {
|
|||
MenuItem stateManager{&toolsMenu};
|
||||
MenuItem manifestViewer{&toolsMenu};
|
||||
Menu helpMenu{&menuBar};
|
||||
MenuItem documentation{&helpMenu};
|
||||
MenuItem about{&helpMenu};
|
||||
|
||||
FixedLayout layout{this};
|
||||
|
|
|
@ -53,11 +53,7 @@ auto Program::videoRefresh(const uint32* data, uint pitch, uint width, uint heig
|
|||
pitch >>= 2, length >>= 2;
|
||||
|
||||
for(auto y : range(height)) {
|
||||
const uint32* sp = data + y * pitch;
|
||||
uint32* dp = output + y * length;
|
||||
for(auto x : range(width)) {
|
||||
*dp++ = *sp++;
|
||||
}
|
||||
memory::copy(output + y * length, data + y * pitch, width * sizeof(uint32));
|
||||
}
|
||||
|
||||
if(emulator->information.overscan && settings["Video/Overscan/Mask"].boolean()) {
|
||||
|
@ -101,7 +97,7 @@ auto Program::audioSample(int16 lsample, int16 rsample) -> void {
|
|||
}
|
||||
|
||||
auto Program::inputPoll(uint port, uint device, uint input) -> int16 {
|
||||
if(presentation->focused()) {
|
||||
if(presentation->focused() || settings["Input/FocusLoss/AllowInput"].boolean()) {
|
||||
auto guid = emulator->port[port].device[device].input[input].guid;
|
||||
auto mapping = (InputMapping*)guid;
|
||||
if(mapping) return mapping->poll();
|
||||
|
@ -110,7 +106,7 @@ auto Program::inputPoll(uint port, uint device, uint input) -> int16 {
|
|||
}
|
||||
|
||||
auto Program::inputRumble(uint port, uint device, uint input, bool enable) -> void {
|
||||
if(presentation->focused() || !enable) {
|
||||
if(presentation->focused() || settings["Input/FocusLoss/AllowInput"].boolean() || !enable) {
|
||||
auto guid = emulator->port[port].device[device].input[input].guid;
|
||||
auto mapping = (InputMapping*)guid;
|
||||
if(mapping) return mapping->rumble(enable);
|
||||
|
|
|
@ -11,9 +11,8 @@ Program* program = nullptr;
|
|||
|
||||
Program::Program(lstring args) {
|
||||
program = this;
|
||||
directory::create({localpath(), "tomoko/"});
|
||||
directory::create({localpath(), "higan/"});
|
||||
Application::onMain({&Program::main, this});
|
||||
Application::Windows::onModalChange([](bool modal) { if(modal && audio) audio->clear(); });
|
||||
|
||||
emulators.append(new Famicom::Interface);
|
||||
emulators.append(new SuperFamicom::Interface);
|
||||
|
@ -64,7 +63,7 @@ Program::Program(lstring args) {
|
|||
|
||||
presentation->drawSplashScreen();
|
||||
|
||||
updateVideoFilter();
|
||||
updateVideoShader();
|
||||
updateAudioVolume();
|
||||
|
||||
args.takeFirst(); //ignore program location in argument parsing
|
||||
|
@ -101,7 +100,7 @@ auto Program::main() -> void {
|
|||
updateStatusText();
|
||||
inputManager->poll();
|
||||
|
||||
if(!emulator || !emulator->loaded() || pause) {
|
||||
if(!emulator || !emulator->loaded() || pause || (!presentation->focused() && settings["Input/FocusLoss/Pause"].boolean())) {
|
||||
audio->clear();
|
||||
usleep(20 * 1000);
|
||||
return;
|
||||
|
|
|
@ -32,7 +32,7 @@ struct Program : Emulator::Interface::Bind {
|
|||
auto softReset() -> void;
|
||||
auto showMessage(const string& text) -> void;
|
||||
auto updateStatusText() -> void;
|
||||
auto updateVideoFilter() -> void;
|
||||
auto updateVideoShader() -> void;
|
||||
auto updateAudio() -> void;
|
||||
auto updateAudioVolume() -> void;
|
||||
auto updateDSP() -> void;
|
||||
|
|
|
@ -24,7 +24,7 @@ auto Program::updateStatusText() -> void {
|
|||
text = statusMessage;
|
||||
} else if(!emulator || emulator->loaded() == false) {
|
||||
text = "No cartridge loaded";
|
||||
} else if(pause) {
|
||||
} else if(pause || (!presentation->focused() && settings["Input/FocusLoss/Pause"].boolean())) {
|
||||
text = "Paused";
|
||||
} else {
|
||||
text = statusText;
|
||||
|
@ -35,15 +35,16 @@ auto Program::updateStatusText() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto Program::updateVideoFilter() -> void {
|
||||
auto Program::updateVideoShader() -> void {
|
||||
if(settings["Video/Driver"].text() == "OpenGL"
|
||||
&& settings["Video/Shader"].text() != "None"
|
||||
&& settings["Video/Shader"].text() != "Blur"
|
||||
&& directory::exists(settings["Video/Shader"].text())
|
||||
) {
|
||||
video->set(Video::Filter, Video::FilterNearest);
|
||||
video->set(Video::Shader, settings["Video/Shader"].text());
|
||||
} else {
|
||||
video->set(Video::Filter, settings["Video/Filter"].text() == "Blur" ? Video::FilterLinear : Video::FilterNearest);
|
||||
video->set(Video::Filter, settings["Video/Shader"].text() == "Blur" ? Video::FilterLinear : Video::FilterNearest);
|
||||
video->set(Video::Shader, (string)"");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ auto HotkeySettings::reloadMappings() -> void {
|
|||
mappingList.append(ListViewHeader().setVisible()
|
||||
.append(ListViewColumn().setText("Name"))
|
||||
.append(ListViewColumn().setText("Mapping").setExpandable())
|
||||
.append(ListViewColumn().setText("Device"))
|
||||
.append(ListViewColumn().setText("Device").setAlignment(1.0).setForegroundColor({0, 128, 0}))
|
||||
);
|
||||
for(auto& hotkey : inputManager->hotkeys) {
|
||||
mappingList.append(ListViewItem()
|
||||
|
|
|
@ -3,6 +3,14 @@ InputSettings::InputSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
setText("Input");
|
||||
|
||||
layout.setMargin(5);
|
||||
focusLabel.setText("When Focus is Lost:");
|
||||
pauseEmulation.setText("Pause Emulation").setChecked(settings["Input/FocusLoss/Pause"].boolean()).onToggle([&] {
|
||||
settings["Input/FocusLoss/Pause"].setValue(pauseEmulation.checked());
|
||||
allowInput.setEnabled(!pauseEmulation.checked());
|
||||
}).doToggle();
|
||||
allowInput.setText("Allow Input").setChecked(settings["Input/FocusLoss/AllowInput"].boolean()).onToggle([&] {
|
||||
settings["Input/FocusLoss/AllowInput"].setValue(allowInput.checked());
|
||||
});
|
||||
for(auto& emulator : inputManager->emulators) {
|
||||
emulatorList.append(ComboButtonItem().setText(emulator.name));
|
||||
}
|
||||
|
@ -87,7 +95,7 @@ auto InputSettings::reloadMappings() -> void {
|
|||
mappingList.append(ListViewHeader().setVisible()
|
||||
.append(ListViewColumn().setText("Name"))
|
||||
.append(ListViewColumn().setText("Mapping").setExpandable())
|
||||
.append(ListViewColumn().setText("Device").setForegroundColor({0, 128, 0}))
|
||||
.append(ListViewColumn().setText("Device").setAlignment(1.0).setForegroundColor({0, 128, 0}))
|
||||
);
|
||||
for(auto& mapping : activeDevice().mappings) {
|
||||
mappingList.append(ListViewItem()
|
||||
|
|
|
@ -53,6 +53,10 @@ struct InputSettings : TabFrameItem {
|
|||
Timer timer;
|
||||
|
||||
VerticalLayout layout{this};
|
||||
HorizontalLayout focusLayout{&layout, Size{~0, 0}};
|
||||
Label focusLabel{&focusLayout, Size{0, 0}};
|
||||
CheckLabel pauseEmulation{&focusLayout, Size{0, 0}};
|
||||
CheckLabel allowInput{&focusLayout, Size{0, 0}};
|
||||
HorizontalLayout selectionLayout{&layout, Size{~0, 0}};
|
||||
ComboButton emulatorList{&selectionLayout, Size{~0, 0}};
|
||||
ComboButton portList{&selectionLayout, Size{~0, 0}};
|
||||
|
|
|
@ -4,6 +4,10 @@ include ../hiro/GNUmakefile
|
|||
flags += -I.. -O3
|
||||
link +=
|
||||
|
||||
ifeq ($(platform),windows)
|
||||
link += -mwindows
|
||||
endif
|
||||
|
||||
objects := obj/hiro.o
|
||||
objects += obj/icarus.o
|
||||
objects += $(if $(call streq,$(platform),windows),obj/resource.o)
|
||||
|
|
|
@ -74,6 +74,22 @@ auto nall::main(lstring args) -> void {
|
|||
new SettingsDialog;
|
||||
new ImportDialog;
|
||||
new ErrorDialog;
|
||||
#if defined(PLATFORM_MACOSX)
|
||||
Application::Cocoa::onAbout([&] {
|
||||
MessageDialog().setTitle("About icarus").setText({
|
||||
"icarus\n\n"
|
||||
"Author: byuu\n"
|
||||
"License: GPLv3\n"
|
||||
"Website: http://byuu.org/\n"
|
||||
}).information();
|
||||
});
|
||||
Application::Cocoa::onPreferences([&] {
|
||||
scanDialog->settingsButton.doActivate();
|
||||
});
|
||||
Application::Cocoa::onQuit({
|
||||
Application::quit();
|
||||
});
|
||||
#endif
|
||||
scanDialog->show();
|
||||
Application::run();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue