Update to 20180724 release.

byuu says:

I failed to complete a WIP, have five of eight cores updated with some
major changes to Emulator::Interface. I'll just post a quick temporary
WIP in the off chance someone wants to look over the new interface and
comment on it.

Also implemented screen saver suppression into hiro/GTK.

I should also add ... a plan of mine is to develop target-bsnes into a
more generic user interface, with the general idea being that
target-higan is for multiple Emulator::Interface cores at the same time,
and target-bsnes is for just one Emulator::Interface core.

The idea being that if one were to compile target-bsnes with the GBA
core, it'd become bgba, for instance.
I don't plan on releasing single-core emulators like this, but ... I don't see any downsides to being more flexible.
This commit is contained in:
Tim Allen 2018-07-24 23:41:41 +10:00
parent 0aedb3430c
commit f1a4576ac4
52 changed files with 840 additions and 704 deletions

View File

@ -1,4 +1,4 @@
target := bsnes target := higan
binary := application binary := application
build := performance build := performance
openmp := true openmp := true

View File

@ -13,7 +13,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; static const string Name = "higan";
static const string Version = "106.51"; static const string Version = "106.52";
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/";

View File

@ -3,42 +3,24 @@
namespace Emulator { namespace Emulator {
struct Interface { struct Interface {
//information
struct Information { struct Information {
string manufacturer; string manufacturer;
string name; string name;
bool overscan = false; string extension;
bool resettable = false; bool resettable = false;
} information;
struct Medium {
uint id;
string name;
string type; //extension
}; };
vector<Medium> media; virtual auto information() -> Information = 0;
struct Device {
uint id;
string name;
struct Input {
uint type; //0 = digital, 1 = analog (relative), 2 = rumble
string name;
};
vector<Input> inputs;
};
struct Port {
uint id;
string name;
vector<Device> devices;
};
vector<Port> ports;
//information
virtual auto manifest() -> string = 0; virtual auto manifest() -> string = 0;
virtual auto title() -> string = 0; virtual auto title() -> string = 0;
struct VideoInformation { struct Display {
struct Type { enum : uint {
CRT,
LCD,
};};
uint type = 0;
uint colors = 0;
uint width = 0; uint width = 0;
uint height = 0; uint height = 0;
uint internalWidth = 0; uint internalWidth = 0;
@ -46,23 +28,48 @@ struct Interface {
double aspectCorrection = 0; double aspectCorrection = 0;
double refreshRate = 0; double refreshRate = 0;
}; };
virtual auto videoInformation() -> VideoInformation = 0; virtual auto display() -> Display = 0;
virtual auto videoColors() -> uint32 = 0; virtual auto color(uint32 color) -> uint64 = 0;
virtual auto videoColor(uint32 color) -> uint64 = 0;
//media interface //game interface
virtual auto loaded() -> bool { return false; } virtual auto loaded() -> bool = 0;
virtual auto sha256() -> string { return ""; } virtual auto sha256() -> string { return ""; }
virtual auto load(uint id) -> bool { return false; } virtual auto load() -> bool = 0;
virtual auto save() -> void {} virtual auto save() -> void = 0;
virtual auto unload() -> void {} virtual auto unload() -> void = 0;
//system interface //system interface
struct Port {
uint id;
string name;
};
virtual auto ports() -> vector<Port> = 0;
struct Device {
uint id;
string name;
};
virtual auto devices(uint port) -> vector<Device> = 0;
struct Input {
struct Type { enum : uint {
Hat,
Button,
Trigger,
Control,
Axis,
Rumble,
};};
uint type;
string name;
};
virtual auto inputs(uint device) -> vector<Input> = 0;
virtual auto connected(uint port) -> uint { return 0; } virtual auto connected(uint port) -> uint { return 0; }
virtual auto connect(uint port, uint device) -> void {} virtual auto connect(uint port, uint device) -> void {}
virtual auto power() -> void {} virtual auto power() -> void = 0;
virtual auto reset() -> void {} virtual auto reset() -> void {}
virtual auto run() -> void {} virtual auto run() -> void = 0;
//time functions //time functions
virtual auto rtc() -> bool { return false; } virtual auto rtc() -> bool { return false; }

View File

@ -2,40 +2,16 @@
namespace Famicom { namespace Famicom {
#define returns(T) T { return ([&] { struct With : T { With() {
#define $ }}; return With(); })(); }
Settings settings; Settings settings;
Interface::Interface() { auto Interface::information() -> returns(Information) {
information.manufacturer = "Nintendo"; manufacturer = "Nintendo";
information.name = "Famicom"; name = "Famicom";
information.overscan = true; resettable = true;
information.resettable = true; }$
media.append({ID::Famicom, "Famicom", "fc"});
Port controllerPort1{ID::Port::Controller1, "Controller Port 1"};
Port controllerPort2{ID::Port::Controller2, "Controller Port 2"};
{ Device device{ID::Device::None, "None"};
controllerPort1.devices.append(device);
controllerPort2.devices.append(device);
}
{ Device device{ID::Device::Gamepad, "Gamepad"};
device.inputs.append({0, "Up" });
device.inputs.append({0, "Down" });
device.inputs.append({0, "Left" });
device.inputs.append({0, "Right" });
device.inputs.append({0, "B" });
device.inputs.append({0, "A" });
device.inputs.append({0, "Select"});
device.inputs.append({0, "Start" });
controllerPort1.devices.append(device);
controllerPort2.devices.append(device);
}
ports.append(move(controllerPort1));
ports.append(move(controllerPort2));
}
auto Interface::manifest() -> string { auto Interface::manifest() -> string {
return cartridge.manifest(); return cartridge.manifest();
@ -45,22 +21,18 @@ auto Interface::title() -> string {
return cartridge.title(); return cartridge.title();
} }
auto Interface::videoInformation() -> VideoInformation { auto Interface::display() -> returns(Display) {
VideoInformation vi; type = Display::Type::CRT;
vi.width = 256; colors = 1 << 9;
vi.height = 240; width = 256;
vi.internalWidth = 256; height = 240;
vi.internalHeight = 240; internalWidth = 256;
vi.aspectCorrection = 8.0 / 7.0; internalHeight = 240;
vi.refreshRate = system.frequency() / (ppu.vlines() * ppu.rate() * 341.0); aspectCorrection = 8.0 / 7.0;
return vi; refreshRate = system.frequency() / (ppu.vlines() * ppu.rate() * 341.0);
} }$
auto Interface::videoColors() -> uint32 { auto Interface::color(uint32 n) -> uint64 {
return 1 << 9;
}
auto Interface::videoColor(uint32 n) -> uint64 {
double saturation = 2.0; double saturation = 2.0;
double hue = 0.0; double hue = 0.0;
double contrast = 1.0; double contrast = 1.0;
@ -119,7 +91,7 @@ auto Interface::sha256() -> string {
return cartridge.sha256(); return cartridge.sha256();
} }
auto Interface::load(uint id) -> bool { auto Interface::load() -> bool {
return system.load(this); return system.load(this);
} }
@ -132,6 +104,50 @@ auto Interface::unload() -> void {
system.unload(); system.unload();
} }
auto Interface::ports() -> vector<Port> { return {
{ID::Port::Controller1, "Controller Port 1"},
{ID::Port::Controller2, "Controller Port 2"},
{ID::Port::Expansion, "Expansion Port" }};
}
auto Interface::devices(uint port) -> vector<Device> {
if(port == ID::Port::Controller1) return {
{ID::Device::None, "None" },
{ID::Device::Gamepad, "Gamepad"}
};
if(port == ID::Port::Controller2) return {
{ID::Device::None, "None" },
{ID::Device::Gamepad, "Gamepad"}
};
if(port == ID::Port::Expansion) return {
{ID::Device::None, "None"}
};
return {};
}
auto Interface::inputs(uint device) -> vector<Input> {
using Type = Input::Type;
if(device == ID::Device::None) return {
};
if(device == ID::Device::Gamepad) return {
{Type::Hat, "Up" },
{Type::Hat, "Down" },
{Type::Hat, "Left" },
{Type::Hat, "Right" },
{Type::Button, "B" },
{Type::Button, "A" },
{Type::Control, "Select"},
{Type::Control, "Start" }
};
return {};
}
auto Interface::connected(uint port) -> uint { auto Interface::connected(uint port) -> uint {
if(port == ID::Port::Controller1) return settings.controllerPort1; if(port == ID::Port::Controller1) return settings.controllerPort1;
if(port == ID::Port::Controller2) return settings.controllerPort2; if(port == ID::Port::Controller2) return settings.controllerPort2;
@ -191,4 +207,7 @@ auto Interface::set(const string& name, const any& value) -> bool {
return false; return false;
} }
#undef returns
#undef $
} }

View File

@ -21,21 +21,24 @@ struct ID {
struct Interface : Emulator::Interface { struct Interface : Emulator::Interface {
using Emulator::Interface::load; using Emulator::Interface::load;
Interface(); auto information() -> Information override;
auto manifest() -> string override; auto manifest() -> string override;
auto title() -> string override; auto title() -> string override;
auto videoInformation() -> VideoInformation override; auto display() -> Display override;
auto videoColors() -> uint32 override; auto color(uint32 color) -> uint64 override;
auto videoColor(uint32 color) -> uint64 override;
auto loaded() -> bool override; auto loaded() -> bool override;
auto sha256() -> string override; auto sha256() -> string override;
auto load(uint id) -> bool override; auto load() -> bool override;
auto save() -> void override; auto save() -> void override;
auto unload() -> void override; auto unload() -> void override;
auto ports() -> vector<Port> override;
auto devices(uint port) -> vector<Device> override;
auto inputs(uint device) -> vector<Input> override;
auto connected(uint port) -> uint override; auto connected(uint port) -> uint override;
auto connect(uint port, uint device) -> void override; auto connect(uint port, uint device) -> void override;
auto power() -> void override; auto power() -> void override;

View File

@ -1,16 +1,10 @@
GameBoyColorInterface::GameBoyColorInterface() { auto GameBoyColorInterface::information() -> returns(Information) {
information.manufacturer = "Nintendo"; manufacturer = "Nintendo";
information.name = "Game Boy Color"; name = "Game Boy Color";
information.overscan = false; extension = "gbc";
}$
media.append({ID::GameBoyColor, "Game Boy Color", "gbc"}); auto GameBoyColorInterface::color(uint32 color) -> uint64 {
}
auto GameBoyColorInterface::videoColors() -> uint32 {
return 1 << 15;
}
auto GameBoyColorInterface::videoColor(uint32 color) -> uint64 {
uint r = color.bits( 0, 4); uint r = color.bits( 0, 4);
uint g = color.bits( 5, 9); uint g = color.bits( 5, 9);
uint b = color.bits(10,14); uint b = color.bits(10,14);
@ -31,7 +25,6 @@ auto GameBoyColorInterface::videoColor(uint32 color) -> uint64 {
return R << 32 | G << 16 | B << 0; return R << 32 | G << 16 | B << 0;
} }
auto GameBoyColorInterface::load(uint id) -> bool { auto GameBoyColorInterface::load() -> bool {
if(id == ID::GameBoyColor) return system.load(this, System::Model::GameBoyColor); return system.load(this, System::Model::GameBoyColor);
return false;
} }

View File

@ -1,16 +1,10 @@
GameBoyInterface::GameBoyInterface() { auto GameBoyInterface::information() -> returns(Information) {
information.manufacturer = "Nintendo"; manufacturer = "Nintendo";
information.name = "Game Boy"; name = "Game Boy";
information.overscan = false; extension = "gb";
}$
media.append({ID::GameBoy, "Game Boy", "gb"}); auto GameBoyInterface::color(uint32 color) -> uint64 {
}
auto GameBoyInterface::videoColors() -> uint32 {
return 1 << 2;
}
auto GameBoyInterface::videoColor(uint32 color) -> uint64 {
if(!settings.colorEmulation) { if(!settings.colorEmulation) {
uint64 L = image::normalize(3 - color, 2, 16); uint64 L = image::normalize(3 - color, 2, 16);
return L << 32 | L << 16 | L << 0; return L << 32 | L << 16 | L << 0;
@ -46,7 +40,6 @@ auto GameBoyInterface::videoColor(uint32 color) -> uint64 {
} }
} }
auto GameBoyInterface::load(uint id) -> bool { auto GameBoyInterface::load() -> bool {
if(id == ID::GameBoy) return system.load(this, System::Model::GameBoy); return system.load(this, System::Model::GameBoy);
return false;
} }

View File

@ -2,32 +2,14 @@
namespace GameBoy { namespace GameBoy {
#define returns(T) T { return ([&] { struct With : T { With() {
#define $ }}; return With(); })(); }
SuperGameBoyInterface* superGameBoy = nullptr; SuperGameBoyInterface* superGameBoy = nullptr;
Settings settings; Settings settings;
#include "game-boy.cpp" #include "game-boy.cpp"
#include "game-boy-color.cpp" #include "game-boy-color.cpp"
Interface::Interface() {
Port hardwarePort{ID::Port::Hardware, "Hardware"};
{ Device device{ID::Device::Controls, "Controls"};
device.inputs.append({0, "Up" });
device.inputs.append({0, "Down" });
device.inputs.append({0, "Left" });
device.inputs.append({0, "Right" });
device.inputs.append({0, "B" });
device.inputs.append({0, "A" });
device.inputs.append({0, "Select"});
device.inputs.append({0, "Start" });
device.inputs.append({1, "X-axis"});
device.inputs.append({1, "Y-axis"});
device.inputs.append({2, "Rumble"});
hardwarePort.devices.append(device);
}
ports.append(move(hardwarePort));
}
auto Interface::manifest() -> string { auto Interface::manifest() -> string {
return cartridge.manifest(); return cartridge.manifest();
} }
@ -36,16 +18,16 @@ auto Interface::title() -> string {
return cartridge.title(); return cartridge.title();
} }
auto Interface::videoInformation() -> VideoInformation { auto Interface::display() -> returns(Display) {
VideoInformation vi; type = Display::Type::LCD;
vi.width = 160; colors = Model::GameBoyColor() ? 1 << 15 : 1 << 2;
vi.height = 144; width = 160;
vi.internalWidth = 160; height = 144;
vi.internalHeight = 144; internalWidth = 160;
vi.aspectCorrection = 1.0; internalHeight = 144;
vi.refreshRate = (4.0 * 1024.0 * 1024.0) / (154.0 * 456.0); aspectCorrection = 1.0;
return vi; refreshRate = (4.0 * 1024.0 * 1024.0) / (154.0 * 456.0);
} }$
auto Interface::loaded() -> bool { auto Interface::loaded() -> bool {
return system.loaded(); return system.loaded();
@ -64,6 +46,38 @@ auto Interface::unload() -> void {
system.unload(); system.unload();
} }
auto Interface::ports() -> vector<Port> { return {
{ID::Port::Hardware, "Hardware"}};
}
auto Interface::devices(uint port) -> vector<Device> {
if(port == ID::Port::Hardware) return {
{ID::Device::Controls, "Controls"}
};
return {};
}
auto Interface::inputs(uint device) -> vector<Input> {
using Type = Input::Type;
if(device == ID::Device::Controls) return {
{Type::Hat, "Up" },
{Type::Hat, "Down" },
{Type::Hat, "Left" },
{Type::Hat, "Right" },
{Type::Button, "B" },
{Type::Button, "A" },
{Type::Control, "Select"},
{Type::Control, "Start" },
{Type::Axis, "X-axis"},
{Type::Axis, "Y-axis"},
{Type::Rumble, "Rumble"}
};
return {};
}
auto Interface::power() -> void { auto Interface::power() -> void {
system.power(); system.power();
} }
@ -115,4 +129,7 @@ auto Interface::set(const string& name, const any& value) -> bool {
return false; return false;
} }
#undef returns
#undef $
} }

View File

@ -18,12 +18,10 @@ struct ID {
}; };
struct Interface : Emulator::Interface { struct Interface : Emulator::Interface {
Interface();
auto manifest() -> string override; auto manifest() -> string override;
auto title() -> string override; auto title() -> string override;
auto videoInformation() -> VideoInformation override; auto display() -> Display override;
auto loaded() -> bool override; auto loaded() -> bool override;
auto sha256() -> string override; auto sha256() -> string override;
@ -31,6 +29,10 @@ struct Interface : Emulator::Interface {
auto save() -> void override; auto save() -> void override;
auto unload() -> void override; auto unload() -> void override;
auto ports() -> vector<Port> override;
auto devices(uint port) -> vector<Device> override;
auto inputs(uint device) -> vector<Input> override;
auto power() -> void override; auto power() -> void override;
auto run() -> void override; auto run() -> void override;
@ -47,23 +49,21 @@ struct Interface : Emulator::Interface {
struct GameBoyInterface : Interface { struct GameBoyInterface : Interface {
using Emulator::Interface::load; using Emulator::Interface::load;
GameBoyInterface(); auto information() -> Information override;
auto videoColors() -> uint32 override; auto color(uint32 color) -> uint64 override;
auto videoColor(uint32 color) -> uint64 override;
auto load(uint id) -> bool override; auto load() -> bool override;
}; };
struct GameBoyColorInterface : Interface { struct GameBoyColorInterface : Interface {
using Emulator::Interface::load; using Emulator::Interface::load;
GameBoyColorInterface(); auto information() -> Information override;
auto videoColors() -> uint32 override; auto color(uint32 color) -> uint64 override;
auto videoColor(uint32 color) -> uint64 override;
auto load(uint id) -> bool override; auto load() -> bool override;
}; };
struct SuperGameBoyInterface { struct SuperGameBoyInterface {

View File

@ -2,34 +2,15 @@
namespace GameBoyAdvance { namespace GameBoyAdvance {
#define returns(T) T { return ([&] { struct With : T { With() {
#define $ }}; return With(); })(); }
Settings settings; Settings settings;
Interface::Interface() { auto Interface::information() -> returns(Information) {
information.manufacturer = "Nintendo"; manufacturer = "Nintendo";
information.name = "Game Boy Advance"; name = "Game Boy Advance";
information.overscan = false; }$
media.append({ID::GameBoyAdvance, "Game Boy Advance", "gba"});
Port hardwarePort{ID::Port::Hardware, "Hardware"};
{ Device device{ID::Device::Controls, "Controls"};
device.inputs.append({0, "Up" });
device.inputs.append({0, "Down" });
device.inputs.append({0, "Left" });
device.inputs.append({0, "Right" });
device.inputs.append({0, "B" });
device.inputs.append({0, "A" });
device.inputs.append({0, "L" });
device.inputs.append({0, "R" });
device.inputs.append({0, "Select"});
device.inputs.append({0, "Start" });
device.inputs.append({2, "Rumble"});
hardwarePort.devices.append(device);
}
ports.append(move(hardwarePort));
}
auto Interface::manifest() -> string { auto Interface::manifest() -> string {
return cartridge.manifest(); return cartridge.manifest();
@ -39,26 +20,22 @@ auto Interface::title() -> string {
return cartridge.title(); return cartridge.title();
} }
auto Interface::videoInformation() -> VideoInformation { auto Interface::display() -> returns(Display) {
VideoInformation vi; type = Display::Type::LCD;
vi.width = 240; colors = 1 << 15;
vi.height = 160; width = 240;
vi.internalWidth = 240; height = 160;
vi.internalHeight = 160; internalWidth = 240;
vi.aspectCorrection = 1.0; internalHeight = 160;
vi.refreshRate = system.frequency() / (228.0 * 1232.0); aspectCorrection = 1.0;
refreshRate = system.frequency() / (228.0 * 1232.0);
if(settings.rotateLeft) { if(settings.rotateLeft) {
swap(vi.width, vi.height); swap(width, height);
swap(vi.internalWidth, vi.internalHeight); swap(internalWidth, internalHeight);
} }
return vi; }$
}
auto Interface::videoColors() -> uint32 { auto Interface::color(uint32 color) -> uint64 {
return 1 << 15;
}
auto Interface::videoColor(uint32 color) -> uint64 {
uint R = color.bits( 0, 4); uint R = color.bits( 0, 4);
uint G = color.bits( 5, 9); uint G = color.bits( 5, 9);
uint B = color.bits(10,14); uint B = color.bits(10,14);
@ -84,7 +61,7 @@ auto Interface::loaded() -> bool {
return system.loaded(); return system.loaded();
} }
auto Interface::load(uint id) -> bool { auto Interface::load() -> bool {
return system.load(this); return system.load(this);
} }
@ -97,6 +74,38 @@ auto Interface::unload() -> void {
system.unload(); system.unload();
} }
auto Interface::ports() -> vector<Port> { return {
{ID::Port::Hardware, "Hardware"}};
}
auto Interface::devices(uint port) -> vector<Device> {
if(port == ID::Port::Hardware) return {
{ID::Device::Controls, "Controls"}
};
return {};
}
auto Interface::inputs(uint device) -> vector<Input> {
using Type = Input::Type;
if(device == ID::Device::Controls) return {
{Type::Hat, "Up" },
{Type::Hat, "Down" },
{Type::Hat, "Left" },
{Type::Hat, "Right" },
{Type::Button, "B" },
{Type::Button, "A" },
{Type::Trigger, "L" },
{Type::Trigger, "R" },
{Type::Control, "Select"},
{Type::Control, "Start" },
{Type::Rumble, "Rumble"}
};
return {};
}
auto Interface::power() -> void { auto Interface::power() -> void {
system.power(); system.power();
} }
@ -150,4 +159,7 @@ auto Interface::set(const string& name, const any& value) -> bool {
return false; return false;
} }
#undef returns
#undef $
} }

View File

@ -18,20 +18,23 @@ struct ID {
struct Interface : Emulator::Interface { struct Interface : Emulator::Interface {
using Emulator::Interface::load; using Emulator::Interface::load;
Interface(); auto information() -> Information override;
auto manifest() -> string override; auto manifest() -> string override;
auto title() -> string override; auto title() -> string override;
auto videoInformation() -> VideoInformation override; auto display() -> Display override;
auto videoColors() -> uint32 override; auto color(uint32 color) -> uint64 override;
auto videoColor(uint32 color) -> uint64 override;
auto loaded() -> bool override; auto loaded() -> bool override;
auto load(uint id) -> bool override; auto load() -> bool override;
auto save() -> void override; auto save() -> void override;
auto unload() -> void override; auto unload() -> void override;
auto ports() -> vector<Port> override;
auto devices(uint port) -> vector<Device> override;
auto inputs(uint device) -> vector<Input> override;
auto power() -> void override; auto power() -> void override;
auto run() -> void override; auto run() -> void override;

View File

@ -2,60 +2,16 @@
namespace MegaDrive { namespace MegaDrive {
#define returns(T) T { return ([&] { struct With : T { With() {
#define $ }}; return With(); })(); }
Settings settings; Settings settings;
Interface::Interface() { auto Interface::information() -> returns(Information) {
information.manufacturer = "Sega"; manufacturer = "Sega";
information.name = "Mega Drive"; name = "Mega Drive";
information.overscan = true; resettable = true;
information.resettable = true; }$
media.append({ID::MegaDrive, "Mega Drive", "md"});
Port controllerPort1{ID::Port::Controller1, "Controller Port 1"};
Port controllerPort2{ID::Port::Controller2, "Controller Port 2"};
Port extensionPort{ID::Port::Extension, "Extension Port"};
{ Device device{ID::Device::None, "None"};
controllerPort1.devices.append(device);
controllerPort2.devices.append(device);
extensionPort.devices.append(device);
}
{ Device device{ID::Device::ControlPad, "Control Pad"};
device.inputs.append({0, "Up" });
device.inputs.append({0, "Down" });
device.inputs.append({0, "Left" });
device.inputs.append({0, "Right"});
device.inputs.append({0, "A" });
device.inputs.append({0, "B" });
device.inputs.append({0, "C" });
device.inputs.append({0, "Start"});
controllerPort1.devices.append(device);
controllerPort2.devices.append(device);
}
{ Device device{ID::Device::FightingPad, "Fighting Pad"};
device.inputs.append({0, "Up" });
device.inputs.append({0, "Down" });
device.inputs.append({0, "Left" });
device.inputs.append({0, "Right"});
device.inputs.append({0, "A" });
device.inputs.append({0, "B" });
device.inputs.append({0, "C" });
device.inputs.append({0, "X" });
device.inputs.append({0, "Y" });
device.inputs.append({0, "Z" });
device.inputs.append({0, "Mode" });
device.inputs.append({0, "Start"});
controllerPort1.devices.append(device);
controllerPort2.devices.append(device);
}
ports.append(move(controllerPort1));
ports.append(move(controllerPort2));
ports.append(move(extensionPort));
}
auto Interface::manifest() -> string { auto Interface::manifest() -> string {
return cartridge.manifest(); return cartridge.manifest();
@ -65,22 +21,18 @@ auto Interface::title() -> string {
return cartridge.title(); return cartridge.title();
} }
auto Interface::videoInformation() -> VideoInformation { auto Interface::display() -> returns(Display) {
VideoInformation vi; type = Display::Type::CRT;
vi.width = 320; colors = 3 * (1 << 9);
vi.height = 240; width = 320;
vi.internalWidth = 1280; height = 240;
vi.internalHeight = 480; internalWidth = 1280;
vi.aspectCorrection = 1.0; internalHeight = 480;
vi.refreshRate = (system.frequency() / 2.0) / (vdp.frameHeight() * 1710.0); aspectCorrection = 1.0;
return vi; refreshRate = (system.frequency() / 2.0) / (vdp.frameHeight() * 1710.0);
} }$
auto Interface::videoColors() -> uint32 { auto Interface::color(uint32 color) -> uint64 {
return 3 * (1 << 9);
}
auto Interface::videoColor(uint32 color) -> uint64 {
uint R = color.bits(0, 2); uint R = color.bits(0, 2);
uint G = color.bits(3, 5); uint G = color.bits(3, 5);
uint B = color.bits(6, 8); uint B = color.bits(6, 8);
@ -103,7 +55,7 @@ auto Interface::loaded() -> bool {
return system.loaded(); return system.loaded();
} }
auto Interface::load(uint id) -> bool { auto Interface::load() -> bool {
return system.load(this); return system.load(this);
} }
@ -116,6 +68,67 @@ auto Interface::unload() -> void {
system.unload(); system.unload();
} }
auto Interface::ports() -> vector<Port> { return {
{ID::Port::Controller1, "Controller Port 1"},
{ID::Port::Controller2, "Controller Port 2"},
{ID::Port::Extension, "Extension Port" }};
}
auto Interface::devices(uint port) -> vector<Device> {
if(port == ID::Port::Controller1) return {
{ID::Device::None, "None" },
{ID::Device::ControlPad, "Control Pad" },
{ID::Device::FightingPad, "Fighting Pad"}
};
if(port == ID::Port::Controller2) return {
{ID::Device::None, "None" },
{ID::Device::ControlPad, "Control Pad" },
{ID::Device::FightingPad, "Fighting Pad"}
};
if(port == ID::Port::Extension) return {
{ID::Device::None, "None"}
};
return {};
}
auto Interface::inputs(uint device) -> vector<Input> {
using Type = Input::Type;
if(device == ID::Device::None) return {
};
if(device == ID::Device::ControlPad) return {
{Type::Hat, "Up" },
{Type::Hat, "Down" },
{Type::Hat, "Left" },
{Type::Hat, "Right"},
{Type::Button, "A" },
{Type::Button, "B" },
{Type::Button, "C" },
{Type::Control, "Start"}
};
if(device == ID::Device::FightingPad) return {
{Type::Hat, "Up" },
{Type::Hat, "Down" },
{Type::Hat, "Left" },
{Type::Hat, "Right"},
{Type::Button, "A" },
{Type::Button, "B" },
{Type::Button, "C" },
{Type::Button, "X" },
{Type::Button, "Y" },
{Type::Button, "Z" },
{Type::Control, "Mode" },
{Type::Control, "Start"}
};
return {};
}
auto Interface::connected(uint port) -> uint { auto Interface::connected(uint port) -> uint {
if(port == ID::Port::Controller1) return settings.controllerPort1; if(port == ID::Port::Controller1) return settings.controllerPort1;
if(port == ID::Port::Controller2) return settings.controllerPort2; if(port == ID::Port::Controller2) return settings.controllerPort2;
@ -166,4 +179,7 @@ auto Interface::set(const string& name, const any& value) -> bool {
return false; return false;
} }
#undef returns
#undef $
} }

View File

@ -22,20 +22,23 @@ struct ID {
struct Interface : Emulator::Interface { struct Interface : Emulator::Interface {
using Emulator::Interface::load; using Emulator::Interface::load;
Interface(); auto information() -> Information override;
auto manifest() -> string override; auto manifest() -> string override;
auto title() -> string override; auto title() -> string override;
auto videoInformation() -> VideoInformation override; auto display() -> Display override;
auto videoColors() -> uint32 override; auto color(uint32 color) -> uint64 override;
auto videoColor(uint32 color) -> uint64 override;
auto loaded() -> bool override; auto loaded() -> bool override;
auto load(uint id) -> bool override; auto load() -> bool override;
auto save() -> void override; auto save() -> void override;
auto unload() -> void override; auto unload() -> void override;
auto ports() -> vector<Port> override;
auto devices(uint port) -> vector<Device> override;
auto inputs(uint device) -> vector<Input> override;
auto connected(uint port) -> uint override; auto connected(uint port) -> uint override;
auto connect(uint port, uint device) -> void override; auto connect(uint port, uint device) -> void override;
auto power() -> void override; auto power() -> void override;

View File

@ -2,112 +2,17 @@
namespace SuperFamicom { namespace SuperFamicom {
#define returns(T) T { return ([&] { struct With : T { With() {
#define $ }}; return With(); })(); }
Settings settings; Settings settings;
Interface::Interface() { auto Interface::information() -> returns(Information) {
information.manufacturer = "Nintendo"; manufacturer = "Nintendo";
information.name = "Super Famicom"; name = "Super Famicom";
information.overscan = true; extension = "sfc";
information.resettable = true; resettable = true;
}$
media.append({ID::SuperFamicom, "Super Famicom", "sfc"});
Port controllerPort1{ID::Port::Controller1, "Controller Port 1"};
Port controllerPort2{ID::Port::Controller2, "Controller Port 2"};
Port expansionPort{ID::Port::Expansion, "Expansion Port"};
{ Device device{ID::Device::None, "None"};
controllerPort1.devices.append(device);
controllerPort2.devices.append(device);
expansionPort.devices.append(device);
}
{ Device device{ID::Device::Gamepad, "Gamepad"};
device.inputs.append({0, "Up" });
device.inputs.append({0, "Down" });
device.inputs.append({0, "Left" });
device.inputs.append({0, "Right" });
device.inputs.append({0, "B" });
device.inputs.append({0, "A" });
device.inputs.append({0, "Y" });
device.inputs.append({0, "X" });
device.inputs.append({0, "L" });
device.inputs.append({0, "R" });
device.inputs.append({0, "Select"});
device.inputs.append({0, "Start" });
controllerPort1.devices.append(device);
controllerPort2.devices.append(device);
}
{ Device device{ID::Device::Mouse, "Mouse"};
device.inputs.append({1, "X-axis"});
device.inputs.append({1, "Y-axis"});
device.inputs.append({0, "Left" });
device.inputs.append({0, "Right" });
controllerPort1.devices.append(device);
controllerPort2.devices.append(device);
}
{ Device device{ID::Device::SuperMultitap, "Super Multitap"};
for(uint p = 2; p <= 5; p++) {
device.inputs.append({0, {"Port ", p, " - ", "Up" }});
device.inputs.append({0, {"Port ", p, " - ", "Down" }});
device.inputs.append({0, {"Port ", p, " - ", "Left" }});
device.inputs.append({0, {"Port ", p, " - ", "Right" }});
device.inputs.append({0, {"Port ", p, " - ", "B" }});
device.inputs.append({0, {"Port ", p, " - ", "A" }});
device.inputs.append({0, {"Port ", p, " - ", "Y" }});
device.inputs.append({0, {"Port ", p, " - ", "X" }});
device.inputs.append({0, {"Port ", p, " - ", "L" }});
device.inputs.append({0, {"Port ", p, " - ", "R" }});
device.inputs.append({0, {"Port ", p, " - ", "Select"}});
device.inputs.append({0, {"Port ", p, " - ", "Start" }});
}
controllerPort2.devices.append(device);
}
{ Device device{ID::Device::SuperScope, "Super Scope"};
device.inputs.append({1, "X-axis" });
device.inputs.append({1, "Y-axis" });
device.inputs.append({0, "Trigger"});
device.inputs.append({0, "Cursor" });
device.inputs.append({0, "Turbo" });
device.inputs.append({0, "Pause" });
controllerPort2.devices.append(device);
}
{ Device device{ID::Device::Justifier, "Justifier"};
device.inputs.append({1, "X-axis" });
device.inputs.append({1, "Y-axis" });
device.inputs.append({0, "Trigger"});
device.inputs.append({0, "Start" });
controllerPort2.devices.append(device);
}
{ Device device{ID::Device::Justifiers, "Justifiers"};
device.inputs.append({1, "Port 1 - X-axis" });
device.inputs.append({1, "Port 1 - Y-axis" });
device.inputs.append({0, "Port 1 - Trigger"});
device.inputs.append({0, "Port 1 - Start" });
device.inputs.append({1, "Port 2 - X-axis" });
device.inputs.append({1, "Port 2 - Y-axis" });
device.inputs.append({0, "Port 2 - Trigger"});
device.inputs.append({0, "Port 2 - Start" });
controllerPort2.devices.append(device);
}
{ Device device{ID::Device::Satellaview, "Satellaview"};
expansionPort.devices.append(device);
}
{ Device device{ID::Device::S21FX, "21fx"};
expansionPort.devices.append(device);
}
ports.append(move(controllerPort1));
ports.append(move(controllerPort2));
ports.append(move(expansionPort));
}
auto Interface::manifest() -> string { auto Interface::manifest() -> string {
return cartridge.manifest(); return cartridge.manifest();
@ -117,23 +22,19 @@ auto Interface::title() -> string {
return cartridge.title(); return cartridge.title();
} }
auto Interface::videoInformation() -> VideoInformation { auto Interface::display() -> returns(Display) {
VideoInformation vi; type = Display::Type::CRT;
vi.width = 256; colors = 1 << 19;
vi.height = 240; width = 256;
vi.internalWidth = 512; height = 240;
vi.internalHeight = 480; internalWidth = 512;
vi.aspectCorrection = 8.0 / 7.0; internalHeight = 480;
if(Region::NTSC()) vi.refreshRate = system.cpuFrequency() / (262.0 * 1364.0); aspectCorrection = 8.0 / 7.0;
if(Region::PAL()) vi.refreshRate = system.cpuFrequency() / (312.0 * 1364.0); if(Region::NTSC()) refreshRate = system.cpuFrequency() / (262.0 * 1364.0);
return vi; if(Region::PAL()) refreshRate = system.cpuFrequency() / (312.0 * 1364.0);
} }$
auto Interface::videoColors() -> uint32 { auto Interface::color(uint32 color) -> uint64 {
return 1 << 19;
}
auto Interface::videoColor(uint32 color) -> uint64 {
uint r = color.bits( 0, 4); uint r = color.bits( 0, 4);
uint g = color.bits( 5, 9); uint g = color.bits( 5, 9);
uint b = color.bits(10,14); uint b = color.bits(10,14);
@ -169,12 +70,8 @@ auto Interface::sha256() -> string {
return cartridge.sha256(); return cartridge.sha256();
} }
auto Interface::load(uint id) -> bool { auto Interface::load() -> bool {
if(id == ID::SuperFamicom) return system.load(this); return system.load(this);
if(id == ID::BSMemory) return cartridge.loadBSMemory();
if(id == ID::SufamiTurboA) return cartridge.loadSufamiTurboA();
if(id == ID::SufamiTurboB) return cartridge.loadSufamiTurboB();
return false;
} }
auto Interface::save() -> void { auto Interface::save() -> void {
@ -186,6 +83,121 @@ auto Interface::unload() -> void {
system.unload(); system.unload();
} }
auto Interface::ports() -> vector<Port> { return {
{ID::Port::Controller1, "Controller Port 1"},
{ID::Port::Controller2, "Controller Port 2"},
{ID::Port::Expansion, "Expansion Port" }};
}
auto Interface::devices(uint port) -> vector<Device> {
if(port == ID::Port::Controller1) return {
{ID::Device::None, "None" },
{ID::Device::Gamepad, "Gamepad"},
{ID::Device::Mouse, "Mouse" }
};
if(port == ID::Port::Controller2) return {
{ID::Device::None, "None" },
{ID::Device::Gamepad, "Gamepad" },
{ID::Device::Mouse, "Mouse" },
{ID::Device::SuperMultitap, "Super Multitap"},
{ID::Device::SuperScope, "Super Scope" },
{ID::Device::Justifier, "Justifier" },
{ID::Device::Justifiers, "Justifiers" }
};
if(port == ID::Port::Expansion) return {
{ID::Device::None, "None" },
{ID::Device::Satellaview, "Satellaview"},
{ID::Device::S21FX, "21fx" }
};
return {};
}
auto Interface::inputs(uint device) -> vector<Input> {
using Type = Input::Type;
if(device == ID::Device::None) return {
};
if(device == ID::Device::Gamepad) return {
{Type::Hat, "Up" },
{Type::Hat, "Down" },
{Type::Hat, "Left" },
{Type::Hat, "Right" },
{Type::Button, "B" },
{Type::Button, "A" },
{Type::Button, "Y" },
{Type::Button, "X" },
{Type::Trigger, "L" },
{Type::Trigger, "R" },
{Type::Control, "Select"},
{Type::Control, "Start" }
};
if(device == ID::Device::Mouse) return {
{Type::Axis, "X-axis"},
{Type::Axis, "Y-axis"},
{Type::Button, "Left" },
{Type::Button, "Right" }
};
if(device == ID::Device::SuperMultitap) {
vector<Input> inputs;
for(uint p = 2; p <= 5; p++) inputs.append({
{Type::Hat, {"Port ", p, " - ", "Up" }},
{Type::Hat, {"Port ", p, " - ", "Down" }},
{Type::Hat, {"Port ", p, " - ", "Left" }},
{Type::Hat, {"Port ", p, " - ", "Right" }},
{Type::Button, {"Port ", p, " - ", "B" }},
{Type::Button, {"Port ", p, " - ", "A" }},
{Type::Button, {"Port ", p, " - ", "Y" }},
{Type::Button, {"Port ", p, " - ", "X" }},
{Type::Trigger, {"Port ", p, " - ", "L" }},
{Type::Trigger, {"Port ", p, " - ", "R" }},
{Type::Control, {"Port ", p, " - ", "Select"}},
{Type::Control, {"Port ", p, " - ", "Start" }}
});
return inputs;
}
if(device == ID::Device::SuperScope) return {
{Type::Axis, "X-axis" },
{Type::Axis, "Y-axis" },
{Type::Control, "Trigger"},
{Type::Control, "Cursor" },
{Type::Control, "Turbo" },
{Type::Control, "Pause" }
};
if(device == ID::Device::Justifier) return {
{Type::Axis, "X-axis" },
{Type::Axis, "Y-axis" },
{Type::Control, "Trigger"},
{Type::Control, "Start" }
};
if(device == ID::Device::Justifiers) return {
{Type::Axis, "Port 1 - X-axis" },
{Type::Axis, "Port 1 - Y-axis" },
{Type::Control, "Port 1 - Trigger"},
{Type::Control, "Port 1 - Start" },
{Type::Axis, "Port 2 - X-axis" },
{Type::Axis, "Port 2 - Y-axis" },
{Type::Control, "Port 2 - Trigger"},
{Type::Control, "Port 2 - Start" }
};
if(device == ID::Device::Satellaview) return {
};
if(device == ID::Device::S21FX) return {
};
return {};
}
auto Interface::connected(uint port) -> uint { auto Interface::connected(uint port) -> uint {
if(port == ID::Port::Controller1) return settings.controllerPort1; if(port == ID::Port::Controller1) return settings.controllerPort1;
if(port == ID::Port::Controller2) return settings.controllerPort2; if(port == ID::Port::Controller2) return settings.controllerPort2;
@ -295,4 +307,7 @@ auto Interface::set(const string& name, const any& value) -> bool {
return false; return false;
} }
#undef returns
#undef $
} }

View File

@ -33,21 +33,23 @@ struct ID {
struct Interface : Emulator::Interface { struct Interface : Emulator::Interface {
using Emulator::Interface::load; using Emulator::Interface::load;
Interface(); auto information() -> Information;
auto manifest() -> string override; auto manifest() -> string override;
auto title() -> string override; auto title() -> string override;
auto videoInformation() -> VideoInformation override; auto display() -> Display override;
auto videoColors() -> uint32 override; auto color(uint32 color) -> uint64 override;
auto videoColor(uint32 color) -> uint64 override;
auto loaded() -> bool override; auto loaded() -> bool override;
auto sha256() -> string override; auto sha256() -> string override;
auto load(uint id) -> bool override; auto load() -> bool override;
auto save() -> void override; auto save() -> void override;
auto unload() -> void override; auto unload() -> void override;
auto ports() -> vector<Port> override;
auto devices(uint port) -> vector<Device> override;
auto inputs(uint device) -> vector<Input> override;
auto connected(uint port) -> uint override; auto connected(uint port) -> uint override;
auto connect(uint port, uint device) -> void override; auto connect(uint port, uint device) -> void override;
auto power() -> void override; auto power() -> void override;

View File

@ -27,6 +27,7 @@ auto nall::main(string_vector arguments) -> void {
} }
} }
Application::setName("bsnes"); Application::setName("bsnes");
Application::setScreenSaver(false);
Application::locale().scan(locate("locales/")); Application::locale().scan(locate("locales/"));
Application::locale().select(locale); Application::locale().select(locale);
emulator = new SuperFamicom::Interface; emulator = new SuperFamicom::Interface;

View File

@ -187,11 +187,11 @@ auto InputManager::initialize() -> void {
input->onChange({&InputManager::onChange, this}); input->onChange({&InputManager::onChange, this});
frequency = max(1u, settings["Input/Frequency"].natural()); frequency = max(1u, settings["Input/Frequency"].natural());
for(auto& port : emulator->ports) { for(auto& port : emulator->ports()) {
InputPort inputPort{port.id, port.name}; InputPort inputPort{port.id, port.name};
for(auto& device : port.devices) { for(auto& device : emulator->devices(port.id)) {
InputDevice inputDevice{device.id, device.name}; InputDevice inputDevice{device.id, device.name};
for(auto& input : device.inputs) { for(auto& input : emulator->inputs(device.id)) {
InputMapping inputMapping; InputMapping inputMapping;
inputMapping.name = input.name; inputMapping.name = input.name;
inputMapping.type = input.type; inputMapping.type = input.type;

View File

@ -7,9 +7,16 @@ struct InputMapping {
auto rumble(bool enable) -> void; auto rumble(bool enable) -> void;
auto displayName() -> string; auto displayName() -> string;
auto isDigital() const -> bool { return type == 0; } using Type = Emulator::Interface::Input::Type;
auto isAnalog() const -> bool { return type == 1; } auto isDigital() const -> bool {
auto isRumble() const -> bool { return type == 2; } return type == Type::Hat || type == Type::Button || type == Type::Trigger || type == Type::Control;
}
auto isAnalog() const -> bool {
return type == Type::Axis;
}
auto isRumble() const -> bool {
return type == Type::Rumble;
}
string path; //configuration file key path string path; //configuration file key path
string name; //input name (human readable) string name; //input name (human readable)

View File

@ -183,14 +183,6 @@ Presentation::Presentation() {
Application::Windows::onModalChange([&](bool modal) { Application::Windows::onModalChange([&](bool modal) {
if(modal && audio) audio->clear(); if(modal && audio) audio->clear();
}); });
Application::Windows::onScreenSaver([&]() -> bool {
if(emulator->loaded()) {
if(pauseEmulation.checked()) return true;
if(!program->focused() && settingsWindow->input.pauseEmulation.checked()) return true;
return false;
}
return true;
});
#endif #endif
#if defined(PLATFORM_MACOS) #if defined(PLATFORM_MACOS)
@ -346,7 +338,7 @@ auto Presentation::updateDeviceMenu() -> void {
controllerPort2.reset(); controllerPort2.reset();
expansionPort.reset(); expansionPort.reset();
for(auto& port : emulator->ports) { for(auto& port : emulator->ports()) {
Menu* menu = nullptr; Menu* menu = nullptr;
if(port.name == "Controller Port 1") menu = &controllerPort1; if(port.name == "Controller Port 1") menu = &controllerPort1;
if(port.name == "Controller Port 2") menu = &controllerPort2; if(port.name == "Controller Port 2") menu = &controllerPort2;
@ -358,7 +350,7 @@ auto Presentation::updateDeviceMenu() -> void {
auto deviceID = emulator->connected(port.id); auto deviceID = emulator->connected(port.id);
Group devices; Group devices;
for(auto& device : port.devices) { for(auto& device : emulator->devices(port.id)) {
if(port.name == "Expansion Port" && device.name == "21fx") continue; if(port.name == "Expansion Port" && device.name == "21fx") continue;
MenuRadioItem item{menu}; MenuRadioItem item{menu};
@ -380,7 +372,7 @@ auto Presentation::updateDeviceMenu() -> void {
} }
auto Presentation::updateDeviceSelections() -> void { auto Presentation::updateDeviceSelections() -> void {
for(auto& port : emulator->ports) { for(auto& port : emulator->ports()) {
Menu* menu = nullptr; Menu* menu = nullptr;
if(port.name == "Controller Port 1") menu = &controllerPort1; if(port.name == "Controller Port 1") menu = &controllerPort1;
if(port.name == "Controller Port 2") menu = &controllerPort2; if(port.name == "Controller Port 2") menu = &controllerPort2;

View File

@ -1,10 +1,8 @@
auto Program::load() -> void { auto Program::load() -> void {
unload(); unload();
if(!emulator->load()) return;
for(auto& media : emulator->media) { gameQueue = {};
if(media.type != "sfc") continue;
if(emulator->load(media.id)) {
screenshot = {}; screenshot = {};
frameAdvance = false; frameAdvance = false;
if(!verified() && settingsWindow->advanced.warnOnUnverifiedGames.checked()) { if(!verified() && settingsWindow->advanced.warnOnUnverifiedGames.checked()) {
@ -49,12 +47,6 @@ auto Program::load() -> void {
updateVideoPalette(); updateVideoPalette();
updateAudioEffects(); updateAudioEffects();
updateAudioFrequency(); updateAudioFrequency();
}
break;
}
gameQueue = {};
} }
auto Program::loadFile(string location) -> vector<uint8_t> { auto Program::loadFile(string location) -> vector<uint8_t> {

View File

@ -3,12 +3,12 @@ flags += -DSFC_SUPERGAMEBOY
include fc/GNUmakefile include fc/GNUmakefile
include sfc/GNUmakefile include sfc/GNUmakefile
include ms/GNUmakefile #include ms/GNUmakefile
include md/GNUmakefile include md/GNUmakefile
include pce/GNUmakefile #include pce/GNUmakefile
include gb/GNUmakefile include gb/GNUmakefile
include gba/GNUmakefile include gba/GNUmakefile
include ws/GNUmakefile #include ws/GNUmakefile
include processor/GNUmakefile include processor/GNUmakefile
hiro.path := ../hiro hiro.path := ../hiro

View File

@ -15,6 +15,7 @@ auto locate(string name) -> string {
#include <nall/main.hpp> #include <nall/main.hpp>
auto nall::main(string_vector args) -> void { auto nall::main(string_vector args) -> void {
Application::setName("higan"); Application::setName("higan");
Application::setScreenSaver(false);
new Program(args); new Program(args);
Application::run(); Application::run();
} }

View File

@ -192,12 +192,12 @@ InputManager::InputManager() {
for(auto& emulator : program->emulators) { for(auto& emulator : program->emulators) {
InputEmulator inputEmulator; InputEmulator inputEmulator;
inputEmulator.interface = emulator; inputEmulator.interface = emulator;
inputEmulator.name = emulator->information.name; inputEmulator.name = emulator->information().name;
for(auto& port : emulator->ports) { for(auto& port : emulator->ports()) {
InputPort inputPort{port.id, port.name}; InputPort inputPort{port.id, port.name};
for(auto& device : port.devices) { for(auto& device : emulator->devices(port.id)) {
InputDevice inputDevice{device.id, device.name}; InputDevice inputDevice{device.id, device.name};
for(auto& input : device.inputs) { for(auto& input : emulator->inputs(device.id)) {
InputMapping inputMapping; InputMapping inputMapping;
inputMapping.name = input.name; inputMapping.name = input.name;
inputMapping.type = input.type; inputMapping.type = input.type;

View File

@ -6,9 +6,16 @@ struct InputMapping {
auto poll() -> int16; auto poll() -> int16;
auto rumble(bool enable) -> void; auto rumble(bool enable) -> void;
auto isDigital() const -> bool { return type == 0; } using Type = Emulator::Interface::Input::Type;
auto isAnalog() const -> bool { return type == 1; } auto isDigital() const -> bool {
auto isRumble() const -> bool { return type == 2; } return type == Type::Hat || type == Type::Button || type == Type::Trigger || type == Type::Control;
}
auto isAnalog() const -> bool {
return type == Type::Axis;
}
auto isRumble() const -> bool {
return type == Type::Rumble;
}
auto displayName() -> string; auto displayName() -> string;

View File

@ -122,8 +122,8 @@ Presentation::Presentation() {
viewport.setDroppable().onDrop([&](auto locations) { viewport.setDroppable().onDrop([&](auto locations) {
if(!directory::exists(locations(0))) return; if(!directory::exists(locations(0))) return;
program->mediumQueue.append(locations(0)); program->gameQueue.append(locations(0));
program->loadMedium(); program->load();
}); });
onSize([&] { onSize([&] {
@ -143,14 +143,6 @@ Presentation::Presentation() {
Application::Windows::onModalChange([&](bool modal) { Application::Windows::onModalChange([&](bool modal) {
if(modal && audio) audio->clear(); if(modal && audio) audio->clear();
}); });
Application::Windows::onScreenSaver([&]() -> bool {
if(emulator && emulator->loaded()) {
if(program->pause) return true;
if(!program->focused() && settingsManager->input.pauseEmulation.checked()) return true;
return false;
}
return true;
});
#endif #endif
#if defined(PLATFORM_MACOS) #if defined(PLATFORM_MACOS)
@ -164,9 +156,10 @@ Presentation::Presentation() {
auto Presentation::updateEmulatorMenu() -> void { auto Presentation::updateEmulatorMenu() -> void {
if(!emulator) return; if(!emulator) return;
auto information = emulator->information();
systemMenu.reset(); systemMenu.reset();
for(auto& port : emulator->ports) { for(auto& port : emulator->ports()) {
Menu menu{&systemMenu}; Menu menu{&systemMenu};
menu.setProperty("portID", port.id); menu.setProperty("portID", port.id);
menu.setText(port.name); menu.setText(port.name);
@ -176,12 +169,12 @@ auto Presentation::updateEmulatorMenu() -> void {
menu.setIcon(Icon::Device::Joypad); menu.setIcon(Icon::Device::Joypad);
} }
auto path = string{emulator->information.name, "/", port.name}.replace(" ", ""); auto path = string{information.name, "/", port.name}.replace(" ", "");
auto deviceName = settings(path).text(); auto deviceName = settings(path).text();
auto deviceID = emulator->connected(port.id); auto deviceID = emulator->connected(port.id);
Group devices; Group devices;
for(auto& device : port.devices) { for(auto& device : emulator->devices(port.id)) {
MenuRadioItem item{&menu}; MenuRadioItem item{&menu};
item.setProperty("deviceID", device.id); item.setProperty("deviceID", device.id);
item.setText(device.name); item.setText(device.name);
@ -205,7 +198,7 @@ auto Presentation::updateEmulatorMenu() -> void {
systemMenu.append(MenuSeparator()); systemMenu.append(MenuSeparator());
} }
if(emulator->information.resettable) { if(information.resettable) {
systemMenu.append(MenuItem().setText("Soft Reset").setIcon(Icon::Action::Refresh).onActivate([&] { systemMenu.append(MenuItem().setText("Soft Reset").setIcon(Icon::Action::Refresh).onActivate([&] {
program->softReset(); program->softReset();
})); }));
@ -216,7 +209,7 @@ auto Presentation::updateEmulatorMenu() -> void {
})); }));
systemMenu.append(MenuItem().setText("Unload").setIcon(Icon::Media::Eject).onActivate([&] { systemMenu.append(MenuItem().setText("Unload").setIcon(Icon::Media::Eject).onActivate([&] {
program->unloadMedium(); program->unload();
})); }));
updateEmulatorDeviceSelections(); updateEmulatorDeviceSelections();
@ -225,7 +218,7 @@ auto Presentation::updateEmulatorMenu() -> void {
auto Presentation::updateEmulatorDeviceSelections() -> void { auto Presentation::updateEmulatorDeviceSelections() -> void {
if(!emulator) return; if(!emulator) return;
for(auto& port : emulator->ports) { for(auto& port : emulator->ports()) {
for(auto& action : systemMenu->actions()) { for(auto& action : systemMenu->actions()) {
auto portID = action.property("portID"); auto portID = action.property("portID");
if(portID && portID.natural() == port.id) { if(portID && portID.natural() == port.id) {
@ -293,11 +286,11 @@ auto Presentation::resizeViewport(bool resizeWindow) -> void {
double emulatorHeight = 240; double emulatorHeight = 240;
double aspectCorrection = 1.0; double aspectCorrection = 1.0;
if(emulator) { if(emulator) {
auto information = emulator->videoInformation(); auto display = emulator->display();
emulatorWidth = information.width; emulatorWidth = display.width;
emulatorHeight = information.height; emulatorHeight = display.height;
aspectCorrection = information.aspectCorrection; aspectCorrection = display.aspectCorrection;
if(emulator->information.overscan) { if(display.type == Emulator::Interface::Display::Type::CRT) {
uint overscanHorizontal = settings["Video/Overscan/Horizontal"].natural(); uint overscanHorizontal = settings["Video/Overscan/Horizontal"].natural();
uint overscanVertical = settings["Video/Overscan/Vertical"].natural(); uint overscanVertical = settings["Video/Overscan/Vertical"].natural();
emulatorWidth -= overscanHorizontal * 2; emulatorWidth -= overscanHorizontal * 2;
@ -387,37 +380,38 @@ auto Presentation::loadSystems() -> void {
systemsMenu.reset(); systemsMenu.reset();
for(auto system : settings.find("Systems/System")) { for(auto system : settings.find("Systems/System")) {
if(!system["Visible"].boolean()) continue; if(!system["Visible"].boolean()) continue;
MenuItem item; MenuItem item{&systemsMenu};
string name = system.text(); string name = system.text();
string filename = system["Load"].text(); string filename = system["Load"].text();
string load = Location::base(filename).trimRight("/", 1L); string load = Location::base(filename).trimRight("/", 1L);
string alias = system["Alias"].text(); string alias = system["Alias"].text();
item item.setIcon(load ? Icon::Emblem::Folder : Icon::Device::Storage);
.setIcon(load ? Icon::Emblem::Folder : Icon::Device::Storage) item.setText({alias ? alias : load ? load : name, " ..."});
.setText({alias ? alias : load ? load : name, " ..."}).onActivate([=] { item.onActivate([=] {
for(auto& emulator : program->emulators) { for(auto& emulator : program->emulators) {
if(name == emulator->information.name) { auto information = emulator->information();
if(filename) program->mediumQueue.append(filename); if(name == information.name) {
program->loadMedium(*emulator, emulator->media(0)); if(filename) program->gameQueue.append(filename);
program->load(*emulator);
break; break;
} }
} }
}); });
systemsMenu.append(item);
} }
//add icarus menu option -- but only if icarus binary is present //add icarus menu option -- but only if icarus binary is present
if(execute("icarus", "--name").output.strip() == "icarus") { if(execute("icarus", "--name").output.strip() == "icarus") {
if(systemsMenu.actionCount()) systemsMenu.append(MenuSeparator()); if(systemsMenu.actionCount()) systemsMenu.append(MenuSeparator());
systemsMenu.append(MenuItem() MenuItem item{&systemsMenu};
.setIcon(Icon::Emblem::File) item.setIcon(Icon::Emblem::File);
.setText("Load ROM File ...").onActivate([&] { item.setText("Load ROM File ...");
item.onActivate([&] {
audio->clear(); audio->clear();
if(auto location = execute("icarus", "--import")) { if(auto location = execute("icarus", "--import")) {
program->mediumQueue.append(location.output.strip()); program->gameQueue.append(location.output.strip());
program->loadMedium(); program->load();
} }
})); });
} }
} }

View File

@ -1,29 +1,28 @@
auto Program::loadMedium() -> void { auto Program::load() -> void {
if(!mediumQueue) return; if(!gameQueue) return;
string location = mediumQueue.left(); string location = gameQueue.left();
string type = Location::suffix(location).trimLeft(".", 1L); string extension = Location::suffix(location).trimLeft(".", 1L);
for(auto& emulator : emulators) { for(auto& emulator : emulators) {
for(auto& medium : emulator->media) { auto information = emulator->information();
if(medium.type != type) continue; if(information.extension == extension) return load(*emulator);
return loadMedium(*emulator, medium);
}
} }
mediumQueue.reset(); gameQueue.reset();
} }
auto Program::loadMedium(Emulator::Interface& interface, const Emulator::Interface::Medium& medium) -> void { auto Program::load(Emulator::Interface& interface) -> void {
unloadMedium(); unload();
mediumPaths.append(locate({"systems/", medium.name, ".sys/"})); auto information = interface.information();
gamePaths.append(locate({"systems/", information.name, ".sys/"}));
inputManager->bind(emulator = &interface); inputManager->bind(emulator = &interface);
presentation->updateEmulatorMenu(); presentation->updateEmulatorMenu();
if(!emulator->load(medium.id)) { if(!emulator->load()) {
emulator = nullptr; emulator = nullptr;
mediumPaths.reset(); gamePaths.reset();
return; return;
} }
emulator->power(); emulator->power();
@ -35,7 +34,7 @@ auto Program::loadMedium(Emulator::Interface& interface, const Emulator::Interfa
presentation->resizeViewport(); presentation->resizeViewport();
presentation->setTitle(emulator->title()); presentation->setTitle(emulator->title());
presentation->systemMenu.setText(medium.name).setVisible(true); presentation->systemMenu.setText(information.name).setVisible(true);
presentation->toolsMenu.setVisible(true); presentation->toolsMenu.setVisible(true);
toolsManager->cheatEditor.loadCheats(); toolsManager->cheatEditor.loadCheats();
toolsManager->stateManager.doRefresh(); toolsManager->stateManager.doRefresh();
@ -43,7 +42,7 @@ auto Program::loadMedium(Emulator::Interface& interface, const Emulator::Interfa
toolsManager->gameNotes.loadNotes(); toolsManager->gameNotes.loadNotes();
} }
auto Program::unloadMedium() -> void { auto Program::unload() -> void {
if(!emulator) return; if(!emulator) return;
presentation->clearViewport(); presentation->clearViewport();
@ -51,7 +50,7 @@ auto Program::unloadMedium() -> void {
toolsManager->gameNotes.saveNotes(); toolsManager->gameNotes.saveNotes();
emulator->unload(); emulator->unload();
emulator = nullptr; emulator = nullptr;
mediumPaths.reset(); gamePaths.reset();
presentation->resizeViewport(); presentation->resizeViewport();
presentation->setTitle({"higan v", Emulator::Version}); presentation->setTitle({"higan v", Emulator::Version});

View File

@ -1,5 +1,5 @@
auto Program::path(uint id) -> string { auto Program::path(uint id) -> string {
return mediumPaths(id); return gamePaths(id);
} }
auto Program::open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file { auto Program::open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file {
@ -25,8 +25,8 @@ auto Program::open(uint id, string name, vfs::file::mode mode, bool required) ->
auto Program::load(uint id, string name, string type, string_vector options) -> Emulator::Platform::Load { auto Program::load(uint id, string name, string type, string_vector options) -> Emulator::Platform::Load {
string location, option; string location, option;
if(mediumQueue) { if(gameQueue) {
auto entry = mediumQueue.takeLeft().split("|", 1L); auto entry = gameQueue.takeLeft().split("|", 1L);
location = entry.right(); location = entry.right();
if(entry.size() == 1) option = options(0); if(entry.size() == 1) option = options(0);
if(entry.size() == 2) option = entry.left(); if(entry.size() == 2) option = entry.left();
@ -41,12 +41,12 @@ auto Program::load(uint id, string name, string type, string_vector options) ->
option = dialog.option(); option = dialog.option();
} }
if(!directory::exists(location)) { if(!directory::exists(location)) {
mediumQueue.reset(); gameQueue.reset();
return {}; return {};
} }
uint pathID = mediumPaths.size(); uint pathID = gamePaths.size();
mediumPaths.append(location); gamePaths.append(location);
return {pathID, option}; return {pathID, option};
} }
@ -56,12 +56,12 @@ auto Program::videoRefresh(const uint32* data, uint pitch, uint width, uint heig
pitch >>= 2; pitch >>= 2;
if(emulator->information.overscan) { auto display = emulator->display();
if(display.type == Emulator::Interface::Display::Type::CRT) {
uint overscanHorizontal = settings["Video/Overscan/Horizontal"].natural(); uint overscanHorizontal = settings["Video/Overscan/Horizontal"].natural();
uint overscanVertical = settings["Video/Overscan/Vertical"].natural(); uint overscanVertical = settings["Video/Overscan/Vertical"].natural();
auto information = emulator->videoInformation(); overscanHorizontal *= display.internalWidth / display.width;
overscanHorizontal *= information.internalWidth / information.width; overscanVertical *= display.internalHeight / display.height;
overscanVertical *= information.internalHeight / information.height;
data += overscanVertical * pitch + overscanHorizontal; data += overscanVertical * pitch + overscanHorizontal;
width -= overscanHorizontal * 2; width -= overscanHorizontal * 2;
height -= overscanVertical * 2; height -= overscanVertical * 2;

View File

@ -1,14 +1,14 @@
#include "../higan.hpp" #include "../higan.hpp"
#include <fc/interface/interface.hpp> #include <fc/interface/interface.hpp>
#include <sfc/interface/interface.hpp> #include <sfc/interface/interface.hpp>
#include <ms/interface/interface.hpp> //#include <ms/interface/interface.hpp>
#include <md/interface/interface.hpp> #include <md/interface/interface.hpp>
#include <pce/interface/interface.hpp> //#include <pce/interface/interface.hpp>
#include <gb/interface/interface.hpp> #include <gb/interface/interface.hpp>
#include <gba/interface/interface.hpp> #include <gba/interface/interface.hpp>
#include <ws/interface/interface.hpp> //#include <ws/interface/interface.hpp>
#include "platform.cpp" #include "platform.cpp"
#include "medium.cpp" #include "game.cpp"
#include "state.cpp" #include "state.cpp"
#include "utility.cpp" #include "utility.cpp"
unique_pointer<Program> program; unique_pointer<Program> program;
@ -19,17 +19,17 @@ Program::Program(string_vector args) {
Emulator::platform = this; Emulator::platform = this;
emulators.append(new Famicom::Interface); emulators.append(new Famicom::Interface);
emulators.append(new SuperFamicom::Interface); emulators.append(new SuperFamicom::Interface);
emulators.append(new MasterSystem::MasterSystemInterface); // emulators.append(new MasterSystem::MasterSystemInterface);
emulators.append(new MegaDrive::Interface); emulators.append(new MegaDrive::Interface);
emulators.append(new PCEngine::PCEngineInterface); // emulators.append(new PCEngine::PCEngineInterface);
emulators.append(new PCEngine::SuperGrafxInterface); // emulators.append(new PCEngine::SuperGrafxInterface);
emulators.append(new GameBoy::GameBoyInterface); emulators.append(new GameBoy::GameBoyInterface);
emulators.append(new GameBoy::GameBoyColorInterface); emulators.append(new GameBoy::GameBoyColorInterface);
emulators.append(new GameBoyAdvance::Interface); emulators.append(new GameBoyAdvance::Interface);
emulators.append(new MasterSystem::GameGearInterface); // emulators.append(new MasterSystem::GameGearInterface);
emulators.append(new WonderSwan::WonderSwanInterface); // emulators.append(new WonderSwan::WonderSwanInterface);
emulators.append(new WonderSwan::WonderSwanColorInterface); // emulators.append(new WonderSwan::WonderSwanColorInterface);
emulators.append(new WonderSwan::PocketChallengeV2Interface); // emulators.append(new WonderSwan::PocketChallengeV2Interface);
new Presentation; new Presentation;
presentation->setVisible(); presentation->setVisible();
@ -68,14 +68,14 @@ Program::Program(string_vector args) {
presentation->toggleFullScreen(); presentation->toggleFullScreen();
} else if(directory::exists(argument.split("|", 1L).right())) { } else if(directory::exists(argument.split("|", 1L).right())) {
if(!argument.transform("\\", "/").endsWith("/")) argument.append("/"); if(!argument.transform("\\", "/").endsWith("/")) argument.append("/");
mediumQueue.append(argument); gameQueue.append(argument);
} else if(file::exists(argument)) { } else if(file::exists(argument)) {
if(auto result = execute("icarus", "--import", argument)) { if(auto result = execute("icarus", "--import", argument)) {
mediumQueue.append(result.output.strip()); gameQueue.append(result.output.strip());
} }
} }
} }
loadMedium(); if(gameQueue) load();
Application::onMain({&Program::main, this}); Application::onMain({&Program::main, this});
} }
@ -107,7 +107,7 @@ auto Program::main() -> void {
auto Program::quit() -> void { auto Program::quit() -> void {
hasQuit = true; hasQuit = true;
unloadMedium(); unload();
settings.save(); settings.save();
inputManager->quit(); inputManager->quit();
video.reset(); video.reset();

View File

@ -15,10 +15,10 @@ struct Program : Emulator::Platform {
auto dipSettings(Markup::Node node) -> uint override; auto dipSettings(Markup::Node node) -> uint override;
auto notify(string text) -> void override; auto notify(string text) -> void override;
//medium.cpp //game.cpp
auto loadMedium() -> void; auto load() -> void;
auto loadMedium(Emulator::Interface& interface, const Emulator::Interface::Medium& medium) -> void; auto load(Emulator::Interface& interface) -> void;
auto unloadMedium() -> void; auto unload() -> void;
//state.cpp //state.cpp
auto stateName(uint slot, bool managed = false) -> string; auto stateName(uint slot, bool managed = false) -> string;
@ -47,8 +47,8 @@ struct Program : Emulator::Platform {
vector<Emulator::Interface*> emulators; vector<Emulator::Interface*> emulators;
vector<string> mediumQueue; //for command-line and drag-and-drop loading vector<string> gameQueue; //for command-line and drag-and-drop loading
vector<string> mediumPaths; //for keeping track of loaded folder locations vector<string> gamePaths; //for keeping track of loaded folder locations
time_t autoSaveTime = 0; //for automatically saving RAM periodically time_t autoSaveTime = 0; //for automatically saving RAM periodically

View File

@ -1,6 +1,6 @@
auto Program::stateName(uint slot, bool managed) -> string { auto Program::stateName(uint slot, bool managed) -> string {
return { return {
mediumPaths(1), "higan/states/", gamePaths(1), "higan/states/",
managed ? "managed/" : "quick/", managed ? "managed/" : "quick/",
"slot-", slot, ".bst" "slot-", slot, ".bst"
}; };

View File

@ -63,8 +63,7 @@ auto Program::initializeInputDriver() -> void {
} }
auto Program::softReset() -> void { auto Program::softReset() -> void {
if(!emulator) return; if(!emulator || !emulator->information().resettable) return;
if(!emulator->information.resettable) return;
emulator->reset(); emulator->reset();
showMessage("System has been soft reset"); showMessage("System has been soft reset");
} }

View File

@ -4,15 +4,13 @@ SystemProperties::SystemProperties() {
layout.setPadding(5); layout.setPadding(5);
systemLabel.setAlignment(1.0).setText("System:"); systemLabel.setAlignment(1.0).setText("System:");
for(auto& emulator : program->emulators) { for(auto& emulator : program->emulators) {
systemOption.append(ComboButtonItem().setText(emulator->information.name)); systemOption.append(ComboButtonItem().setText(emulator->information().name));
} }
loadLabel.setAlignment(1.0).setText("Load:"); loadLabel.setAlignment(1.0).setText("Load:");
loadBrowse.setText("Browse ...").onActivate([&] { loadBrowse.setText("Browse ...").onActivate([&] {
string filters = "Games|"; string filters = "Games|";
for(auto& emulator : program->emulators) { for(auto& emulator : program->emulators) {
for(auto& media : emulator->media) { filters.append("*.", emulator->information().extension, ":");
filters.append("*.", media.type, ":");
}
} }
filters.trimRight(":", 1L); filters.trimRight(":", 1L);
if(auto location = BrowserDialog() if(auto location = BrowserDialog()
@ -21,17 +19,13 @@ SystemProperties::SystemProperties() {
.setFilters(filters) .setFilters(filters)
.openFolder()) { .openFolder()) {
loadEdit.setText(location); loadEdit.setText(location);
//change system option to match the media selected //change system option to match the game selected
auto suffix = Location::suffix(location).trimLeft(".", 1L); auto extension = Location::suffix(location).trimLeft(".", 1L);
for(auto& emulator : program->emulators) { for(auto& emulator : program->emulators) {
for(auto& media : emulator->media) { auto information = emulator->information();
if(media.type == suffix) { if(information.extension == extension) {
for(auto item : systemOption.items()) { for(auto item : systemOption.items()) {
if(item.text() == emulator->information.name) { if(item.text() == information.name) return item.setSelected(), void();
item.setSelected();
return;
}
}
} }
} }
} }

View File

@ -125,7 +125,7 @@ auto CheatEditor::addCode(const string& code, const string& description, bool en
auto CheatEditor::loadCheats() -> void { auto CheatEditor::loadCheats() -> void {
doReset(true); doReset(true);
auto contents = string::read({program->mediumPaths(1), "higan/cheats.bml"}); auto contents = string::read({program->gamePaths(1), "higan/cheats.bml"});
auto document = BML::unserialize(contents); auto document = BML::unserialize(contents);
for(auto cheat : document["cartridge"].find("cheat")) { for(auto cheat : document["cartridge"].find("cheat")) {
if(!addCode(cheat["code"].text(), cheat["description"].text(), (bool)cheat["enabled"])) break; if(!addCode(cheat["code"].text(), cheat["description"].text(), (bool)cheat["enabled"])) break;
@ -146,10 +146,10 @@ auto CheatEditor::saveCheats() -> void {
count++; count++;
} }
if(count) { if(count) {
directory::create({program->mediumPaths(1), "higan/"}); directory::create({program->gamePaths(1), "higan/"});
file::write({program->mediumPaths(1), "higan/cheats.bml"}, document); file::write({program->gamePaths(1), "higan/cheats.bml"}, document);
} else { } else {
file::remove({program->mediumPaths(1), "higan/cheats.bml"}); file::remove({program->gamePaths(1), "higan/cheats.bml"});
} }
doReset(true); doReset(true);
} }

View File

@ -7,16 +7,16 @@ GameNotes::GameNotes(TabFrame* parent) : TabFrameItem(parent) {
} }
auto GameNotes::loadNotes() -> void { auto GameNotes::loadNotes() -> void {
auto contents = string::read({program->mediumPaths(1), "higan/notes.txt"}); auto contents = string::read({program->gamePaths(1), "higan/notes.txt"});
notes.setText(contents); notes.setText(contents);
} }
auto GameNotes::saveNotes() -> void { auto GameNotes::saveNotes() -> void {
auto contents = notes.text(); auto contents = notes.text();
if(contents) { if(contents) {
directory::create({program->mediumPaths(1), "higan/"}); directory::create({program->gamePaths(1), "higan/"});
file::write({program->mediumPaths(1), "higan/notes.txt"}, contents); file::write({program->gamePaths(1), "higan/notes.txt"}, contents);
} else { } else {
file::remove({program->mediumPaths(1), "higan/notes.txt"}); file::remove({program->gamePaths(1), "higan/notes.txt"});
} }
} }

View File

@ -30,10 +30,10 @@ auto Video::setPalette() -> void {
if(!interface) return; if(!interface) return;
delete palette; delete palette;
colors = interface->videoColors(); colors = interface->display().colors;
palette = new uint32[colors]; palette = new uint32[colors];
for(auto index : range(colors)) { for(auto index : range(colors)) {
uint64 color = interface->videoColor(index); uint64 color = interface->color(index);
uint16 b = color.bits( 0,15); uint16 b = color.bits( 0,15);
uint16 g = color.bits(16,31); uint16 g = color.bits(16,31);
uint16 r = color.bits(32,47); uint16 r = color.bits(32,47);

View File

@ -82,6 +82,10 @@ auto pApplication::quit() -> void {
} }
} }
auto pApplication::setScreenSaver(bool screenSaver) -> void {
//TODO: not implemented
}
auto pApplication::initialize() -> void { auto pApplication::initialize() -> void {
@autoreleasepool { @autoreleasepool {
[NSApplication sharedApplication]; [NSApplication sharedApplication];

View File

@ -15,6 +15,7 @@ struct pApplication {
static auto pendingEvents() -> bool; static auto pendingEvents() -> bool;
static auto processEvents() -> void; static auto processEvents() -> void;
static auto quit() -> void; static auto quit() -> void;
static auto setScreenSaver(bool screenSaver) -> void;
static auto initialize() -> void; static auto initialize() -> void;
}; };

View File

@ -51,6 +51,10 @@ auto Application::scale(float value) -> float {
return value * state.scale; return value * state.scale;
} }
auto Application::screenSaver() -> bool {
return state.screenSaver;
}
auto Application::setFont(const Font& font) -> void { auto Application::setFont(const Font& font) -> void {
state.font = font; state.font = font;
} }
@ -63,6 +67,11 @@ auto Application::setScale(float scale) -> void {
state.scale = scale; state.scale = scale;
} }
auto Application::setScreenSaver(bool screenSaver) -> void {
state.screenSaver = screenSaver;
pApplication::setScreenSaver(screenSaver);
}
auto Application::unscale(float value) -> float { auto Application::unscale(float value) -> float {
return value * (1.0 / state.scale); return value * (1.0 / state.scale);
} }
@ -74,19 +83,10 @@ auto Application::Windows::doModalChange(bool modal) -> void {
if(state.windows.onModalChange) return state.windows.onModalChange(modal); if(state.windows.onModalChange) return state.windows.onModalChange(modal);
} }
auto Application::Windows::doScreenSaver() -> bool {
if(state.windows.onScreenSaver) return state.windows.onScreenSaver();
return true; //true = allow screen saver (default); false = suppress screen saver
}
auto Application::Windows::onModalChange(const function<void (bool)>& callback) -> void { auto Application::Windows::onModalChange(const function<void (bool)>& callback) -> void {
state.windows.onModalChange = callback; state.windows.onModalChange = callback;
} }
auto Application::Windows::onScreenSaver(const function<bool ()>& callback) -> void {
state.windows.onScreenSaver = callback;
}
//Cocoa //Cocoa
//===== //=====

70
hiro/core/application.hpp Normal file
View File

@ -0,0 +1,70 @@
#if defined(Hiro_Application)
struct Application {
Application() = delete;
static auto doMain() -> void;
static auto font() -> Font;
static auto locale() -> Locale&;
static auto modal() -> bool;
static auto name() -> string;
static auto onMain(const function<void ()>& callback = {}) -> void;
static auto run() -> void;
static auto scale() -> float;
static auto scale(float value) -> float;
static auto pendingEvents() -> bool;
static auto processEvents() -> void;
static auto quit() -> void;
static auto screenSaver() -> bool;
static auto setFont(const Font& font = {}) -> void;
static auto setName(const string& name = "") -> void;
static auto setScale(float scale = 1.0) -> void;
static auto setScreenSaver(bool screenSaver = true) -> void;
static auto unscale(float value) -> float;
struct Windows {
static auto doModalChange(bool modal) -> void;
static auto onModalChange(const function<void (bool)>& callback = {}) -> void;
};
struct Cocoa {
static auto doAbout() -> void;
static auto doActivate() -> void;
static auto doPreferences() -> void;
static auto doQuit() -> void;
static auto onAbout(const function<void ()>& callback = {}) -> void;
static auto onActivate(const function<void ()>& callback = {}) -> void;
static auto onPreferences(const function<void ()>& callback = {}) -> void;
static auto onQuit(const function<void ()>& callback = {}) -> void;
};
struct Namespace : Locale::Namespace {
Namespace(const string& value) : Locale::Namespace(Application::locale(), value) {}
};
//private:
struct State {
Font font;
Locale locale;
int modal = 0;
string name;
function<void ()> onMain;
bool quit = false;
float scale = 1.0;
bool screenSaver = true;
struct Windows {
function<void (bool)> onModalChange;
function<bool ()> onScreenSaver;
} windows;
struct Cocoa {
function<void ()> onAbout;
function<void ()> onActivate;
function<void ()> onPreferences;
function<void ()> onQuit;
} cocoa;
};
static State state;
static auto initialize() -> void;
};
#endif

View File

@ -7,6 +7,7 @@
#include <nall/maybe.hpp> #include <nall/maybe.hpp>
#include <nall/path.hpp> #include <nall/path.hpp>
#include <nall/range.hpp> #include <nall/range.hpp>
#include <nall/run.hpp>
#include <nall/set.hpp> #include <nall/set.hpp>
#include <nall/shared-pointer.hpp> #include <nall/shared-pointer.hpp>
#include <nall/stdint.hpp> #include <nall/stdint.hpp>
@ -369,84 +370,8 @@ struct Hotkey {
}; };
#endif #endif
#if defined(Hiro_Application) #include "application.hpp"
struct Application { #include "desktop.hpp"
Application() = delete;
static auto doMain() -> void;
static auto font() -> Font;
static auto locale() -> Locale&;
static auto modal() -> bool;
static auto name() -> string;
static auto onMain(const function<void ()>& callback = {}) -> void;
static auto run() -> void;
static auto scale() -> float;
static auto scale(float value) -> float;
static auto pendingEvents() -> bool;
static auto processEvents() -> void;
static auto quit() -> void;
static auto setFont(const Font& font = {}) -> void;
static auto setName(const string& name = "") -> void;
static auto setScale(float scale = 1.0) -> void;
static auto unscale(float value) -> float;
struct Windows {
static auto doModalChange(bool modal) -> void;
static auto doScreenSaver() -> bool;
static auto onModalChange(const function<void (bool)>& callback = {}) -> void;
static auto onScreenSaver(const function<bool ()>& callback = {}) -> void;
};
struct Cocoa {
static auto doAbout() -> void;
static auto doActivate() -> void;
static auto doPreferences() -> void;
static auto doQuit() -> void;
static auto onAbout(const function<void ()>& callback = {}) -> void;
static auto onActivate(const function<void ()>& callback = {}) -> void;
static auto onPreferences(const function<void ()>& callback = {}) -> void;
static auto onQuit(const function<void ()>& callback = {}) -> void;
};
struct Namespace : Locale::Namespace {
Namespace(const string& value) : Locale::Namespace(Application::locale(), value) {}
};
//private:
struct State {
Font font;
Locale locale;
int modal = 0;
string name;
function<void ()> onMain;
bool quit = false;
float scale = 1.0;
struct Windows {
function<void (bool)> onModalChange;
function<bool ()> onScreenSaver;
} windows;
struct Cocoa {
function<void ()> onAbout;
function<void ()> onActivate;
function<void ()> onPreferences;
function<void ()> onQuit;
} cocoa;
};
static State state;
static auto initialize() -> void;
};
#endif
#if defined(Hiro_Desktop)
struct Desktop {
Desktop() = delete;
static auto size() -> Size;
static auto workspace() -> Geometry;
};
#endif
#if defined(Hiro_Monitor) #if defined(Hiro_Monitor)
struct Monitor { struct Monitor {

8
hiro/core/desktop.hpp Normal file
View File

@ -0,0 +1,8 @@
#if defined(Hiro_Desktop)
struct Desktop {
Desktop() = delete;
static auto size() -> Size;
static auto workspace() -> Geometry;
};
#endif

View File

@ -6,6 +6,7 @@ vector<pWindow*> pApplication::windows;
#if defined(DISPLAY_XORG) #if defined(DISPLAY_XORG)
XlibDisplay* pApplication::display = nullptr; XlibDisplay* pApplication::display = nullptr;
bool pApplication::xdgScreenSaver = false;
#endif #endif
auto pApplication::run() -> void { auto pApplication::run() -> void {
@ -29,16 +30,21 @@ auto pApplication::quit() -> void {
if(gtk_main_level()) gtk_main_quit(); if(gtk_main_level()) gtk_main_quit();
#if defined(DISPLAY_XORG) #if defined(DISPLAY_XORG)
//TODO: Keyboard::poll() is being called after Application::quit(); XCloseDisplay(display);
//so if display is closed; this causes a segfault display = nullptr;
//XCloseDisplay(display); #endif
//display = nullptr; }
auto pApplication::setScreenSaver(bool screenSaver) -> void {
#if defined(DISPLAY_XORG)
for(auto& window : windows) window->_setScreenSaver(screenSaver);
#endif #endif
} }
auto pApplication::initialize() -> void { auto pApplication::initialize() -> void {
#if defined(DISPLAY_XORG) #if defined(DISPLAY_XORG)
display = XOpenDisplay(nullptr); display = XOpenDisplay(nullptr);
xdgScreenSaver = (bool)execute("xdg-screensaver", "--version").output.find("xdg-screensaver");
#endif #endif
//set WM_CLASS to Application::name() //set WM_CLASS to Application::name()

View File

@ -7,6 +7,7 @@ struct pApplication {
static auto pendingEvents() -> bool; static auto pendingEvents() -> bool;
static auto processEvents() -> void; static auto processEvents() -> void;
static auto quit() -> void; static auto quit() -> void;
static auto setScreenSaver(bool screenSaver) -> void;
static auto initialize() -> void; static auto initialize() -> void;
@ -14,6 +15,7 @@ struct pApplication {
#if defined(DISPLAY_XORG) #if defined(DISPLAY_XORG)
static XlibDisplay* display; static XlibDisplay* display;
static bool xdgScreenSaver;
#endif #endif
}; };

View File

@ -3,6 +3,8 @@
namespace hiro { namespace hiro {
auto pKeyboard::poll() -> vector<bool> { auto pKeyboard::poll() -> vector<bool> {
if(Application::state.quit) return {};
vector<bool> result; vector<bool> result;
char state[256]; char state[256];
#if defined(DISPLAY_XORG) #if defined(DISPLAY_XORG)

View File

@ -79,14 +79,16 @@ auto pViewport::destruct() -> void {
gtkParent = nullptr; gtkParent = nullptr;
} }
auto pViewport::handle() const -> uintptr_t { auto pViewport::handle() const -> uintptr {
#if defined(DISPLAY_WINDOWS) #if defined(DISPLAY_WINDOWS)
return (uintptr_t)GDK_WINDOW_HWND(gtk_widget_get_window(gtkWidget)); return (uintptr)GDK_WINDOW_HWND(gtk_widget_get_window(gtkWidget));
#endif #endif
#if defined(DISPLAY_XORG) #if defined(DISPLAY_XORG)
return GDK_WINDOW_XID(gtk_widget_get_window(gtkWidget)); return GDK_WINDOW_XID(gtk_widget_get_window(gtkWidget));
#endif #endif
return (uintptr)nullptr;
} }
auto pViewport::setDroppable(bool droppable) -> void { auto pViewport::setDroppable(bool droppable) -> void {

View File

@ -5,7 +5,7 @@ namespace hiro {
struct pViewport : pWidget { struct pViewport : pWidget {
Declare(Viewport, Widget) Declare(Viewport, Widget)
auto handle() const -> uintptr_t; auto handle() const -> uintptr;
auto setDroppable(bool droppable) -> void; auto setDroppable(bool droppable) -> void;
}; };

View File

@ -99,6 +99,10 @@ static auto Window_keyRelease(GtkWidget* widget, GdkEventKey* event, pWindow* p)
return false; return false;
} }
static auto Window_realize(GtkWidget* widget, pWindow* p) -> void {
p->_setScreenSaver(Application::screenSaver());
}
static auto Window_sizeAllocate(GtkWidget* widget, GtkAllocation* allocation, pWindow* p) -> void { static auto Window_sizeAllocate(GtkWidget* widget, GtkAllocation* allocation, pWindow* p) -> void {
p->_synchronizeState(); p->_synchronizeState();
p->_synchronizeGeometry(); p->_synchronizeGeometry();
@ -125,6 +129,10 @@ static auto Window_stateEvent(GtkWidget* widget, GdkEvent* event, pWindow* p) ->
} }
} }
static auto Window_unrealize(GtkWidget* widget, pWindow* p) -> void {
p->_setScreenSaver(true);
}
auto pWindow::construct() -> void { auto pWindow::construct() -> void {
widget = gtk_window_new(GTK_WINDOW_TOPLEVEL); widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_resizable(GTK_WINDOW(widget), true); gtk_window_set_resizable(GTK_WINDOW(widget), true);
@ -186,6 +194,7 @@ auto pWindow::construct() -> void {
g_signal_connect(G_OBJECT(widget), "drag-data-received", G_CALLBACK(Window_drop), (gpointer)this); g_signal_connect(G_OBJECT(widget), "drag-data-received", G_CALLBACK(Window_drop), (gpointer)this);
g_signal_connect(G_OBJECT(widget), "key-press-event", G_CALLBACK(Window_keyPress), (gpointer)this); g_signal_connect(G_OBJECT(widget), "key-press-event", G_CALLBACK(Window_keyPress), (gpointer)this);
g_signal_connect(G_OBJECT(widget), "key-release-event", G_CALLBACK(Window_keyRelease), (gpointer)this); g_signal_connect(G_OBJECT(widget), "key-release-event", G_CALLBACK(Window_keyRelease), (gpointer)this);
g_signal_connect(G_OBJECT(widget), "realize", G_CALLBACK(Window_realize), (gpointer)this);
g_signal_connect(G_OBJECT(formContainer), "size-allocate", G_CALLBACK(Window_sizeAllocate), (gpointer)this); g_signal_connect(G_OBJECT(formContainer), "size-allocate", G_CALLBACK(Window_sizeAllocate), (gpointer)this);
#if HIRO_GTK==2 #if HIRO_GTK==2
g_signal_connect(G_OBJECT(formContainer), "size-request", G_CALLBACK(Window_sizeRequest), (gpointer)this); g_signal_connect(G_OBJECT(formContainer), "size-request", G_CALLBACK(Window_sizeRequest), (gpointer)this);
@ -194,6 +203,7 @@ auto pWindow::construct() -> void {
widgetClass->get_preferred_width = Window_getPreferredWidth; widgetClass->get_preferred_width = Window_getPreferredWidth;
widgetClass->get_preferred_height = Window_getPreferredHeight; widgetClass->get_preferred_height = Window_getPreferredHeight;
#endif #endif
g_signal_connect(G_OBJECT(widget), "unrealize", G_CALLBACK(Window_unrealize), (gpointer)this);
g_signal_connect(G_OBJECT(widget), "window-state-event", G_CALLBACK(Window_stateEvent), (gpointer)this); g_signal_connect(G_OBJECT(widget), "window-state-event", G_CALLBACK(Window_stateEvent), (gpointer)this);
g_object_set_data(G_OBJECT(widget), "hiro::window", (gpointer)this); g_object_set_data(G_OBJECT(widget), "hiro::window", (gpointer)this);
@ -247,6 +257,18 @@ auto pWindow::frameMargin() const -> Geometry {
}; };
} }
auto pWindow::handle() const -> uintptr {
#if defined(DISPLAY_WINDOWS)
return (uintptr)GDK_WINDOW_HWND(gtk_widget_get_window(widget));
#endif
#if defined(DISPLAY_XORG)
return GDK_WINDOW_XID(gtk_widget_get_window(widget));
#endif
return (uintptr)nullptr;
}
auto pWindow::remove(sMenuBar menuBar) -> void { auto pWindow::remove(sMenuBar menuBar) -> void {
_setMenuVisible(false); _setMenuVisible(false);
} }
@ -458,6 +480,19 @@ auto pWindow::_setMenuVisible(bool visible) -> void {
gtk_widget_set_visible(gtkMenu, visible); gtk_widget_set_visible(gtkMenu, visible);
} }
auto pWindow::_setScreenSaver(bool screenSaver) -> void {
if(!gtk_widget_get_realized(widget)) return;
#if defined(DISPLAY_XORG)
if(pApplication::xdgScreenSaver) {
if(this->screenSaver != screenSaver) {
this->screenSaver = screenSaver;
execute("xdg-screensaver", screenSaver ? "resume" : "suspend", string{"0x", hex(handle())});
}
}
#endif
}
auto pWindow::_setStatusEnabled(bool enabled) -> void { auto pWindow::_setStatusEnabled(bool enabled) -> void {
gtk_widget_set_sensitive(gtkStatus, enabled); gtk_widget_set_sensitive(gtkStatus, enabled);
} }

View File

@ -10,6 +10,7 @@ struct pWindow : pObject {
auto append(sStatusBar statusBar) -> void; auto append(sStatusBar statusBar) -> void;
auto focused() const -> bool override; auto focused() const -> bool override;
auto frameMargin() const -> Geometry; auto frameMargin() const -> Geometry;
auto handle() const -> uintptr;
auto remove(sMenuBar menuBar) -> void; auto remove(sMenuBar menuBar) -> void;
auto remove(sSizable sizable) -> void; auto remove(sSizable sizable) -> void;
auto remove(sStatusBar statusBar) -> void; auto remove(sStatusBar statusBar) -> void;
@ -33,6 +34,7 @@ struct pWindow : pObject {
auto _append(mMenu& menu) -> void; auto _append(mMenu& menu) -> void;
auto _menuHeight() const -> int; auto _menuHeight() const -> int;
auto _menuTextHeight() const -> int; auto _menuTextHeight() const -> int;
auto _setScreenSaver(bool screenSaver) -> void;
auto _setIcon(const string& basename) -> bool; auto _setIcon(const string& basename) -> bool;
auto _setMenuEnabled(bool enabled) -> void; auto _setMenuEnabled(bool enabled) -> void;
auto _setMenuFont(const Font& font) -> void; auto _setMenuFont(const Font& font) -> void;
@ -55,6 +57,7 @@ struct pWindow : pObject {
GtkWidget* gtkStatus = nullptr; GtkWidget* gtkStatus = nullptr;
GtkAllocation lastMove = {0}; GtkAllocation lastMove = {0};
GtkAllocation lastSize = {0}; GtkAllocation lastSize = {0};
bool screenSaver = true;
}; };
} }

View File

@ -28,6 +28,10 @@ auto pApplication::quit() -> void {
qtApplication = nullptr; //note: deleting QApplication will crash libQtGui qtApplication = nullptr; //note: deleting QApplication will crash libQtGui
} }
auto pApplication::setScreenSaver(bool screenSaver) -> void {
//TODO: not implemented
}
//this is sadly necessary for things like determining window frame geometry //this is sadly necessary for things like determining window frame geometry
//obviously, it is used as sparingly as possible //obviously, it is used as sparingly as possible
auto pApplication::syncX() -> void { auto pApplication::syncX() -> void {

View File

@ -7,6 +7,7 @@ struct pApplication {
static auto pendingEvents() -> bool; static auto pendingEvents() -> bool;
static auto processEvents() -> void; static auto processEvents() -> void;
static auto quit() -> void; static auto quit() -> void;
static auto setScreenSaver(bool screenSaver) -> void;
static auto initialize() -> void; static auto initialize() -> void;
static auto syncX() -> void; static auto syncX() -> void;

View File

@ -54,6 +54,9 @@ auto pApplication::quit() -> void {
PostQuitMessage(0); PostQuitMessage(0);
} }
auto pApplication::setScreenSaver(bool screenSaver) -> void {
}
auto pApplication::initialize() -> void { auto pApplication::initialize() -> void {
CoInitialize(0); CoInitialize(0);
InitCommonControls(); InitCommonControls();
@ -262,7 +265,7 @@ static auto CALLBACK Application_windowProc(HWND hwnd, UINT msg, WPARAM wparam,
case WM_EXITMENULOOP: case WM_EXITSIZEMOVE: pWindow->onModalEnd(); return false; case WM_EXITMENULOOP: case WM_EXITSIZEMOVE: pWindow->onModalEnd(); return false;
case WM_SYSCOMMAND: case WM_SYSCOMMAND:
if(wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) { if(wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) {
if(!Application::Windows::doScreenSaver()) return 0; if(!Application::screenSaver()) return 0;
} }
} }

View File

@ -7,6 +7,7 @@ struct pApplication {
static auto pendingEvents() -> bool; static auto pendingEvents() -> bool;
static auto processEvents() -> void; static auto processEvents() -> void;
static auto quit() -> void; static auto quit() -> void;
static auto setScreenSaver(bool screenSaver) -> void;
static auto initialize() -> void; static auto initialize() -> void;
}; };