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
build := performance
openmp := true

View File

@ -13,7 +13,7 @@ using namespace nall;
namespace Emulator {
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 License = "GPLv3";
static const string Website = "https://byuu.org/";

View File

@ -3,42 +3,24 @@
namespace Emulator {
struct Interface {
//information
struct Information {
string manufacturer;
string name;
bool overscan = false;
string extension;
bool resettable = false;
} information;
struct Medium {
uint id;
string name;
string type; //extension
};
vector<Medium> media;
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 information() -> Information = 0;
virtual auto manifest() -> 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 height = 0;
uint internalWidth = 0;
@ -46,23 +28,48 @@ struct Interface {
double aspectCorrection = 0;
double refreshRate = 0;
};
virtual auto videoInformation() -> VideoInformation = 0;
virtual auto videoColors() -> uint32 = 0;
virtual auto videoColor(uint32 color) -> uint64 = 0;
virtual auto display() -> Display = 0;
virtual auto color(uint32 color) -> uint64 = 0;
//media interface
virtual auto loaded() -> bool { return false; }
//game interface
virtual auto loaded() -> bool = 0;
virtual auto sha256() -> string { return ""; }
virtual auto load(uint id) -> bool { return false; }
virtual auto save() -> void {}
virtual auto unload() -> void {}
virtual auto load() -> bool = 0;
virtual auto save() -> void = 0;
virtual auto unload() -> void = 0;
//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 connect(uint port, uint device) -> void {}
virtual auto power() -> void {}
virtual auto power() -> void = 0;
virtual auto reset() -> void {}
virtual auto run() -> void {}
virtual auto run() -> void = 0;
//time functions
virtual auto rtc() -> bool { return false; }

View File

@ -2,40 +2,16 @@
namespace Famicom {
#define returns(T) T { return ([&] { struct With : T { With() {
#define $ }}; return With(); })(); }
Settings settings;
Interface::Interface() {
information.manufacturer = "Nintendo";
information.name = "Famicom";
information.overscan = 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::information() -> returns(Information) {
manufacturer = "Nintendo";
name = "Famicom";
resettable = true;
}$
auto Interface::manifest() -> string {
return cartridge.manifest();
@ -45,22 +21,18 @@ auto Interface::title() -> string {
return cartridge.title();
}
auto Interface::videoInformation() -> VideoInformation {
VideoInformation vi;
vi.width = 256;
vi.height = 240;
vi.internalWidth = 256;
vi.internalHeight = 240;
vi.aspectCorrection = 8.0 / 7.0;
vi.refreshRate = system.frequency() / (ppu.vlines() * ppu.rate() * 341.0);
return vi;
}
auto Interface::display() -> returns(Display) {
type = Display::Type::CRT;
colors = 1 << 9;
width = 256;
height = 240;
internalWidth = 256;
internalHeight = 240;
aspectCorrection = 8.0 / 7.0;
refreshRate = system.frequency() / (ppu.vlines() * ppu.rate() * 341.0);
}$
auto Interface::videoColors() -> uint32 {
return 1 << 9;
}
auto Interface::videoColor(uint32 n) -> uint64 {
auto Interface::color(uint32 n) -> uint64 {
double saturation = 2.0;
double hue = 0.0;
double contrast = 1.0;
@ -119,7 +91,7 @@ auto Interface::sha256() -> string {
return cartridge.sha256();
}
auto Interface::load(uint id) -> bool {
auto Interface::load() -> bool {
return system.load(this);
}
@ -132,6 +104,50 @@ auto Interface::unload() -> void {
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 {
if(port == ID::Port::Controller1) return settings.controllerPort1;
if(port == ID::Port::Controller2) return settings.controllerPort2;
@ -191,4 +207,7 @@ auto Interface::set(const string& name, const any& value) -> bool {
return false;
}
#undef returns
#undef $
}

View File

@ -21,21 +21,24 @@ struct ID {
struct Interface : Emulator::Interface {
using Emulator::Interface::load;
Interface();
auto information() -> Information override;
auto manifest() -> string override;
auto title() -> string override;
auto videoInformation() -> VideoInformation override;
auto videoColors() -> uint32 override;
auto videoColor(uint32 color) -> uint64 override;
auto display() -> Display override;
auto color(uint32 color) -> uint64 override;
auto loaded() -> bool override;
auto sha256() -> string override;
auto load(uint id) -> bool override;
auto load() -> bool override;
auto save() -> 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 connect(uint port, uint device) -> void override;
auto power() -> void override;

View File

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

View File

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

View File

@ -2,32 +2,14 @@
namespace GameBoy {
#define returns(T) T { return ([&] { struct With : T { With() {
#define $ }}; return With(); })(); }
SuperGameBoyInterface* superGameBoy = nullptr;
Settings settings;
#include "game-boy.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 {
return cartridge.manifest();
}
@ -36,16 +18,16 @@ auto Interface::title() -> string {
return cartridge.title();
}
auto Interface::videoInformation() -> VideoInformation {
VideoInformation vi;
vi.width = 160;
vi.height = 144;
vi.internalWidth = 160;
vi.internalHeight = 144;
vi.aspectCorrection = 1.0;
vi.refreshRate = (4.0 * 1024.0 * 1024.0) / (154.0 * 456.0);
return vi;
}
auto Interface::display() -> returns(Display) {
type = Display::Type::LCD;
colors = Model::GameBoyColor() ? 1 << 15 : 1 << 2;
width = 160;
height = 144;
internalWidth = 160;
internalHeight = 144;
aspectCorrection = 1.0;
refreshRate = (4.0 * 1024.0 * 1024.0) / (154.0 * 456.0);
}$
auto Interface::loaded() -> bool {
return system.loaded();
@ -64,6 +46,38 @@ auto Interface::unload() -> void {
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 {
system.power();
}
@ -115,4 +129,7 @@ auto Interface::set(const string& name, const any& value) -> bool {
return false;
}
#undef returns
#undef $
}

View File

@ -18,12 +18,10 @@ struct ID {
};
struct Interface : Emulator::Interface {
Interface();
auto manifest() -> string override;
auto title() -> string override;
auto videoInformation() -> VideoInformation override;
auto display() -> Display override;
auto loaded() -> bool override;
auto sha256() -> string override;
@ -31,6 +29,10 @@ struct Interface : Emulator::Interface {
auto save() -> 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 run() -> void override;
@ -47,23 +49,21 @@ struct Interface : Emulator::Interface {
struct GameBoyInterface : Interface {
using Emulator::Interface::load;
GameBoyInterface();
auto information() -> Information override;
auto videoColors() -> uint32 override;
auto videoColor(uint32 color) -> uint64 override;
auto color(uint32 color) -> uint64 override;
auto load(uint id) -> bool override;
auto load() -> bool override;
};
struct GameBoyColorInterface : Interface {
using Emulator::Interface::load;
GameBoyColorInterface();
auto information() -> Information override;
auto videoColors() -> uint32 override;
auto videoColor(uint32 color) -> uint64 override;
auto color(uint32 color) -> uint64 override;
auto load(uint id) -> bool override;
auto load() -> bool override;
};
struct SuperGameBoyInterface {

View File

@ -2,34 +2,15 @@
namespace GameBoyAdvance {
#define returns(T) T { return ([&] { struct With : T { With() {
#define $ }}; return With(); })(); }
Settings settings;
Interface::Interface() {
information.manufacturer = "Nintendo";
information.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::information() -> returns(Information) {
manufacturer = "Nintendo";
name = "Game Boy Advance";
}$
auto Interface::manifest() -> string {
return cartridge.manifest();
@ -39,26 +20,22 @@ auto Interface::title() -> string {
return cartridge.title();
}
auto Interface::videoInformation() -> VideoInformation {
VideoInformation vi;
vi.width = 240;
vi.height = 160;
vi.internalWidth = 240;
vi.internalHeight = 160;
vi.aspectCorrection = 1.0;
vi.refreshRate = system.frequency() / (228.0 * 1232.0);
auto Interface::display() -> returns(Display) {
type = Display::Type::LCD;
colors = 1 << 15;
width = 240;
height = 160;
internalWidth = 240;
internalHeight = 160;
aspectCorrection = 1.0;
refreshRate = system.frequency() / (228.0 * 1232.0);
if(settings.rotateLeft) {
swap(vi.width, vi.height);
swap(vi.internalWidth, vi.internalHeight);
swap(width, height);
swap(internalWidth, internalHeight);
}
return vi;
}
}$
auto Interface::videoColors() -> uint32 {
return 1 << 15;
}
auto Interface::videoColor(uint32 color) -> uint64 {
auto Interface::color(uint32 color) -> uint64 {
uint R = color.bits( 0, 4);
uint G = color.bits( 5, 9);
uint B = color.bits(10,14);
@ -84,7 +61,7 @@ auto Interface::loaded() -> bool {
return system.loaded();
}
auto Interface::load(uint id) -> bool {
auto Interface::load() -> bool {
return system.load(this);
}
@ -97,6 +74,38 @@ auto Interface::unload() -> void {
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 {
system.power();
}
@ -150,4 +159,7 @@ auto Interface::set(const string& name, const any& value) -> bool {
return false;
}
#undef returns
#undef $
}

View File

@ -18,20 +18,23 @@ struct ID {
struct Interface : Emulator::Interface {
using Emulator::Interface::load;
Interface();
auto information() -> Information override;
auto manifest() -> string override;
auto title() -> string override;
auto videoInformation() -> VideoInformation override;
auto videoColors() -> uint32 override;
auto videoColor(uint32 color) -> uint64 override;
auto display() -> Display override;
auto color(uint32 color) -> uint64 override;
auto loaded() -> bool override;
auto load(uint id) -> bool override;
auto load() -> bool override;
auto save() -> 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 run() -> void override;

View File

@ -2,60 +2,16 @@
namespace MegaDrive {
#define returns(T) T { return ([&] { struct With : T { With() {
#define $ }}; return With(); })(); }
Settings settings;
Interface::Interface() {
information.manufacturer = "Sega";
information.name = "Mega Drive";
information.overscan = 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::information() -> returns(Information) {
manufacturer = "Sega";
name = "Mega Drive";
resettable = true;
}$
auto Interface::manifest() -> string {
return cartridge.manifest();
@ -65,22 +21,18 @@ auto Interface::title() -> string {
return cartridge.title();
}
auto Interface::videoInformation() -> VideoInformation {
VideoInformation vi;
vi.width = 320;
vi.height = 240;
vi.internalWidth = 1280;
vi.internalHeight = 480;
vi.aspectCorrection = 1.0;
vi.refreshRate = (system.frequency() / 2.0) / (vdp.frameHeight() * 1710.0);
return vi;
}
auto Interface::display() -> returns(Display) {
type = Display::Type::CRT;
colors = 3 * (1 << 9);
width = 320;
height = 240;
internalWidth = 1280;
internalHeight = 480;
aspectCorrection = 1.0;
refreshRate = (system.frequency() / 2.0) / (vdp.frameHeight() * 1710.0);
}$
auto Interface::videoColors() -> uint32 {
return 3 * (1 << 9);
}
auto Interface::videoColor(uint32 color) -> uint64 {
auto Interface::color(uint32 color) -> uint64 {
uint R = color.bits(0, 2);
uint G = color.bits(3, 5);
uint B = color.bits(6, 8);
@ -103,7 +55,7 @@ auto Interface::loaded() -> bool {
return system.loaded();
}
auto Interface::load(uint id) -> bool {
auto Interface::load() -> bool {
return system.load(this);
}
@ -116,6 +68,67 @@ auto Interface::unload() -> void {
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 {
if(port == ID::Port::Controller1) return settings.controllerPort1;
if(port == ID::Port::Controller2) return settings.controllerPort2;
@ -166,4 +179,7 @@ auto Interface::set(const string& name, const any& value) -> bool {
return false;
}
#undef returns
#undef $
}

View File

@ -22,20 +22,23 @@ struct ID {
struct Interface : Emulator::Interface {
using Emulator::Interface::load;
Interface();
auto information() -> Information override;
auto manifest() -> string override;
auto title() -> string override;
auto videoInformation() -> VideoInformation override;
auto videoColors() -> uint32 override;
auto videoColor(uint32 color) -> uint64 override;
auto display() -> Display override;
auto color(uint32 color) -> uint64 override;
auto loaded() -> bool override;
auto load(uint id) -> bool override;
auto load() -> bool override;
auto save() -> 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 connect(uint port, uint device) -> void override;
auto power() -> void override;

View File

@ -2,112 +2,17 @@
namespace SuperFamicom {
#define returns(T) T { return ([&] { struct With : T { With() {
#define $ }}; return With(); })(); }
Settings settings;
Interface::Interface() {
information.manufacturer = "Nintendo";
information.name = "Super Famicom";
information.overscan = true;
information.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::information() -> returns(Information) {
manufacturer = "Nintendo";
name = "Super Famicom";
extension = "sfc";
resettable = true;
}$
auto Interface::manifest() -> string {
return cartridge.manifest();
@ -117,23 +22,19 @@ auto Interface::title() -> string {
return cartridge.title();
}
auto Interface::videoInformation() -> VideoInformation {
VideoInformation vi;
vi.width = 256;
vi.height = 240;
vi.internalWidth = 512;
vi.internalHeight = 480;
vi.aspectCorrection = 8.0 / 7.0;
if(Region::NTSC()) vi.refreshRate = system.cpuFrequency() / (262.0 * 1364.0);
if(Region::PAL()) vi.refreshRate = system.cpuFrequency() / (312.0 * 1364.0);
return vi;
}
auto Interface::display() -> returns(Display) {
type = Display::Type::CRT;
colors = 1 << 19;
width = 256;
height = 240;
internalWidth = 512;
internalHeight = 480;
aspectCorrection = 8.0 / 7.0;
if(Region::NTSC()) refreshRate = system.cpuFrequency() / (262.0 * 1364.0);
if(Region::PAL()) refreshRate = system.cpuFrequency() / (312.0 * 1364.0);
}$
auto Interface::videoColors() -> uint32 {
return 1 << 19;
}
auto Interface::videoColor(uint32 color) -> uint64 {
auto Interface::color(uint32 color) -> uint64 {
uint r = color.bits( 0, 4);
uint g = color.bits( 5, 9);
uint b = color.bits(10,14);
@ -169,12 +70,8 @@ auto Interface::sha256() -> string {
return cartridge.sha256();
}
auto Interface::load(uint id) -> bool {
if(id == ID::SuperFamicom) 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::load() -> bool {
return system.load(this);
}
auto Interface::save() -> void {
@ -186,6 +83,121 @@ auto Interface::unload() -> void {
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 {
if(port == ID::Port::Controller1) return settings.controllerPort1;
if(port == ID::Port::Controller2) return settings.controllerPort2;
@ -295,4 +307,7 @@ auto Interface::set(const string& name, const any& value) -> bool {
return false;
}
#undef returns
#undef $
}

View File

@ -33,21 +33,23 @@ struct ID {
struct Interface : Emulator::Interface {
using Emulator::Interface::load;
Interface();
auto information() -> Information;
auto manifest() -> string override;
auto title() -> string override;
auto videoInformation() -> VideoInformation override;
auto videoColors() -> uint32 override;
auto videoColor(uint32 color) -> uint64 override;
auto display() -> Display override;
auto color(uint32 color) -> uint64 override;
auto loaded() -> bool override;
auto sha256() -> string override;
auto load(uint id) -> bool override;
auto load() -> bool override;
auto save() -> 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 connect(uint port, uint device) -> void override;
auto power() -> void override;

View File

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

View File

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

View File

@ -7,9 +7,16 @@ struct InputMapping {
auto rumble(bool enable) -> void;
auto displayName() -> string;
auto isDigital() const -> bool { return type == 0; }
auto isAnalog() const -> bool { return type == 1; }
auto isRumble() const -> bool { return type == 2; }
using Type = Emulator::Interface::Input::Type;
auto isDigital() const -> bool {
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 name; //input name (human readable)

View File

@ -183,14 +183,6 @@ Presentation::Presentation() {
Application::Windows::onModalChange([&](bool modal) {
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
#if defined(PLATFORM_MACOS)
@ -346,7 +338,7 @@ auto Presentation::updateDeviceMenu() -> void {
controllerPort2.reset();
expansionPort.reset();
for(auto& port : emulator->ports) {
for(auto& port : emulator->ports()) {
Menu* menu = nullptr;
if(port.name == "Controller Port 1") menu = &controllerPort1;
if(port.name == "Controller Port 2") menu = &controllerPort2;
@ -358,7 +350,7 @@ auto Presentation::updateDeviceMenu() -> void {
auto deviceID = emulator->connected(port.id);
Group devices;
for(auto& device : port.devices) {
for(auto& device : emulator->devices(port.id)) {
if(port.name == "Expansion Port" && device.name == "21fx") continue;
MenuRadioItem item{menu};
@ -380,7 +372,7 @@ auto Presentation::updateDeviceMenu() -> void {
}
auto Presentation::updateDeviceSelections() -> void {
for(auto& port : emulator->ports) {
for(auto& port : emulator->ports()) {
Menu* menu = nullptr;
if(port.name == "Controller Port 1") menu = &controllerPort1;
if(port.name == "Controller Port 2") menu = &controllerPort2;

View File

@ -1,60 +1,52 @@
auto Program::load() -> void {
unload();
for(auto& media : emulator->media) {
if(media.type != "sfc") continue;
if(emulator->load(media.id)) {
screenshot = {};
frameAdvance = false;
if(!verified() && settingsWindow->advanced.warnOnUnverifiedGames.checked()) {
//todo: MessageDialog crashes with GTK+; unsure the reason why this happens
//once MessageDialog functions, add an "Always" option
if(MessageWindow(
"Warning: this game image is unverified. Running it *may* be a security risk.\n\n"
"Do you wish to run the game anyway?"
).setParent(*presentation).question() == MessageWindow::Response::No) {
emulator->unload();
return showMessage("Game loading cancelled");
}
}
hackCompatibility();
emulator->power();
if(settingsWindow->advanced.autoLoadStateOnLoad.checked()) {
program->loadState("quick/undo");
}
showMessage({
verified() ? "Verified game loaded" : "Game loaded",
appliedPatch() ? " and patch applied" : ""
});
presentation->setTitle(emulator->title());
presentation->resetSystem.setEnabled(true);
presentation->unloadGame.setEnabled(true);
presentation->toolsMenu.setVisible(true);
presentation->speedNormal.setChecked();
presentation->pauseEmulation.setChecked(false);
presentation->updateStatusIcon();
presentation->resizeViewport();
toolsWindow->cheatEditor.loadCheats();
toolsWindow->stateManager.loadStates();
toolsWindow->manifestViewer.loadManifest();
string locations = superFamicom.location;
if(auto location = gameBoy.location) locations.append("|", location);
if(auto location = bsMemory.location) locations.append("|", location);
if(auto location = sufamiTurboA.location) locations.append("|", location);
if(auto location = sufamiTurboB.location) locations.append("|", location);
presentation->addRecentGame(locations);
updateVideoPalette();
updateAudioEffects();
updateAudioFrequency();
}
break;
}
if(!emulator->load()) return;
gameQueue = {};
screenshot = {};
frameAdvance = false;
if(!verified() && settingsWindow->advanced.warnOnUnverifiedGames.checked()) {
//todo: MessageDialog crashes with GTK+; unsure the reason why this happens
//once MessageDialog functions, add an "Always" option
if(MessageWindow(
"Warning: this game image is unverified. Running it *may* be a security risk.\n\n"
"Do you wish to run the game anyway?"
).setParent(*presentation).question() == MessageWindow::Response::No) {
emulator->unload();
return showMessage("Game loading cancelled");
}
}
hackCompatibility();
emulator->power();
if(settingsWindow->advanced.autoLoadStateOnLoad.checked()) {
program->loadState("quick/undo");
}
showMessage({
verified() ? "Verified game loaded" : "Game loaded",
appliedPatch() ? " and patch applied" : ""
});
presentation->setTitle(emulator->title());
presentation->resetSystem.setEnabled(true);
presentation->unloadGame.setEnabled(true);
presentation->toolsMenu.setVisible(true);
presentation->speedNormal.setChecked();
presentation->pauseEmulation.setChecked(false);
presentation->updateStatusIcon();
presentation->resizeViewport();
toolsWindow->cheatEditor.loadCheats();
toolsWindow->stateManager.loadStates();
toolsWindow->manifestViewer.loadManifest();
string locations = superFamicom.location;
if(auto location = gameBoy.location) locations.append("|", location);
if(auto location = bsMemory.location) locations.append("|", location);
if(auto location = sufamiTurboA.location) locations.append("|", location);
if(auto location = sufamiTurboB.location) locations.append("|", location);
presentation->addRecentGame(locations);
updateVideoPalette();
updateAudioEffects();
updateAudioFrequency();
}
auto Program::loadFile(string location) -> vector<uint8_t> {

View File

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

View File

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

View File

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

View File

@ -6,9 +6,16 @@ struct InputMapping {
auto poll() -> int16;
auto rumble(bool enable) -> void;
auto isDigital() const -> bool { return type == 0; }
auto isAnalog() const -> bool { return type == 1; }
auto isRumble() const -> bool { return type == 2; }
using Type = Emulator::Interface::Input::Type;
auto isDigital() const -> bool {
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;

View File

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

View File

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

View File

@ -1,5 +1,5 @@
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 {
@ -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 {
string location, option;
if(mediumQueue) {
auto entry = mediumQueue.takeLeft().split("|", 1L);
if(gameQueue) {
auto entry = gameQueue.takeLeft().split("|", 1L);
location = entry.right();
if(entry.size() == 1) option = options(0);
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();
}
if(!directory::exists(location)) {
mediumQueue.reset();
gameQueue.reset();
return {};
}
uint pathID = mediumPaths.size();
mediumPaths.append(location);
uint pathID = gamePaths.size();
gamePaths.append(location);
return {pathID, option};
}
@ -56,12 +56,12 @@ auto Program::videoRefresh(const uint32* data, uint pitch, uint width, uint heig
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 overscanVertical = settings["Video/Overscan/Vertical"].natural();
auto information = emulator->videoInformation();
overscanHorizontal *= information.internalWidth / information.width;
overscanVertical *= information.internalHeight / information.height;
overscanHorizontal *= display.internalWidth / display.width;
overscanVertical *= display.internalHeight / display.height;
data += overscanVertical * pitch + overscanHorizontal;
width -= overscanHorizontal * 2;
height -= overscanVertical * 2;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,16 +7,16 @@ GameNotes::GameNotes(TabFrame* parent) : TabFrameItem(parent) {
}
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);
}
auto GameNotes::saveNotes() -> void {
auto contents = notes.text();
if(contents) {
directory::create({program->mediumPaths(1), "higan/"});
file::write({program->mediumPaths(1), "higan/notes.txt"}, contents);
directory::create({program->gamePaths(1), "higan/"});
file::write({program->gamePaths(1), "higan/notes.txt"}, contents);
} 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;
delete palette;
colors = interface->videoColors();
colors = interface->display().colors;
palette = new uint32[colors];
for(auto index : range(colors)) {
uint64 color = interface->videoColor(index);
uint64 color = interface->color(index);
uint16 b = color.bits( 0,15);
uint16 g = color.bits(16,31);
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 {
@autoreleasepool {
[NSApplication sharedApplication];

View File

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

View File

@ -51,6 +51,10 @@ auto Application::scale(float value) -> float {
return value * state.scale;
}
auto Application::screenSaver() -> bool {
return state.screenSaver;
}
auto Application::setFont(const Font& font) -> void {
state.font = font;
}
@ -63,6 +67,11 @@ auto Application::setScale(float scale) -> void {
state.scale = scale;
}
auto Application::setScreenSaver(bool screenSaver) -> void {
state.screenSaver = screenSaver;
pApplication::setScreenSaver(screenSaver);
}
auto Application::unscale(float value) -> float {
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);
}
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 {
state.windows.onModalChange = callback;
}
auto Application::Windows::onScreenSaver(const function<bool ()>& callback) -> void {
state.windows.onScreenSaver = callback;
}
//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/path.hpp>
#include <nall/range.hpp>
#include <nall/run.hpp>
#include <nall/set.hpp>
#include <nall/shared-pointer.hpp>
#include <nall/stdint.hpp>
@ -369,84 +370,8 @@ struct Hotkey {
};
#endif
#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 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
#include "application.hpp"
#include "desktop.hpp"
#if defined(Hiro_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)
XlibDisplay* pApplication::display = nullptr;
bool pApplication::xdgScreenSaver = false;
#endif
auto pApplication::run() -> void {
@ -29,16 +30,21 @@ auto pApplication::quit() -> void {
if(gtk_main_level()) gtk_main_quit();
#if defined(DISPLAY_XORG)
//TODO: Keyboard::poll() is being called after Application::quit();
//so if display is closed; this causes a segfault
//XCloseDisplay(display);
//display = nullptr;
XCloseDisplay(display);
display = nullptr;
#endif
}
auto pApplication::setScreenSaver(bool screenSaver) -> void {
#if defined(DISPLAY_XORG)
for(auto& window : windows) window->_setScreenSaver(screenSaver);
#endif
}
auto pApplication::initialize() -> void {
#if defined(DISPLAY_XORG)
display = XOpenDisplay(nullptr);
xdgScreenSaver = (bool)execute("xdg-screensaver", "--version").output.find("xdg-screensaver");
#endif
//set WM_CLASS to Application::name()

View File

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

View File

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

View File

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

View File

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

View File

@ -99,6 +99,10 @@ static auto Window_keyRelease(GtkWidget* widget, GdkEventKey* event, pWindow* p)
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 {
p->_synchronizeState();
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 {
widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
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), "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), "realize", G_CALLBACK(Window_realize), (gpointer)this);
g_signal_connect(G_OBJECT(formContainer), "size-allocate", G_CALLBACK(Window_sizeAllocate), (gpointer)this);
#if HIRO_GTK==2
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_height = Window_getPreferredHeight;
#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_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 {
_setMenuVisible(false);
}
@ -458,6 +480,19 @@ auto pWindow::_setMenuVisible(bool visible) -> void {
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 {
gtk_widget_set_sensitive(gtkStatus, enabled);
}

View File

@ -10,6 +10,7 @@ struct pWindow : pObject {
auto append(sStatusBar statusBar) -> void;
auto focused() const -> bool override;
auto frameMargin() const -> Geometry;
auto handle() const -> uintptr;
auto remove(sMenuBar menuBar) -> void;
auto remove(sSizable sizable) -> void;
auto remove(sStatusBar statusBar) -> void;
@ -33,6 +34,7 @@ struct pWindow : pObject {
auto _append(mMenu& menu) -> void;
auto _menuHeight() const -> int;
auto _menuTextHeight() const -> int;
auto _setScreenSaver(bool screenSaver) -> void;
auto _setIcon(const string& basename) -> bool;
auto _setMenuEnabled(bool enabled) -> void;
auto _setMenuFont(const Font& font) -> void;
@ -55,6 +57,7 @@ struct pWindow : pObject {
GtkWidget* gtkStatus = nullptr;
GtkAllocation lastMove = {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
}
auto pApplication::setScreenSaver(bool screenSaver) -> void {
//TODO: not implemented
}
//this is sadly necessary for things like determining window frame geometry
//obviously, it is used as sparingly as possible
auto pApplication::syncX() -> void {

View File

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

View File

@ -54,6 +54,9 @@ auto pApplication::quit() -> void {
PostQuitMessage(0);
}
auto pApplication::setScreenSaver(bool screenSaver) -> void {
}
auto pApplication::initialize() -> void {
CoInitialize(0);
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_SYSCOMMAND:
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 processEvents() -> void;
static auto quit() -> void;
static auto setScreenSaver(bool screenSaver) -> void;
static auto initialize() -> void;
};