mirror of https://github.com/bsnes-emu/bsnes.git
v114.5
* improved appended firmware detection [devinacker] * added dynamic rate control support to ALSA and PulseAudio drivers [RedDwarf] * added option to use native file dialogs
This commit is contained in:
parent
c13745d753
commit
d2211d8818
|
@ -29,8 +29,8 @@ using namespace nall;
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "bsnes";
|
static const string Name = "bsnes";
|
||||||
static const string Version = "114.4";
|
static const string Version = "114.5";
|
||||||
static const string Author = "byuu";
|
static const string Copyright = "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";
|
||||||
|
|
||||||
|
|
|
@ -433,13 +433,7 @@ auto SuperFamicom::serial() const -> string {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SuperFamicom::romSize() const -> uint {
|
auto SuperFamicom::romSize() const -> uint {
|
||||||
//subtract appended firmware size, if firmware is present
|
return size() - firmwareRomSize();
|
||||||
if((size() & 0x7fff) == 0x100) return size() - 0x100;
|
|
||||||
if((size() & 0x7fff) == 0xc00) return size() - 0xc00;
|
|
||||||
if((size() & 0x7fff) == 0x2000) return size() - 0x2000;
|
|
||||||
if((size() & 0xffff) == 0xd000) return size() - 0xd000;
|
|
||||||
if((size() & 0x3ffff) == 0x28000) return size() - 0x28000;
|
|
||||||
return size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SuperFamicom::programRomSize() const -> uint {
|
auto SuperFamicom::programRomSize() const -> uint {
|
||||||
|
@ -459,8 +453,38 @@ auto SuperFamicom::expansionRomSize() const -> uint {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//detect if any firmware is appended to the ROM image, and return its size if so
|
||||||
auto SuperFamicom::firmwareRomSize() const -> uint {
|
auto SuperFamicom::firmwareRomSize() const -> uint {
|
||||||
return size() - romSize();
|
auto cartridgeTypeLo = data[headerAddress + 0x26] & 15;
|
||||||
|
auto cartridgeTypeHi = data[headerAddress + 0x26] >> 4;
|
||||||
|
auto cartridgeSubType = data[headerAddress + 0x0f];
|
||||||
|
|
||||||
|
if(serial() == "042J" || (cartridgeTypeLo == 0x3 && cartridgeTypeHi == 0xe)) {
|
||||||
|
//Game Boy
|
||||||
|
if((size() & 0x7fff) == 0x100) return 0x100;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cartridgeTypeLo >= 0x3 && cartridgeTypeHi == 0xf && cartridgeSubType == 0x10) {
|
||||||
|
//Hitachi HG51BS169
|
||||||
|
if((size() & 0x7fff) == 0xc00) return 0xc00;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cartridgeTypeLo >= 0x3 && cartridgeTypeHi == 0x0) {
|
||||||
|
//NEC uPD7725
|
||||||
|
if((size() & 0x7fff) == 0x2000) return 0x2000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cartridgeTypeLo >= 0x3 && cartridgeTypeHi == 0xf && cartridgeSubType == 0x01) {
|
||||||
|
//NEC uPD96050
|
||||||
|
if((size() & 0xffff) == 0xd000) return 0xd000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cartridgeTypeLo >= 0x3 && cartridgeTypeHi == 0xf && cartridgeSubType == 0x02) {
|
||||||
|
//ARM6
|
||||||
|
if((size() & 0x3ffff) == 0x28000) return 0x28000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SuperFamicom::ramSize() const -> uint {
|
auto SuperFamicom::ramSize() const -> uint {
|
||||||
|
|
|
@ -52,22 +52,6 @@ auto nall::main(Arguments arguments) -> void {
|
||||||
emulator = new SuperFamicom::Interface;
|
emulator = new SuperFamicom::Interface;
|
||||||
program.create();
|
program.create();
|
||||||
|
|
||||||
if(Emulator::Version.find(".") && settings.general.betaWarning && 0) {
|
|
||||||
MessageDialog dialog;
|
|
||||||
dialog.setTitle(Emulator::Name);
|
|
||||||
dialog.setText(
|
|
||||||
"This is a nightly release. Bugs and regressions are possible!\n"
|
|
||||||
"If you experience issues, please report them to me.\n"
|
|
||||||
"If stability is required, please use a stable release.\n"
|
|
||||||
);
|
|
||||||
dialog.setOption("Don't show this message again");
|
|
||||||
dialog.information();
|
|
||||||
if(dialog.checked()) {
|
|
||||||
settings.general.betaWarning = false;
|
|
||||||
settings.save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Application::run();
|
Application::run();
|
||||||
Instances::presentation.destruct();
|
Instances::presentation.destruct();
|
||||||
Instances::settingsWindow.destruct();
|
Instances::settingsWindow.destruct();
|
||||||
|
|
|
@ -187,7 +187,7 @@ auto Presentation::create() -> void {
|
||||||
.setLogo(Resource::SameBoy)
|
.setLogo(Resource::SameBoy)
|
||||||
.setDescription("Super Game Boy emulator")
|
.setDescription("Super Game Boy emulator")
|
||||||
.setVersion("0.12.1")
|
.setVersion("0.12.1")
|
||||||
.setAuthor("Lior Halphon")
|
.setCopyright("Lior Halphon")
|
||||||
.setLicense("MIT")
|
.setLicense("MIT")
|
||||||
.setWebsite("https://sameboy.github.io")
|
.setWebsite("https://sameboy.github.io")
|
||||||
.setAlignment(*this)
|
.setAlignment(*this)
|
||||||
|
@ -199,7 +199,7 @@ auto Presentation::create() -> void {
|
||||||
.setLogo(Resource::Logo)
|
.setLogo(Resource::Logo)
|
||||||
.setDescription("Super Nintendo emulator")
|
.setDescription("Super Nintendo emulator")
|
||||||
.setVersion(Emulator::Version)
|
.setVersion(Emulator::Version)
|
||||||
.setAuthor("byuu")
|
.setCopyright("byuu")
|
||||||
.setLicense("GPLv3")
|
.setLicense("GPLv3")
|
||||||
.setWebsite("https://byuu.org")
|
.setWebsite("https://byuu.org")
|
||||||
.setAlignment(*this)
|
.setAlignment(*this)
|
||||||
|
|
|
@ -25,7 +25,7 @@ auto Program::moviePlay() -> void {
|
||||||
dialog.setTitle("Play Movie");
|
dialog.setTitle("Play Movie");
|
||||||
dialog.setPath(Path::desktop());
|
dialog.setPath(Path::desktop());
|
||||||
dialog.setFilters({string{"Movies (.bsv)|*.bsv"}});
|
dialog.setFilters({string{"Movies (.bsv)|*.bsv"}});
|
||||||
if(auto location = dialog.openFile()) {
|
if(auto location = openFile(dialog)) {
|
||||||
if(auto fp = file::open(location, file::mode::read)) {
|
if(auto fp = file::open(location, file::mode::read)) {
|
||||||
bool failed = false;
|
bool failed = false;
|
||||||
if(fp.read() != 'B') failed = true;
|
if(fp.read() != 'B') failed = true;
|
||||||
|
@ -92,7 +92,7 @@ auto Program::movieStop() -> void {
|
||||||
dialog.setTitle("Save Movie");
|
dialog.setTitle("Save Movie");
|
||||||
dialog.setPath(Path::desktop());
|
dialog.setPath(Path::desktop());
|
||||||
dialog.setFilters({string{"Movies (.bsv)|*.bsv"}});
|
dialog.setFilters({string{"Movies (.bsv)|*.bsv"}});
|
||||||
if(auto location = dialog.saveFile()) {
|
if(auto location = saveFile(dialog)) {
|
||||||
if(!location.endsWith(".bsv")) location.append(".bsv");
|
if(!location.endsWith(".bsv")) location.append(".bsv");
|
||||||
if(auto fp = file::open(location, file::mode::write)) {
|
if(auto fp = file::open(location, file::mode::write)) {
|
||||||
fp.write('B');
|
fp.write('B');
|
||||||
|
|
|
@ -113,7 +113,7 @@ auto Program::load(uint id, string name, string type, vector<string> options) ->
|
||||||
dialog.setTitle("Load SNES ROM");
|
dialog.setTitle("Load SNES ROM");
|
||||||
dialog.setPath(path("Games", settings.path.recent.superFamicom));
|
dialog.setPath(path("Games", settings.path.recent.superFamicom));
|
||||||
dialog.setFilters({string{"SNES ROMs|*.sfc:*.smc:*.zip:*.7z:*.SFC:*.SMC:*.ZIP:*.7Z:*.Sfc:*.Smc:*.Zip"}, string{"All Files|*"}});
|
dialog.setFilters({string{"SNES ROMs|*.sfc:*.smc:*.zip:*.7z:*.SFC:*.SMC:*.ZIP:*.7Z:*.Sfc:*.Smc:*.Zip"}, string{"All Files|*"}});
|
||||||
superFamicom.location = dialog.openObject();
|
superFamicom.location = openGame(dialog);
|
||||||
superFamicom.option = dialog.option();
|
superFamicom.option = dialog.option();
|
||||||
}
|
}
|
||||||
if(inode::exists(superFamicom.location)) {
|
if(inode::exists(superFamicom.location)) {
|
||||||
|
@ -133,7 +133,7 @@ auto Program::load(uint id, string name, string type, vector<string> options) ->
|
||||||
dialog.setTitle("Load Game Boy ROM");
|
dialog.setTitle("Load Game Boy ROM");
|
||||||
dialog.setPath(path("Games", settings.path.recent.gameBoy));
|
dialog.setPath(path("Games", settings.path.recent.gameBoy));
|
||||||
dialog.setFilters({string{"Game Boy ROMs|*.gb:*.gbc:*.zip:*.7z:*.GB:*.GBC:*.ZIP:*.7Z:*.Gb:*.Gbc:*.Zip"}, string{"All Files|*"}});
|
dialog.setFilters({string{"Game Boy ROMs|*.gb:*.gbc:*.zip:*.7z:*.GB:*.GBC:*.ZIP:*.7Z:*.Gb:*.Gbc:*.Zip"}, string{"All Files|*"}});
|
||||||
gameBoy.location = dialog.openObject();
|
gameBoy.location = openGame(dialog);
|
||||||
gameBoy.option = dialog.option();
|
gameBoy.option = dialog.option();
|
||||||
}
|
}
|
||||||
if(inode::exists(gameBoy.location)) {
|
if(inode::exists(gameBoy.location)) {
|
||||||
|
@ -153,7 +153,7 @@ auto Program::load(uint id, string name, string type, vector<string> options) ->
|
||||||
dialog.setTitle("Load BS Memory ROM");
|
dialog.setTitle("Load BS Memory ROM");
|
||||||
dialog.setPath(path("Games", settings.path.recent.bsMemory));
|
dialog.setPath(path("Games", settings.path.recent.bsMemory));
|
||||||
dialog.setFilters({string{"BS Memory ROMs|*.bs:*.zip:*.7z:*.BS:*.ZIP:*.7Z:*.Bs:*.Zip"}, string{"All Files|*"}});
|
dialog.setFilters({string{"BS Memory ROMs|*.bs:*.zip:*.7z:*.BS:*.ZIP:*.7Z:*.Bs:*.Zip"}, string{"All Files|*"}});
|
||||||
bsMemory.location = dialog.openObject();
|
bsMemory.location = openGame(dialog);
|
||||||
bsMemory.option = dialog.option();
|
bsMemory.option = dialog.option();
|
||||||
}
|
}
|
||||||
if(inode::exists(bsMemory.location)) {
|
if(inode::exists(bsMemory.location)) {
|
||||||
|
@ -173,7 +173,7 @@ auto Program::load(uint id, string name, string type, vector<string> options) ->
|
||||||
dialog.setTitle("Load Sufami Turbo ROM - Slot A");
|
dialog.setTitle("Load Sufami Turbo ROM - Slot A");
|
||||||
dialog.setPath(path("Games", settings.path.recent.sufamiTurboA));
|
dialog.setPath(path("Games", settings.path.recent.sufamiTurboA));
|
||||||
dialog.setFilters({string{"Sufami Turbo ROMs|*.st:*.zip:*.7z:*.ST:*.ZIP:*.7Z:*.St:*.Zip"}, string{"All Files|*"}});
|
dialog.setFilters({string{"Sufami Turbo ROMs|*.st:*.zip:*.7z:*.ST:*.ZIP:*.7Z:*.St:*.Zip"}, string{"All Files|*"}});
|
||||||
sufamiTurboA.location = dialog.openObject();
|
sufamiTurboA.location = openGame(dialog);
|
||||||
sufamiTurboA.option = dialog.option();
|
sufamiTurboA.option = dialog.option();
|
||||||
}
|
}
|
||||||
if(inode::exists(sufamiTurboA.location)) {
|
if(inode::exists(sufamiTurboA.location)) {
|
||||||
|
@ -193,7 +193,7 @@ auto Program::load(uint id, string name, string type, vector<string> options) ->
|
||||||
dialog.setTitle("Load Sufami Turbo ROM - Slot B");
|
dialog.setTitle("Load Sufami Turbo ROM - Slot B");
|
||||||
dialog.setPath(path("Games", settings.path.recent.sufamiTurboB));
|
dialog.setPath(path("Games", settings.path.recent.sufamiTurboB));
|
||||||
dialog.setFilters({string{"Sufami Turbo ROMs|*.st:*.zip:*.7z:*.ST:*.ZIP:*.7Z:*.St:*.Zip"}, string{"All Files|*"}});
|
dialog.setFilters({string{"Sufami Turbo ROMs|*.st:*.zip:*.7z:*.ST:*.ZIP:*.7Z:*.St:*.Zip"}, string{"All Files|*"}});
|
||||||
sufamiTurboB.location = dialog.openObject();
|
sufamiTurboB.location = openGame(dialog);
|
||||||
sufamiTurboB.option = dialog.option();
|
sufamiTurboB.option = dialog.option();
|
||||||
}
|
}
|
||||||
if(inode::exists(sufamiTurboB.location)) {
|
if(inode::exists(sufamiTurboB.location)) {
|
||||||
|
|
|
@ -114,6 +114,10 @@ struct Program : Lock, Emulator::Platform {
|
||||||
auto updateInputDriver(Window parent) -> void;
|
auto updateInputDriver(Window parent) -> void;
|
||||||
|
|
||||||
//utility.cpp
|
//utility.cpp
|
||||||
|
auto openGame(BrowserDialog& dialog) -> string;
|
||||||
|
auto openFile(BrowserDialog& dialog) -> string;
|
||||||
|
auto saveFile(BrowserDialog& dialog) -> string;
|
||||||
|
auto selectPath() -> string;
|
||||||
auto showMessage(string text) -> void;
|
auto showMessage(string text) -> void;
|
||||||
auto showFrameRate(string text) -> void;
|
auto showFrameRate(string text) -> void;
|
||||||
auto updateStatus() -> void;
|
auto updateStatus() -> void;
|
||||||
|
|
|
@ -1,3 +1,54 @@
|
||||||
|
auto Program::openGame(BrowserDialog& dialog) -> string {
|
||||||
|
if(!settings.general.nativeFileDialogs) {
|
||||||
|
return dialog.openObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
BrowserWindow window;
|
||||||
|
window.setTitle(dialog.title());
|
||||||
|
window.setPath(dialog.path());
|
||||||
|
window.setFilters(dialog.filters());
|
||||||
|
window.setParent(dialog.alignmentWindow());
|
||||||
|
return window.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Program::openFile(BrowserDialog& dialog) -> string {
|
||||||
|
if(!settings.general.nativeFileDialogs) {
|
||||||
|
return dialog.openFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
BrowserWindow window;
|
||||||
|
window.setTitle(dialog.title());
|
||||||
|
window.setPath(dialog.path());
|
||||||
|
window.setFilters(dialog.filters());
|
||||||
|
window.setParent(dialog.alignmentWindow());
|
||||||
|
return window.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Program::saveFile(BrowserDialog& dialog) -> string {
|
||||||
|
if(!settings.general.nativeFileDialogs) {
|
||||||
|
return dialog.saveFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
BrowserWindow window;
|
||||||
|
window.setTitle(dialog.title());
|
||||||
|
window.setPath(dialog.path());
|
||||||
|
window.setFilters(dialog.filters());
|
||||||
|
window.setParent(dialog.alignmentWindow());
|
||||||
|
return window.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Program::selectPath() -> string {
|
||||||
|
if(!settings.general.nativeFileDialogs) {
|
||||||
|
BrowserDialog dialog;
|
||||||
|
dialog.setPath(Path::desktop());
|
||||||
|
return dialog.selectFolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
BrowserWindow window;
|
||||||
|
window.setPath(Path::desktop());
|
||||||
|
return window.directory();
|
||||||
|
}
|
||||||
|
|
||||||
auto Program::showMessage(string text) -> void {
|
auto Program::showMessage(string text) -> void {
|
||||||
statusTime = chrono::millisecond();
|
statusTime = chrono::millisecond();
|
||||||
statusMessage = text;
|
statusMessage = text;
|
||||||
|
|
|
@ -20,6 +20,9 @@ auto EmulatorSettings::create() -> void {
|
||||||
autoLoadStateOnLoad.setText("Auto-resume on load").setChecked(settings.emulator.autoLoadStateOnLoad).onToggle([&] {
|
autoLoadStateOnLoad.setText("Auto-resume on load").setChecked(settings.emulator.autoLoadStateOnLoad).onToggle([&] {
|
||||||
settings.emulator.autoLoadStateOnLoad = autoLoadStateOnLoad.checked();
|
settings.emulator.autoLoadStateOnLoad = autoLoadStateOnLoad.checked();
|
||||||
});
|
});
|
||||||
|
nativeFileDialogs.setText("Use native file dialogs").setChecked(settings.general.nativeFileDialogs).onToggle([&] {
|
||||||
|
settings.general.nativeFileDialogs = nativeFileDialogs.checked();
|
||||||
|
});
|
||||||
optionsSpacer.setColor({192, 192, 192});
|
optionsSpacer.setColor({192, 192, 192});
|
||||||
|
|
||||||
fastForwardLabel.setText("Fast Forward").setFont(Font().setBold());
|
fastForwardLabel.setText("Fast Forward").setFont(Font().setBold());
|
||||||
|
|
|
@ -8,7 +8,7 @@ auto PathSettings::create() -> void {
|
||||||
gamesLabel.setText("Games:");
|
gamesLabel.setText("Games:");
|
||||||
gamesPath.setEditable(false);
|
gamesPath.setEditable(false);
|
||||||
gamesAssign.setText("Assign ...").onActivate([&] {
|
gamesAssign.setText("Assign ...").onActivate([&] {
|
||||||
if(auto location = BrowserDialog().selectFolder()) {
|
if(auto location = program.selectPath()) {
|
||||||
settings.path.games = location;
|
settings.path.games = location;
|
||||||
refreshPaths();
|
refreshPaths();
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ auto PathSettings::create() -> void {
|
||||||
patchesLabel.setText("Patches:");
|
patchesLabel.setText("Patches:");
|
||||||
patchesPath.setEditable(false);
|
patchesPath.setEditable(false);
|
||||||
patchesAssign.setText("Assign ...").onActivate([&] {
|
patchesAssign.setText("Assign ...").onActivate([&] {
|
||||||
if(auto location = BrowserDialog().selectFolder()) {
|
if(auto location = program.selectPath()) {
|
||||||
settings.path.patches = location;
|
settings.path.patches = location;
|
||||||
refreshPaths();
|
refreshPaths();
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ auto PathSettings::create() -> void {
|
||||||
savesLabel.setText("Saves:");
|
savesLabel.setText("Saves:");
|
||||||
savesPath.setEditable(false);
|
savesPath.setEditable(false);
|
||||||
savesAssign.setText("Assign ...").onActivate([&] {
|
savesAssign.setText("Assign ...").onActivate([&] {
|
||||||
if(auto location = BrowserDialog().selectFolder()) {
|
if(auto location = program.selectPath()) {
|
||||||
settings.path.saves = location;
|
settings.path.saves = location;
|
||||||
refreshPaths();
|
refreshPaths();
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ auto PathSettings::create() -> void {
|
||||||
cheatsLabel.setText("Cheats:");
|
cheatsLabel.setText("Cheats:");
|
||||||
cheatsPath.setEditable(false);
|
cheatsPath.setEditable(false);
|
||||||
cheatsAssign.setText("Assign ...").onActivate([&] {
|
cheatsAssign.setText("Assign ...").onActivate([&] {
|
||||||
if(auto location = BrowserDialog().selectFolder()) {
|
if(auto location = program.selectPath()) {
|
||||||
settings.path.cheats = location;
|
settings.path.cheats = location;
|
||||||
refreshPaths();
|
refreshPaths();
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ auto PathSettings::create() -> void {
|
||||||
statesLabel.setText("States:");
|
statesLabel.setText("States:");
|
||||||
statesPath.setEditable(false);
|
statesPath.setEditable(false);
|
||||||
statesAssign.setText("Assign ...").onActivate([&] {
|
statesAssign.setText("Assign ...").onActivate([&] {
|
||||||
if(auto location = BrowserDialog().selectFolder()) {
|
if(auto location = program.selectPath()) {
|
||||||
settings.path.states = location;
|
settings.path.states = location;
|
||||||
refreshPaths();
|
refreshPaths();
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ auto PathSettings::create() -> void {
|
||||||
screenshotsLabel.setText("Screenshots:");
|
screenshotsLabel.setText("Screenshots:");
|
||||||
screenshotsPath.setEditable(false);
|
screenshotsPath.setEditable(false);
|
||||||
screenshotsAssign.setText("Assign ...").onActivate([&] {
|
screenshotsAssign.setText("Assign ...").onActivate([&] {
|
||||||
if(auto location = BrowserDialog().selectFolder()) {
|
if(auto location = program.selectPath()) {
|
||||||
settings.path.screenshots = location;
|
settings.path.screenshots = location;
|
||||||
refreshPaths();
|
refreshPaths();
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,7 +141,7 @@ auto Settings::process(bool load) -> void {
|
||||||
bind(boolean, "General/ScreenSaver", general.screenSaver);
|
bind(boolean, "General/ScreenSaver", general.screenSaver);
|
||||||
bind(boolean, "General/ToolTips", general.toolTips);
|
bind(boolean, "General/ToolTips", general.toolTips);
|
||||||
bind(boolean, "General/Crashed", general.crashed);
|
bind(boolean, "General/Crashed", general.crashed);
|
||||||
bind(boolean, "General/BetaWarning", general.betaWarning);
|
bind(boolean, "General/NativeFileDialogs", general.nativeFileDialogs);
|
||||||
|
|
||||||
#undef bind
|
#undef bind
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,7 +145,7 @@ struct Settings : Markup::Node {
|
||||||
bool screenSaver = false;
|
bool screenSaver = false;
|
||||||
bool toolTips = true;
|
bool toolTips = true;
|
||||||
bool crashed = false;
|
bool crashed = false;
|
||||||
bool betaWarning = true;
|
bool nativeFileDialogs = false;
|
||||||
} general;
|
} general;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -303,11 +303,12 @@ struct EmulatorSettings : VerticalLayout {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Label optionsLabel{this, Size{~0, 0}, 2};
|
Label optionsLabel{this, Size{~0, 0}, 2};
|
||||||
CheckLabel warnOnUnverifiedGames{this, Size{~0, 0}};
|
CheckLabel warnOnUnverifiedGames{this, Size{~0, 0}, 2};
|
||||||
CheckLabel autoSaveMemory{this, Size{~0, 0}};
|
CheckLabel autoSaveMemory{this, Size{~0, 0}, 2};
|
||||||
HorizontalLayout autoStateLayout{this, Size{~0, 0}};
|
HorizontalLayout autoStateLayout{this, Size{~0, 0}, 2};
|
||||||
CheckLabel autoSaveStateOnUnload{&autoStateLayout, Size{0, 0}};
|
CheckLabel autoSaveStateOnUnload{&autoStateLayout, Size{0, 0}};
|
||||||
CheckLabel autoLoadStateOnLoad{&autoStateLayout, Size{0, 0}};
|
CheckLabel autoLoadStateOnLoad{&autoStateLayout, Size{0, 0}};
|
||||||
|
CheckLabel nativeFileDialogs{this, Size{~0, 0}};
|
||||||
Canvas optionsSpacer{this, Size{~0, 1}};
|
Canvas optionsSpacer{this, Size{~0, 1}};
|
||||||
//
|
//
|
||||||
Label fastForwardLabel{this, Size{~0, 0}, 2};
|
Label fastForwardLabel{this, Size{~0, 0}, 2};
|
||||||
|
|
|
@ -37,6 +37,11 @@ NSTimer* applicationTimer = nullptr;
|
||||||
|
|
||||||
namespace hiro {
|
namespace hiro {
|
||||||
|
|
||||||
|
auto pApplication::exit() -> void {
|
||||||
|
quit();
|
||||||
|
::exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
auto pApplication::modal() -> bool {
|
auto pApplication::modal() -> bool {
|
||||||
return Application::state().modal > 0;
|
return Application::state().modal > 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
namespace hiro {
|
namespace hiro {
|
||||||
|
|
||||||
struct pApplication {
|
struct pApplication {
|
||||||
|
static auto exit() -> void;
|
||||||
static auto modal() -> bool;
|
static auto modal() -> bool;
|
||||||
static auto run() -> void;
|
static auto run() -> void;
|
||||||
static auto pendingEvents() -> bool;
|
static auto pendingEvents() -> bool;
|
||||||
|
|
|
@ -26,7 +26,7 @@ auto pBrowserWindow::open(BrowserWindow::State& state) -> string {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
NSMutableArray* filters = [[NSMutableArray alloc] init];
|
NSMutableArray* filters = [[NSMutableArray alloc] init];
|
||||||
for(auto& rule : state.filters) {
|
for(auto& rule : state.filters) {
|
||||||
string pattern = rule.split("(", 1L)(1).trimRight(")", 1L);
|
string pattern = rule.split("|", 1L)(1).transform(":", ";");
|
||||||
if(pattern) [filters addObject:[NSString stringWithUTF8String:pattern]];
|
if(pattern) [filters addObject:[NSString stringWithUTF8String:pattern]];
|
||||||
}
|
}
|
||||||
NSOpenPanel* panel = [NSOpenPanel openPanel];
|
NSOpenPanel* panel = [NSOpenPanel openPanel];
|
||||||
|
@ -51,7 +51,7 @@ auto pBrowserWindow::save(BrowserWindow::State& state) -> string {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
NSMutableArray* filters = [[NSMutableArray alloc] init];
|
NSMutableArray* filters = [[NSMutableArray alloc] init];
|
||||||
for(auto& rule : state.filters) {
|
for(auto& rule : state.filters) {
|
||||||
string pattern = rule.split("(", 1L)(1).trimRight(")", 1L);
|
string pattern = rule.split("|", 1L)(1).transform(":", ";");
|
||||||
if(pattern) [filters addObject:[NSString stringWithUTF8String:pattern]];
|
if(pattern) [filters addObject:[NSString stringWithUTF8String:pattern]];
|
||||||
}
|
}
|
||||||
NSSavePanel* panel = [NSSavePanel savePanel];
|
NSSavePanel* panel = [NSSavePanel savePanel];
|
||||||
|
|
|
@ -10,8 +10,8 @@ auto Application::doMain() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Application::exit() -> void {
|
auto Application::exit() -> void {
|
||||||
quit();
|
state().quit = true;
|
||||||
::exit(EXIT_SUCCESS);
|
return pApplication::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Application::font() -> Font {
|
auto Application::font() -> Font {
|
||||||
|
|
|
@ -12,8 +12,8 @@ auto AboutDialog::setAlignment(sWindow relativeTo, Alignment alignment) -> type&
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto AboutDialog::setAuthor(const string& author) -> type& {
|
auto AboutDialog::setCopyright(const string& copyright) -> type& {
|
||||||
state.author = author;
|
state.copyright = copyright;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ auto AboutDialog::show() -> void {
|
||||||
nameLabel.setText(state.name ? state.name : Application::name());
|
nameLabel.setText(state.name ? state.name : Application::name());
|
||||||
nameLabel.setVisible((bool)state.name && !(bool)state.logo);
|
nameLabel.setVisible((bool)state.name && !(bool)state.logo);
|
||||||
|
|
||||||
Canvas logoCanvas{&layout, Size{~0, 0}};
|
Canvas logoCanvas{&layout, Size{~0, 0}, 5_sy};
|
||||||
logoCanvas.setCollapsible();
|
logoCanvas.setCollapsible();
|
||||||
if(state.logo) {
|
if(state.logo) {
|
||||||
image logo{state.logo};
|
image logo{state.logo};
|
||||||
|
@ -95,19 +95,19 @@ auto AboutDialog::show() -> void {
|
||||||
versionValue.setText(state.version);
|
versionValue.setText(state.version);
|
||||||
if(!state.version) versionLayout.setVisible(false);
|
if(!state.version) versionLayout.setVisible(false);
|
||||||
|
|
||||||
HorizontalLayout authorLayout{&layout, Size{~0, 0}, 0};
|
HorizontalLayout copyrightLayout{&layout, Size{~0, 0}, 0};
|
||||||
authorLayout.setCollapsible();
|
copyrightLayout.setCollapsible();
|
||||||
Label authorLabel{&authorLayout, Size{~0, 0}, 3_sx};
|
Label copyrightLabel{©rightLayout, Size{~0, 0}, 3_sx};
|
||||||
authorLabel.setAlignment(1.0);
|
copyrightLabel.setAlignment(1.0);
|
||||||
authorLabel.setFont(Font().setBold());
|
copyrightLabel.setFont(Font().setBold());
|
||||||
authorLabel.setForegroundColor({0, 0, 0});
|
copyrightLabel.setForegroundColor({0, 0, 0});
|
||||||
authorLabel.setText("Author:");
|
copyrightLabel.setText("Copyright:");
|
||||||
Label authorValue{&authorLayout, Size{~0, 0}};
|
Label copyrightValue{©rightLayout, Size{~0, 0}};
|
||||||
authorValue.setAlignment(0.0);
|
copyrightValue.setAlignment(0.0);
|
||||||
authorValue.setFont(Font().setBold());
|
copyrightValue.setFont(Font().setBold());
|
||||||
authorValue.setForegroundColor({0, 0, 0});
|
copyrightValue.setForegroundColor({0, 0, 0});
|
||||||
authorValue.setText(state.author);
|
copyrightValue.setText(state.copyright);
|
||||||
if(!state.author) authorLayout.setVisible(false);
|
if(!state.copyright) copyrightLayout.setVisible(false);
|
||||||
|
|
||||||
HorizontalLayout licenseLayout{&layout, Size{~0, 0}, 0};
|
HorizontalLayout licenseLayout{&layout, Size{~0, 0}, 0};
|
||||||
licenseLayout.setCollapsible();
|
licenseLayout.setCollapsible();
|
||||||
|
@ -151,7 +151,7 @@ auto AboutDialog::show() -> void {
|
||||||
|
|
||||||
window.setTitle({"About ", state.name ? state.name : Application::name(), " ..."});
|
window.setTitle({"About ", state.name ? state.name : Application::name(), " ..."});
|
||||||
window.setBackgroundColor({255, 255, 240});
|
window.setBackgroundColor({255, 255, 240});
|
||||||
window.setSize({max(360_sx, layout.minimumSize().width()), layout.minimumSize().height()});
|
window.setSize({max(320_sx, layout.minimumSize().width()), layout.minimumSize().height()});
|
||||||
window.setResizable(false);
|
window.setResizable(false);
|
||||||
window.setAlignment(state.relativeTo, state.alignment);
|
window.setAlignment(state.relativeTo, state.alignment);
|
||||||
window.setDismissable();
|
window.setDismissable();
|
||||||
|
|
|
@ -5,7 +5,7 @@ struct AboutDialog {
|
||||||
|
|
||||||
auto setAlignment(Alignment = Alignment::Center) -> type&;
|
auto setAlignment(Alignment = Alignment::Center) -> type&;
|
||||||
auto setAlignment(sWindow relativeTo, Alignment = Alignment::Center) -> type&;
|
auto setAlignment(sWindow relativeTo, Alignment = Alignment::Center) -> type&;
|
||||||
auto setAuthor(const string& author = "") -> type&;
|
auto setCopyright(const string& copyright = "") -> type&;
|
||||||
auto setDescription(const string& description = "") -> type&;
|
auto setDescription(const string& description = "") -> type&;
|
||||||
auto setLicense(const string& license = "") -> type&;
|
auto setLicense(const string& license = "") -> type&;
|
||||||
auto setLogo(const image& logo = {}) -> type&;
|
auto setLogo(const image& logo = {}) -> type&;
|
||||||
|
@ -17,7 +17,7 @@ struct AboutDialog {
|
||||||
private:
|
private:
|
||||||
struct State {
|
struct State {
|
||||||
Alignment alignment = Alignment::Center;
|
Alignment alignment = Alignment::Center;
|
||||||
string author;
|
string copyright;
|
||||||
string description;
|
string description;
|
||||||
string license;
|
string license;
|
||||||
image logo;
|
image logo;
|
||||||
|
|
|
@ -241,12 +241,8 @@ auto BrowserDialogWindow::run() -> BrowserDialog::Response {
|
||||||
return (void)window.setModal(false);
|
return (void)window.setModal(false);
|
||||||
}
|
}
|
||||||
if(state.action == "openObject" && isObject(name)) {
|
if(state.action == "openObject" && isObject(name)) {
|
||||||
if(isMatch(name)) {
|
|
||||||
response.selected.append({state.path, name});
|
response.selected.append({state.path, name});
|
||||||
return (void)window.setModal(false);
|
return (void)window.setModal(false);
|
||||||
} else if(isFolder(name)) {
|
|
||||||
return setPath({state.path, name});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if(state.action == "saveFile") return accept();
|
if(state.action == "saveFile") return accept();
|
||||||
setPath(state.path, name);
|
setPath(state.path, name);
|
||||||
|
@ -417,6 +413,18 @@ auto BrowserDialogWindow::setPath(string path, const string& contains) -> void {
|
||||||
BrowserDialog::BrowserDialog() {
|
BrowserDialog::BrowserDialog() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto BrowserDialog::alignment() const -> Alignment {
|
||||||
|
return state.alignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto BrowserDialog::alignmentWindow() const -> Window {
|
||||||
|
return state.relativeTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto BrowserDialog::filters() const -> vector<string> {
|
||||||
|
return state.filters;
|
||||||
|
}
|
||||||
|
|
||||||
auto BrowserDialog::openFile() -> string {
|
auto BrowserDialog::openFile() -> string {
|
||||||
state.action = "openFile";
|
state.action = "openFile";
|
||||||
if(!state.title) state.title = "Open File";
|
if(!state.title) state.title = "Open File";
|
||||||
|
@ -449,6 +457,10 @@ auto BrowserDialog::option() -> string {
|
||||||
return response.option;
|
return response.option;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto BrowserDialog::path() const -> string {
|
||||||
|
return state.path;
|
||||||
|
}
|
||||||
|
|
||||||
auto BrowserDialog::saveFile() -> string {
|
auto BrowserDialog::saveFile() -> string {
|
||||||
state.action = "saveFile";
|
state.action = "saveFile";
|
||||||
if(!state.title) state.title = "Save File";
|
if(!state.title) state.title = "Save File";
|
||||||
|
@ -504,6 +516,10 @@ auto BrowserDialog::setTitle(const string& title) -> type& {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto BrowserDialog::title() const -> string {
|
||||||
|
return state.title;
|
||||||
|
}
|
||||||
|
|
||||||
auto BrowserDialog::_run() -> vector<string> {
|
auto BrowserDialog::_run() -> vector<string> {
|
||||||
if(!state.path) state.path = Path::user();
|
if(!state.path) state.path = Path::user();
|
||||||
response = BrowserDialogWindow(state).run();
|
response = BrowserDialogWindow(state).run();
|
||||||
|
|
|
@ -6,11 +6,15 @@ struct BrowserDialog {
|
||||||
using type = BrowserDialog;
|
using type = BrowserDialog;
|
||||||
|
|
||||||
BrowserDialog();
|
BrowserDialog();
|
||||||
|
auto alignment() const -> Alignment;
|
||||||
|
auto alignmentWindow() const -> Window;
|
||||||
|
auto filters() const -> vector<string>;
|
||||||
auto openFile() -> string; //one existing file
|
auto openFile() -> string; //one existing file
|
||||||
auto openFiles() -> vector<string>; //any existing files
|
auto openFiles() -> vector<string>; //any existing files
|
||||||
auto openFolder() -> string; //one existing folder
|
auto openFolder() -> string; //one existing folder
|
||||||
auto openObject() -> string; //one existing file or folder
|
auto openObject() -> string; //one existing file or folder
|
||||||
auto option() -> string;
|
auto option() -> string;
|
||||||
|
auto path() const -> string;
|
||||||
auto saveFile() -> string; //one file
|
auto saveFile() -> string; //one file
|
||||||
auto selected() -> vector<string>;
|
auto selected() -> vector<string>;
|
||||||
auto selectFolder() -> string; //one existing folder
|
auto selectFolder() -> string; //one existing folder
|
||||||
|
@ -21,6 +25,7 @@ struct BrowserDialog {
|
||||||
auto setOptions(const vector<string>& options = {}) -> type&;
|
auto setOptions(const vector<string>& options = {}) -> type&;
|
||||||
auto setPath(const string& path = "") -> type&;
|
auto setPath(const string& path = "") -> type&;
|
||||||
auto setTitle(const string& title = "") -> type&;
|
auto setTitle(const string& title = "") -> type&;
|
||||||
|
auto title() const -> string;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct State {
|
struct State {
|
||||||
|
|
|
@ -15,6 +15,11 @@ auto Log_Filter(const char* logDomain, GLogLevelFlags logLevel, const char* mess
|
||||||
print(terminal::color::yellow("hiro: "), logDomain, "::", message, "\n");
|
print(terminal::color::yellow("hiro: "), logDomain, "::", message, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto pApplication::exit() -> void {
|
||||||
|
quit();
|
||||||
|
::exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
auto pApplication::modal() -> bool {
|
auto pApplication::modal() -> bool {
|
||||||
return Application::state().modal > 0;
|
return Application::state().modal > 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
namespace hiro {
|
namespace hiro {
|
||||||
|
|
||||||
struct pApplication {
|
struct pApplication {
|
||||||
|
static auto exit() -> void;
|
||||||
static auto modal() -> bool;
|
static auto modal() -> bool;
|
||||||
static auto run() -> void;
|
static auto run() -> void;
|
||||||
static auto pendingEvents() -> bool;
|
static auto pendingEvents() -> bool;
|
||||||
|
|
|
@ -4,9 +4,12 @@ namespace hiro {
|
||||||
|
|
||||||
static auto BrowserWindow_addFilters(GtkWidget* dialog, vector<string> filters) -> void {
|
static auto BrowserWindow_addFilters(GtkWidget* dialog, vector<string> filters) -> void {
|
||||||
for(auto& filter : filters) {
|
for(auto& filter : filters) {
|
||||||
|
auto part = filter.split("|", 1L);
|
||||||
|
if(part.size() != 2) continue;
|
||||||
|
|
||||||
GtkFileFilter* gtkFilter = gtk_file_filter_new();
|
GtkFileFilter* gtkFilter = gtk_file_filter_new();
|
||||||
gtk_file_filter_set_name(gtkFilter, filter);
|
gtk_file_filter_set_name(gtkFilter, part[0]);
|
||||||
auto patterns = filter.split("(", 1L)(1).trimRight(")", 1L).split(",").strip();
|
auto patterns = part[1].split(":");
|
||||||
for(auto& pattern : patterns) gtk_file_filter_add_pattern(gtkFilter, pattern);
|
for(auto& pattern : patterns) gtk_file_filter_add_pattern(gtkFilter, pattern);
|
||||||
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), gtkFilter);
|
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), gtkFilter);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,9 @@ auto pTableViewItem::setBackgroundColor(Color color) -> void {
|
||||||
|
|
||||||
auto pTableViewItem::setFocused() -> void {
|
auto pTableViewItem::setFocused() -> void {
|
||||||
if(auto parent = _parent()) {
|
if(auto parent = _parent()) {
|
||||||
|
//calling setSelected() and then setFocused() right after sometimes fails to set focus
|
||||||
|
Application::processEvents();
|
||||||
|
|
||||||
auto lock = parent->acquire();
|
auto lock = parent->acquire();
|
||||||
GtkTreePath* path = gtk_tree_path_new_from_string(string{self().offset()});
|
GtkTreePath* path = gtk_tree_path_new_from_string(string{self().offset()});
|
||||||
gtk_tree_view_set_cursor(parent->gtkTreeView, path, nullptr, false);
|
gtk_tree_view_set_cursor(parent->gtkTreeView, path, nullptr, false);
|
||||||
|
|
|
@ -127,6 +127,8 @@ auto pWidget::setGeometry(Geometry geometry) -> void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pSizable::setGeometry(geometry);
|
pSizable::setGeometry(geometry);
|
||||||
|
//this is needed to prevent some repainting issues (specifically with a Label which has a background color set for it)
|
||||||
|
gtk_widget_queue_draw(gtkWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pWidget::setMouseCursor(const MouseCursor& mouseCursor) -> void {
|
auto pWidget::setMouseCursor(const MouseCursor& mouseCursor) -> void {
|
||||||
|
|
|
@ -2,6 +2,11 @@
|
||||||
|
|
||||||
namespace hiro {
|
namespace hiro {
|
||||||
|
|
||||||
|
auto pApplication::exit() -> void {
|
||||||
|
quit();
|
||||||
|
::exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
auto pApplication::modal() -> bool {
|
auto pApplication::modal() -> bool {
|
||||||
return Application::state().modal > 0;
|
return Application::state().modal > 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
namespace hiro {
|
namespace hiro {
|
||||||
|
|
||||||
struct pApplication {
|
struct pApplication {
|
||||||
|
static auto exit() -> void;
|
||||||
static auto modal() -> bool;
|
static auto modal() -> bool;
|
||||||
static auto run() -> void;
|
static auto run() -> void;
|
||||||
static auto pendingEvents() -> bool;
|
static auto pendingEvents() -> bool;
|
||||||
|
|
|
@ -3,63 +3,48 @@
|
||||||
namespace hiro {
|
namespace hiro {
|
||||||
|
|
||||||
auto pBrowserWindow::directory(BrowserWindow::State& state) -> string {
|
auto pBrowserWindow::directory(BrowserWindow::State& state) -> string {
|
||||||
return {};
|
|
||||||
/*
|
|
||||||
QString directory = QFileDialog::getExistingDirectory(
|
QString directory = QFileDialog::getExistingDirectory(
|
||||||
state.parent ? state.parent->p.qtWindow : nullptr,
|
state.parent ? state.parent->self()->qtWindow : nullptr,
|
||||||
state.title ? state.title : "Select Directory",
|
state.title ? QString::fromUtf8(state.title) : "Select Directory",
|
||||||
QString::fromUtf8(state.path), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks
|
QString::fromUtf8(state.path), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks
|
||||||
);
|
);
|
||||||
string name = directory.toUtf8().constData();
|
string name = directory.toUtf8().constData();
|
||||||
if(name && name.endsWith("/") == false) name.append("/");
|
if(name && name.endsWith("/") == false) name.append("/");
|
||||||
return name;
|
return name;
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pBrowserWindow::open(BrowserWindow::State& state) -> string {
|
auto pBrowserWindow::open(BrowserWindow::State& state) -> string {
|
||||||
return {};
|
string filters;
|
||||||
/*
|
for(auto& filter : state.filters) {
|
||||||
string filters = state.filters.merge(";;");
|
auto part = filter.split("|", 1L);
|
||||||
|
if(part.size() != 2) continue;
|
||||||
//convert filter list from phoenix to Qt format, example:
|
filters.append(part[0], " (", part[1].transform(":", " "), ");;");
|
||||||
//"Text, XML files (*.txt,*.xml)" -> "Text, XML files (*.txt *.xml)"
|
|
||||||
signed parentheses = 0;
|
|
||||||
for(auto& n : filters) {
|
|
||||||
if(n == '(') parentheses++;
|
|
||||||
if(n == ')') parentheses--;
|
|
||||||
if(n == ',' && parentheses) n = ' ';
|
|
||||||
}
|
}
|
||||||
|
filters.trimRight(";;", 1L);
|
||||||
|
|
||||||
QString filename = QFileDialog::getOpenFileName(
|
QString filename = QFileDialog::getOpenFileName(
|
||||||
state.parent ? state.parent->p.qtWindow : nullptr,
|
state.parent ? state.parent->self()->qtWindow : nullptr,
|
||||||
state.title ? state.title : "Open File",
|
state.title ? QString::fromUtf8(state.title) : "Open File",
|
||||||
QString::fromUtf8(state.path), QString::fromUtf8(filters)
|
QString::fromUtf8(state.path), QString::fromUtf8(filters)
|
||||||
);
|
);
|
||||||
return filename.toUtf8().constData();
|
return filename.toUtf8().constData();
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pBrowserWindow::save(BrowserWindow::State& state) -> string {
|
auto pBrowserWindow::save(BrowserWindow::State& state) -> string {
|
||||||
return {};
|
string filters;
|
||||||
/*
|
for(auto& filter : state.filters) {
|
||||||
string filters = state.filters.merge(";;");
|
auto part = filter.split("|", 1L);
|
||||||
|
if(part.size() != 2) continue;
|
||||||
//convert filter list from phoenix to Qt format, example:
|
filters.append(part[0], " (", part[1].transform(":", " "), ");;");
|
||||||
//"Text, XML files (*.txt,*.xml)" -> "Text, XML files (*.txt *.xml)"
|
|
||||||
signed parentheses = 0;
|
|
||||||
for(auto& n : filters) {
|
|
||||||
if(n == '(') parentheses++;
|
|
||||||
if(n == ')') parentheses--;
|
|
||||||
if(n == ',' && parentheses) n = ' ';
|
|
||||||
}
|
}
|
||||||
|
filters.trimRight(";;", 1L);
|
||||||
|
|
||||||
QString filename = QFileDialog::getSaveFileName(
|
QString filename = QFileDialog::getSaveFileName(
|
||||||
state.parent ? state.parent->p.qtWindow : nullptr,
|
state.parent ? state.parent->self()->qtWindow : nullptr,
|
||||||
state.title ? state.title : "Save File",
|
state.title ? QString::fromUtf8(state.title) : "Save File",
|
||||||
QString::fromUtf8(state.path), QString::fromUtf8(filters)
|
QString::fromUtf8(state.path), QString::fromUtf8(filters)
|
||||||
);
|
);
|
||||||
return filename.toUtf8().constData();
|
return filename.toUtf8().constData();
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,35 +27,31 @@ static auto MessageWindow_response(MessageWindow::Buttons buttons, QMessageBox::
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pMessageWindow::error(MessageWindow::State& state) -> MessageWindow::Response {
|
auto pMessageWindow::error(MessageWindow::State& state) -> MessageWindow::Response {
|
||||||
return {};
|
return MessageWindow_response(
|
||||||
// return MessageWindow_response(
|
state.buttons, QMessageBox::critical(state.parent ? state.parent->self()->qtWindow : nullptr, state.title ? QString::fromUtf8(state.title) : " ",
|
||||||
// state.buttons, QMessageBox::critical(state.parent ? state.parent->p.qtWindow : nullptr, state.title ? state.title : " ",
|
QString::fromUtf8(state.text), MessageWindow_buttons(state.buttons))
|
||||||
// QString::fromUtf8(state.text), MessageWindow_buttons(state.buttons))
|
);
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pMessageWindow::information(MessageWindow::State& state) -> MessageWindow::Response {
|
auto pMessageWindow::information(MessageWindow::State& state) -> MessageWindow::Response {
|
||||||
return {};
|
return MessageWindow_response(
|
||||||
// return MessageWindow_response(
|
state.buttons, QMessageBox::information(state.parent ? state.parent->self()->qtWindow : nullptr, state.title ? QString::fromUtf8(state.title) : " ",
|
||||||
// state.buttons, QMessageBox::information(state.parent ? state.parent->p.qtWindow : nullptr, state.title ? state.title : " ",
|
QString::fromUtf8(state.text), MessageWindow_buttons(state.buttons))
|
||||||
// QString::fromUtf8(state.text), MessageWindow_buttons(state.buttons))
|
);
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pMessageWindow::question(MessageWindow::State& state) -> MessageWindow::Response {
|
auto pMessageWindow::question(MessageWindow::State& state) -> MessageWindow::Response {
|
||||||
return {};
|
return MessageWindow_response(
|
||||||
// return MessageWindow_response(
|
state.buttons, QMessageBox::question(state.parent ? state.parent->self()->qtWindow : nullptr, state.title ? QString::fromUtf8(state.title) : " ",
|
||||||
// state.buttons, QMessageBox::question(state.parent ? state.parent->p.qtWindow : nullptr, state.title ? state.title : " ",
|
QString::fromUtf8(state.text), MessageWindow_buttons(state.buttons))
|
||||||
// QString::fromUtf8(state.text), MessageWindow_buttons(state.buttons))
|
);
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pMessageWindow::warning(MessageWindow::State& state) -> MessageWindow::Response {
|
auto pMessageWindow::warning(MessageWindow::State& state) -> MessageWindow::Response {
|
||||||
return {};
|
return MessageWindow_response(
|
||||||
// return MessageWindow_response(
|
state.buttons, QMessageBox::warning(state.parent ? state.parent->self()->qtWindow : nullptr, state.title ? QString::fromUtf8(state.title) : " ",
|
||||||
// state.buttons, QMessageBox::warning(state.parent ? state.parent->p.qtWindow : nullptr, state.title ? state.title : " ",
|
QString::fromUtf8(state.text), MessageWindow_buttons(state.buttons))
|
||||||
// QString::fromUtf8(state.text), MessageWindow_buttons(state.buttons))
|
);
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
1378
hiro/qt/qt.moc
1378
hiro/qt/qt.moc
File diff suppressed because it is too large
Load Diff
|
@ -6,6 +6,13 @@ static auto Application_keyboardProc(HWND, UINT, WPARAM, LPARAM) -> bool;
|
||||||
static auto Application_processDialogMessage(MSG&) -> void;
|
static auto Application_processDialogMessage(MSG&) -> void;
|
||||||
static auto CALLBACK Window_windowProc(HWND, UINT, WPARAM, LPARAM) -> LRESULT;
|
static auto CALLBACK Window_windowProc(HWND, UINT, WPARAM, LPARAM) -> LRESULT;
|
||||||
|
|
||||||
|
auto pApplication::exit() -> void {
|
||||||
|
quit();
|
||||||
|
auto processID = GetCurrentProcessId();
|
||||||
|
auto handle = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, true, processID);
|
||||||
|
TerminateProcess(handle, 0);
|
||||||
|
}
|
||||||
|
|
||||||
auto pApplication::modal() -> bool {
|
auto pApplication::modal() -> bool {
|
||||||
return state().modalCount > 0;
|
return state().modalCount > 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
namespace hiro {
|
namespace hiro {
|
||||||
|
|
||||||
struct pApplication {
|
struct pApplication {
|
||||||
|
static auto exit() -> void;
|
||||||
static auto modal() -> bool;
|
static auto modal() -> bool;
|
||||||
static auto run() -> void;
|
static auto run() -> void;
|
||||||
static auto pendingEvents() -> bool;
|
static auto pendingEvents() -> bool;
|
||||||
|
|
|
@ -19,12 +19,9 @@ static auto BrowserWindow_fileDialog(bool save, BrowserWindow::State& state) ->
|
||||||
|
|
||||||
string filters;
|
string filters;
|
||||||
for(auto& filter : state.filters) {
|
for(auto& filter : state.filters) {
|
||||||
auto part = filter.split("(");
|
auto part = filter.split("|", 1L);
|
||||||
if(part.size() != 2) continue;
|
if(part.size() != 2) continue;
|
||||||
part[1].trimRight(")", 1L);
|
filters.append(filter, "\t", part[1].merge(";"), "\t");
|
||||||
part[1].replace(" ", "");
|
|
||||||
part[1].transform(",", ";");
|
|
||||||
filters.append(filter, "\t", part[1], "\t");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
utf16_t wfilters(filters);
|
utf16_t wfilters(filters);
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
namespace hiro {
|
namespace hiro {
|
||||||
|
|
||||||
auto pFrame::construct() -> void {
|
auto pFrame::construct() -> void {
|
||||||
hwnd = CreateWindow(L"BUTTON", L"",
|
hwnd = CreateWindowEx(
|
||||||
|
//WS_EX_TRANSPARENT fixes rendering issues caused by Windows using WS_CLIPCHILDREN
|
||||||
|
WS_EX_TRANSPARENT, L"BUTTON", L"",
|
||||||
WS_CHILD | BS_GROUPBOX,
|
WS_CHILD | BS_GROUPBOX,
|
||||||
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
|
0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0);
|
||||||
pWidget::construct();
|
pWidget::construct();
|
||||||
|
|
|
@ -94,22 +94,6 @@ auto pLabel::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> ma
|
||||||
return msg == WM_ERASEBKGND;
|
return msg == WM_ERASEBKGND;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN || msg == WM_RBUTTONDOWN) {
|
|
||||||
switch(msg) {
|
|
||||||
case WM_LBUTTONDOWN: self().doMousePress(Mouse::Button::Left); break;
|
|
||||||
case WM_MBUTTONDOWN: self().doMousePress(Mouse::Button::Middle); break;
|
|
||||||
case WM_RBUTTONDOWN: self().doMousePress(Mouse::Button::Right); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(msg == WM_LBUTTONUP || msg == WM_MBUTTONUP || msg == WM_RBUTTONUP) {
|
|
||||||
switch(msg) {
|
|
||||||
case WM_LBUTTONUP: self().doMouseRelease(Mouse::Button::Left); break;
|
|
||||||
case WM_MBUTTONUP: self().doMouseRelease(Mouse::Button::Middle); break;
|
|
||||||
case WM_RBUTTONUP: self().doMouseRelease(Mouse::Button::Right); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pWidget::windowProc(hwnd, msg, wparam, lparam);
|
return pWidget::windowProc(hwnd, msg, wparam, lparam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,8 @@ auto pWidget::setGeometry(Geometry geometry) -> void {
|
||||||
geometry.setY(geometry.y() - displacement.y());
|
geometry.setY(geometry.y() - displacement.y());
|
||||||
}
|
}
|
||||||
SetWindowPos(hwnd, nullptr, geometry.x(), geometry.y(), geometry.width(), geometry.height(), SWP_NOZORDER);
|
SetWindowPos(hwnd, nullptr, geometry.x(), geometry.y(), geometry.width(), geometry.height(), SWP_NOZORDER);
|
||||||
|
//RedrawWindow fixes painting problems when adjusting Layouts manually
|
||||||
|
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ALLCHILDREN);
|
||||||
pSizable::setGeometry(geometry);
|
pSizable::setGeometry(geometry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,9 @@ static auto CALLBACK Window_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARA
|
||||||
return Shared_windowProc(DefWindowProc, hwnd, msg, wparam, lparam);
|
return Shared_windowProc(DefWindowProc, hwnd, msg, wparam, lparam);
|
||||||
}
|
}
|
||||||
|
|
||||||
//warning: do not add WS_CLIPCHILDREN; this will break painting of Frame ("BUTTON" BS_GROUPBOX) controls
|
static const uint PopupStyle = WS_POPUP | WS_CLIPCHILDREN;
|
||||||
static const uint PopupStyle = WS_POPUP;
|
static const uint FixedStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_BORDER | WS_CLIPCHILDREN;
|
||||||
static const uint FixedStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_BORDER;
|
static const uint ResizableStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN;
|
||||||
static const uint ResizableStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME;
|
|
||||||
|
|
||||||
uint pWindow::minimumStatusHeight = 0;
|
uint pWindow::minimumStatusHeight = 0;
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,7 @@ ifeq ($(findstring clang++,$(compiler)),clang++)
|
||||||
flags += -fno-strict-aliasing -fwrapv -Wno-everything
|
flags += -fno-strict-aliasing -fwrapv -Wno-everything
|
||||||
# gcc settings
|
# gcc settings
|
||||||
else ifeq ($(findstring g++,$(compiler)),g++)
|
else ifeq ($(findstring g++,$(compiler)),g++)
|
||||||
flags += -fno-strict-aliasing -fwrapv
|
flags += -fno-strict-aliasing -fwrapv -Wno-trigraphs
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# windows settings
|
# windows settings
|
||||||
|
|
|
@ -49,6 +49,55 @@ inline auto timestamp() -> uint64_t {
|
||||||
return ::time(nullptr);
|
return ::time(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//0 = failure condition
|
||||||
|
inline auto timestamp(const string& datetime) -> uint64_t {
|
||||||
|
static const uint monthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||||
|
uint64_t timestamp = 0;
|
||||||
|
if(datetime.match("??????????")) {
|
||||||
|
return datetime.natural();
|
||||||
|
}
|
||||||
|
if(datetime.match("????*")) {
|
||||||
|
uint year = datetime.slice(0, 4).natural();
|
||||||
|
if(year < 1970 || year > 2199) return 0;
|
||||||
|
for(uint y = 1970; y < year && y < 2999; y++) {
|
||||||
|
uint daysInYear = 365;
|
||||||
|
if(y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) daysInYear++;
|
||||||
|
timestamp += daysInYear * 24 * 60 * 60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(datetime.match("????-??*")) {
|
||||||
|
uint y = datetime.slice(0, 4).natural();
|
||||||
|
uint month = datetime.slice(5, 2).natural();
|
||||||
|
if(month < 1 || month > 12) return 0;
|
||||||
|
for(uint m = 1; m < month && m < 12; m++) {
|
||||||
|
uint daysInMonth = monthDays[m - 1];
|
||||||
|
if(m == 2 && y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) daysInMonth++;
|
||||||
|
timestamp += daysInMonth * 24 * 60 * 60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(datetime.match("????-??-??*")) {
|
||||||
|
uint day = datetime.slice(8, 2).natural();
|
||||||
|
if(day < 1 || day > 31) return 0;
|
||||||
|
timestamp += (day - 1) * 24 * 60 * 60;
|
||||||
|
}
|
||||||
|
if(datetime.match("????-??-?? ??*")) {
|
||||||
|
uint hour = datetime.slice(11, 2).natural();
|
||||||
|
if(hour > 23) return 0;
|
||||||
|
timestamp += hour * 60 * 60;
|
||||||
|
}
|
||||||
|
if(datetime.match("????-??-?? ??:??*")) {
|
||||||
|
uint minute = datetime.slice(14, 2).natural();
|
||||||
|
if(minute > 59) return 0;
|
||||||
|
timestamp += minute * 60;
|
||||||
|
}
|
||||||
|
if(datetime.match("????-??-?? ??:??:??*")) {
|
||||||
|
uint second = datetime.slice(17, 2).natural();
|
||||||
|
if(second > 59) return 0;
|
||||||
|
timestamp += second;
|
||||||
|
}
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
namespace utc {
|
namespace utc {
|
||||||
inline auto timeinfo(uint64_t time = 0) -> chrono::timeinfo {
|
inline auto timeinfo(uint64_t time = 0) -> chrono::timeinfo {
|
||||||
auto stamp = time ? (time_t)time : (time_t)timestamp();
|
auto stamp = time ? (time_t)time : (time_t)timestamp();
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
/* SQLite3 C++ RAII wrapper for nall
|
//SQLite3 C++ RAII wrapper for nall
|
||||||
*
|
//note: it is safe (no-op) to call sqlite3_* functions on null sqlite3 objects
|
||||||
* Note on code below: it is safe (no-op) to call sqlite3_* functions on null sqlite3 objects
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
@ -34,24 +32,28 @@ struct SQLite3 {
|
||||||
return sqlite3_data_count(statement());
|
return sqlite3_data_count(statement());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto columns() -> unsigned {
|
auto columns() -> uint {
|
||||||
return sqlite3_column_count(statement());
|
return sqlite3_column_count(statement());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto integer(unsigned column) -> int64_t {
|
auto boolean(uint column) -> bool {
|
||||||
|
return sqlite3_column_int64(statement(), column) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto integer(uint column) -> int64_t {
|
||||||
return sqlite3_column_int64(statement(), column);
|
return sqlite3_column_int64(statement(), column);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto natural(unsigned column) -> uint64_t {
|
auto natural(uint column) -> uint64_t {
|
||||||
return sqlite3_column_int64(statement(), column);
|
return sqlite3_column_int64(statement(), column);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto real(unsigned column) -> double {
|
auto real(uint column) -> double {
|
||||||
return sqlite3_column_double(statement(), column);
|
return sqlite3_column_double(statement(), column);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto text(unsigned column) -> string {
|
auto string(uint column) -> nall::string {
|
||||||
string result;
|
nall::string result;
|
||||||
if(auto text = sqlite3_column_text(statement(), column)) {
|
if(auto text = sqlite3_column_text(statement(), column)) {
|
||||||
result.resize(sqlite3_column_bytes(statement(), column));
|
result.resize(sqlite3_column_bytes(statement(), column));
|
||||||
memory::copy(result.get(), text, result.size());
|
memory::copy(result.get(), text, result.size());
|
||||||
|
@ -59,7 +61,7 @@ struct SQLite3 {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto data(unsigned column) -> vector<uint8_t> {
|
auto data(uint column) -> vector<uint8_t> {
|
||||||
vector<uint8_t> result;
|
vector<uint8_t> result;
|
||||||
if(auto data = sqlite3_column_blob(statement(), column)) {
|
if(auto data = sqlite3_column_blob(statement(), column)) {
|
||||||
result.resize(sqlite3_column_bytes(statement(), column));
|
result.resize(sqlite3_column_bytes(statement(), column));
|
||||||
|
@ -68,18 +70,19 @@ struct SQLite3 {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto boolean() -> bool { return boolean(_output++); }
|
||||||
auto integer() -> int64_t { return integer(_output++); }
|
auto integer() -> int64_t { return integer(_output++); }
|
||||||
auto natural() -> uint64_t { return natural(_output++); }
|
auto natural() -> uint64_t { return natural(_output++); }
|
||||||
auto real() -> double { return real(_output++); }
|
auto real() -> double { return real(_output++); }
|
||||||
auto text() -> string { return text(_output++); }
|
auto string() -> nall::string { return string(_output++); }
|
||||||
auto data() -> vector<uint8_t> { return data(_output++); }
|
auto data() -> vector<uint8_t> { return data(_output++); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual auto statement() -> sqlite3_stmt* { return _statement; }
|
virtual auto statement() -> sqlite3_stmt* { return _statement; }
|
||||||
|
|
||||||
sqlite3_stmt* _statement = nullptr;
|
sqlite3_stmt* _statement = nullptr;
|
||||||
signed _response = SQLITE_OK;
|
int _response = SQLITE_OK;
|
||||||
unsigned _output = 0;
|
uint _output = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Query : Statement {
|
struct Query : Statement {
|
||||||
|
@ -102,22 +105,34 @@ struct SQLite3 {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& bind(unsigned column, nullptr_t) { sqlite3_bind_null(_statement, 1 + column); return *this; }
|
auto& bind(uint column, nullptr_t) { sqlite3_bind_null(_statement, 1 + column); return *this; }
|
||||||
auto& bind(unsigned column, int32_t value) { sqlite3_bind_int(_statement, 1 + column, value); return *this; }
|
auto& bind(uint column, bool value) { sqlite3_bind_int(_statement, 1 + column, value); return *this; }
|
||||||
auto& bind(unsigned column, uint32_t value) { sqlite3_bind_int(_statement, 1 + column, value); return *this; }
|
auto& bind(uint column, int32_t value) { sqlite3_bind_int(_statement, 1 + column, value); return *this; }
|
||||||
auto& bind(unsigned column, int64_t value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
auto& bind(uint column, uint32_t value) { sqlite3_bind_int(_statement, 1 + column, value); return *this; }
|
||||||
auto& bind(unsigned column, uint64_t value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
auto& bind(uint column, int64_t value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||||
auto& bind(unsigned column, double value) { sqlite3_bind_double(_statement, 1 + column, value); return *this; }
|
auto& bind(uint column, uint64_t value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||||
auto& bind(unsigned column, const string& value) { sqlite3_bind_text(_statement, 1 + column, value.data(), value.size(), SQLITE_TRANSIENT); return *this; }
|
auto& bind(uint column, intmax value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||||
auto& bind(unsigned column, const vector<uint8_t>& value) { sqlite3_bind_blob(_statement, 1 + column, value.data(), value.size(), SQLITE_TRANSIENT); return *this; }
|
auto& bind(uint column, uintmax value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||||
|
auto& bind(uint column, nall::boolean value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||||
|
auto& bind(uint column, nall::integer value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||||
|
auto& bind(uint column, nall::natural value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||||
|
auto& bind(uint column, double value) { sqlite3_bind_double(_statement, 1 + column, value); return *this; }
|
||||||
|
auto& bind(uint column, const nall::string& value) { sqlite3_bind_text(_statement, 1 + column, value.data(), value.size(), SQLITE_TRANSIENT); return *this; }
|
||||||
|
auto& bind(uint column, const vector<uint8_t>& value) { sqlite3_bind_blob(_statement, 1 + column, value.data(), value.size(), SQLITE_TRANSIENT); return *this; }
|
||||||
|
|
||||||
auto& bind(nullptr_t) { return bind(_input++, nullptr); }
|
auto& bind(nullptr_t) { return bind(_input++, nullptr); }
|
||||||
|
auto& bind(bool value) { return bind(_input++, value); }
|
||||||
auto& bind(int32_t value) { return bind(_input++, value); }
|
auto& bind(int32_t value) { return bind(_input++, value); }
|
||||||
auto& bind(uint32_t value) { return bind(_input++, value); }
|
auto& bind(uint32_t value) { return bind(_input++, value); }
|
||||||
auto& bind(int64_t value) { return bind(_input++, value); }
|
auto& bind(int64_t value) { return bind(_input++, value); }
|
||||||
auto& bind(uint64_t value) { return bind(_input++, value); }
|
auto& bind(uint64_t value) { return bind(_input++, value); }
|
||||||
|
auto& bind(intmax value) { return bind(_input++, value); }
|
||||||
|
auto& bind(uintmax value) { return bind(_input++, value); }
|
||||||
|
auto& bind(nall::boolean value) { return bind(_input++, value); }
|
||||||
|
auto& bind(nall::integer value) { return bind(_input++, value); }
|
||||||
|
auto& bind(nall::natural value) { return bind(_input++, value); }
|
||||||
auto& bind(double value) { return bind(_input++, value); }
|
auto& bind(double value) { return bind(_input++, value); }
|
||||||
auto& bind(const string& value) { return bind(_input++, value); }
|
auto& bind(const nall::string& value) { return bind(_input++, value); }
|
||||||
auto& bind(const vector<uint8_t>& value) { return bind(_input++, value); }
|
auto& bind(const vector<uint8_t>& value) { return bind(_input++, value); }
|
||||||
|
|
||||||
auto step() -> bool {
|
auto step() -> bool {
|
||||||
|
@ -145,7 +160,7 @@ struct SQLite3 {
|
||||||
return _statement;
|
return _statement;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned _input = 0;
|
uint _input = 0;
|
||||||
bool _stepped = false;
|
bool _stepped = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
namespace nall::DSP::Resampler {
|
namespace nall::DSP::Resampler {
|
||||||
|
|
||||||
struct Cubic {
|
struct Cubic {
|
||||||
|
inline auto inputFrequency() const -> double { return _inputFrequency; }
|
||||||
|
inline auto outputFrequency() const -> double { return _outputFrequency; }
|
||||||
|
|
||||||
inline auto reset(double inputFrequency, double outputFrequency = 0, uint queueSize = 0) -> void;
|
inline auto reset(double inputFrequency, double outputFrequency = 0, uint queueSize = 0) -> void;
|
||||||
inline auto setInputFrequency(double inputFrequency) -> void;
|
inline auto setInputFrequency(double inputFrequency) -> void;
|
||||||
inline auto pending() const -> bool;
|
inline auto pending() const -> bool;
|
||||||
|
@ -15,41 +18,41 @@ struct Cubic {
|
||||||
inline auto serialize(serializer&) -> void;
|
inline auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
double inputFrequency;
|
double _inputFrequency;
|
||||||
double outputFrequency;
|
double _outputFrequency;
|
||||||
|
|
||||||
double ratio;
|
double _ratio;
|
||||||
double fraction;
|
double _fraction;
|
||||||
double history[4];
|
double _history[4];
|
||||||
queue<double> samples;
|
queue<double> _samples;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto Cubic::reset(double inputFrequency, double outputFrequency, uint queueSize) -> void {
|
auto Cubic::reset(double inputFrequency, double outputFrequency, uint queueSize) -> void {
|
||||||
this->inputFrequency = inputFrequency;
|
_inputFrequency = inputFrequency;
|
||||||
this->outputFrequency = outputFrequency ? outputFrequency : this->inputFrequency;
|
_outputFrequency = outputFrequency ? outputFrequency : _inputFrequency;
|
||||||
|
|
||||||
ratio = inputFrequency / outputFrequency;
|
_ratio = _inputFrequency / _outputFrequency;
|
||||||
fraction = 0.0;
|
_fraction = 0.0;
|
||||||
for(auto& sample : history) sample = 0.0;
|
for(auto& sample : _history) sample = 0.0;
|
||||||
samples.resize(queueSize ? queueSize : this->outputFrequency * 0.02); //default to 20ms max queue size
|
_samples.resize(queueSize ? queueSize : _outputFrequency * 0.02); //default to 20ms max queue size
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Cubic::setInputFrequency(double inputFrequency) -> void {
|
auto Cubic::setInputFrequency(double inputFrequency) -> void {
|
||||||
this->inputFrequency = inputFrequency;
|
_inputFrequency = inputFrequency;
|
||||||
ratio = inputFrequency / outputFrequency;
|
_ratio = _inputFrequency / _outputFrequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Cubic::pending() const -> bool {
|
auto Cubic::pending() const -> bool {
|
||||||
return samples.pending();
|
return _samples.pending();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Cubic::read() -> double {
|
auto Cubic::read() -> double {
|
||||||
return samples.read();
|
return _samples.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Cubic::write(double sample) -> void {
|
auto Cubic::write(double sample) -> void {
|
||||||
auto& mu = fraction;
|
auto& mu = _fraction;
|
||||||
auto& s = history;
|
auto& s = _history;
|
||||||
|
|
||||||
s[0] = s[1];
|
s[0] = s[1];
|
||||||
s[1] = s[2];
|
s[1] = s[2];
|
||||||
|
@ -62,20 +65,20 @@ auto Cubic::write(double sample) -> void {
|
||||||
double C = s[2] - s[0];
|
double C = s[2] - s[0];
|
||||||
double D = s[1];
|
double D = s[1];
|
||||||
|
|
||||||
samples.write(A * mu * mu * mu + B * mu * mu + C * mu + D);
|
_samples.write(A * mu * mu * mu + B * mu * mu + C * mu + D);
|
||||||
mu += ratio;
|
mu += _ratio;
|
||||||
}
|
}
|
||||||
|
|
||||||
mu -= 1.0;
|
mu -= 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Cubic::serialize(serializer& s) -> void {
|
auto Cubic::serialize(serializer& s) -> void {
|
||||||
s.real(inputFrequency);
|
s.real(_inputFrequency);
|
||||||
s.real(outputFrequency);
|
s.real(_outputFrequency);
|
||||||
s.real(ratio);
|
s.real(_ratio);
|
||||||
s.real(fraction);
|
s.real(_fraction);
|
||||||
s.array(history);
|
s.array(_history);
|
||||||
samples.serialize(s);
|
_samples.serialize(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace nall::Encode {
|
||||||
|
|
||||||
|
struct WAV {
|
||||||
|
static auto stereo_16bit(const string& filename, array_view<int16_t> left, array_view<int16_t> right, uint frequency) -> bool {
|
||||||
|
if(left.size() != right.size()) return false;
|
||||||
|
static uint channels = 2;
|
||||||
|
static uint bits = 16;
|
||||||
|
static uint samples = left.size();
|
||||||
|
|
||||||
|
file_buffer fp;
|
||||||
|
if(!fp.open(filename, file::mode::write)) return false;
|
||||||
|
|
||||||
|
fp.write('R');
|
||||||
|
fp.write('I');
|
||||||
|
fp.write('F');
|
||||||
|
fp.write('F');
|
||||||
|
fp.writel(4 + (8 + 16) + (8 + samples * 4), 4);
|
||||||
|
|
||||||
|
fp.write('W');
|
||||||
|
fp.write('A');
|
||||||
|
fp.write('V');
|
||||||
|
fp.write('E');
|
||||||
|
|
||||||
|
fp.write('f');
|
||||||
|
fp.write('m');
|
||||||
|
fp.write('t');
|
||||||
|
fp.write(' ');
|
||||||
|
fp.writel(16, 4);
|
||||||
|
fp.writel(1, 2);
|
||||||
|
fp.writel(channels, 2);
|
||||||
|
fp.writel(frequency, 4);
|
||||||
|
fp.writel(frequency * channels * bits, 4);
|
||||||
|
fp.writel(channels * bits, 2);
|
||||||
|
fp.writel(bits, 2);
|
||||||
|
|
||||||
|
fp.write('d');
|
||||||
|
fp.write('a');
|
||||||
|
fp.write('t');
|
||||||
|
fp.write('a');
|
||||||
|
fp.writel(samples * 4, 4);
|
||||||
|
for(uint sample : range(samples)) {
|
||||||
|
fp.writel(left[sample], 2);
|
||||||
|
fp.writel(right[sample], 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -38,6 +38,7 @@ namespace nall {
|
||||||
#pragma clang diagnostic ignored "-Wtautological-compare"
|
#pragma clang diagnostic ignored "-Wtautological-compare"
|
||||||
#pragma clang diagnostic ignored "-Wabsolute-value"
|
#pragma clang diagnostic ignored "-Wabsolute-value"
|
||||||
#pragma clang diagnostic ignored "-Wshift-count-overflow"
|
#pragma clang diagnostic ignored "-Wshift-count-overflow"
|
||||||
|
#pragma clang diagnostic ignored "-Wtrigraphs"
|
||||||
|
|
||||||
//temporary
|
//temporary
|
||||||
#pragma clang diagnostic ignored "-Winconsistent-missing-override"
|
#pragma clang diagnostic ignored "-Winconsistent-missing-override"
|
||||||
|
@ -51,6 +52,7 @@ namespace nall {
|
||||||
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
|
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
|
||||||
#pragma GCC diagnostic ignored "-Wpragmas"
|
#pragma GCC diagnostic ignored "-Wpragmas"
|
||||||
#pragma GCC diagnostic ignored "-Wswitch-bool"
|
#pragma GCC diagnostic ignored "-Wswitch-bool"
|
||||||
|
#pragma GCC diagnostic ignored "-Wtrigraphs"
|
||||||
#elif defined(_MSC_VER)
|
#elif defined(_MSC_VER)
|
||||||
#define COMPILER_MICROSOFT
|
#define COMPILER_MICROSOFT
|
||||||
constexpr auto compiler() -> Compiler { return Compiler::Microsoft; }
|
constexpr auto compiler() -> Compiler { return Compiler::Microsoft; }
|
||||||
|
|
|
@ -291,7 +291,7 @@ public:
|
||||||
inline auto remove(uint offset, uint length) -> type&;
|
inline auto remove(uint offset, uint length) -> type&;
|
||||||
inline auto reverse() -> type&;
|
inline auto reverse() -> type&;
|
||||||
inline auto size(int length, char fill = ' ') -> type&;
|
inline auto size(int length, char fill = ' ') -> type&;
|
||||||
inline auto slice(int offset = 0, int length = -1) -> string;
|
inline auto slice(int offset = 0, int length = -1) const -> string;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<> struct vector<string> : vector_base<string> {
|
template<> struct vector<string> : vector_base<string> {
|
||||||
|
|
|
@ -9,16 +9,12 @@
|
||||||
namespace nall {
|
namespace nall {
|
||||||
|
|
||||||
struct DML {
|
struct DML {
|
||||||
inline auto title() const -> string { return state.title; }
|
auto content() const -> string { return state.output; }
|
||||||
inline auto subtitle() const -> string { return state.subtitle; }
|
|
||||||
inline auto description() const -> string { return state.description; }
|
|
||||||
inline auto content() const -> string { return state.output; }
|
|
||||||
|
|
||||||
auto& setAllowHTML(bool allowHTML) { settings.allowHTML = allowHTML; return *this; }
|
auto& setAllowHTML(bool allowHTML) { settings.allowHTML = allowHTML; return *this; }
|
||||||
auto& setHost(const string& hostname) { settings.host = {hostname, "/"}; return *this; }
|
auto& setHost(const string& hostname) { settings.host = hostname; return *this; }
|
||||||
auto& setPath(const string& pathname) { settings.path = pathname; return *this; }
|
auto& setPath(const string& pathname) { settings.path = pathname; return *this; }
|
||||||
auto& setReader(const function<string (string)>& reader) { settings.reader = reader; return *this; }
|
auto& setReader(const function<string (string)>& reader) { settings.reader = reader; return *this; }
|
||||||
auto& setSectioned(bool sectioned) { settings.sectioned = sectioned; return *this; }
|
|
||||||
|
|
||||||
auto parse(const string& filedata, const string& pathname) -> string;
|
auto parse(const string& filedata, const string& pathname) -> string;
|
||||||
auto parse(const string& filename) -> string;
|
auto parse(const string& filename) -> string;
|
||||||
|
@ -28,18 +24,13 @@ struct DML {
|
||||||
private:
|
private:
|
||||||
struct Settings {
|
struct Settings {
|
||||||
bool allowHTML = true;
|
bool allowHTML = true;
|
||||||
string host = "localhost/";
|
string host = "localhost";
|
||||||
string path;
|
string path;
|
||||||
function<string (string)> reader;
|
function<string (string)> reader;
|
||||||
bool sectioned = true;
|
|
||||||
} settings;
|
} settings;
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
string title;
|
|
||||||
string subtitle;
|
|
||||||
string description;
|
|
||||||
string output;
|
string output;
|
||||||
uint sections = 0;
|
|
||||||
} state;
|
} state;
|
||||||
|
|
||||||
struct Attribute {
|
struct Attribute {
|
||||||
|
@ -52,6 +43,7 @@ private:
|
||||||
auto parseBlock(string& block, const string& pathname, uint depth) -> bool;
|
auto parseBlock(string& block, const string& pathname, uint depth) -> bool;
|
||||||
auto count(const string& text, char value) -> uint;
|
auto count(const string& text, char value) -> uint;
|
||||||
|
|
||||||
|
auto address(string text) -> string;
|
||||||
auto escape(const string& text) -> string;
|
auto escape(const string& text) -> string;
|
||||||
auto markup(const string& text) -> string;
|
auto markup(const string& text) -> string;
|
||||||
};
|
};
|
||||||
|
@ -83,7 +75,6 @@ inline auto DML::parseDocument(const string& filedata, const string& pathname, u
|
||||||
|
|
||||||
auto blocks = filedata.split("\n\n");
|
auto blocks = filedata.split("\n\n");
|
||||||
for(auto& block : blocks) parseBlock(block, pathname, depth);
|
for(auto& block : blocks) parseBlock(block, pathname, depth);
|
||||||
if(settings.sectioned && state.sections && depth == 0) state.output.append("</section>\n");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +89,18 @@ inline auto DML::parseBlock(string& block, const string& pathname, uint depth) -
|
||||||
parseDocument(document, Location::path(filename), depth + 1);
|
parseDocument(document, Location::path(filename), depth + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//attribute
|
||||||
|
else if(block.beginsWith("? ")) {
|
||||||
|
for(auto n : range(lines.size())) {
|
||||||
|
if(!lines[n].beginsWith("? ")) continue;
|
||||||
|
auto part = lines[n].trimLeft("? ", 1L).split(":", 1L);
|
||||||
|
if(part.size() != 2) continue;
|
||||||
|
auto name = part[0].strip();
|
||||||
|
auto value = part[1].strip();
|
||||||
|
attributes.append({name, value});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//html
|
//html
|
||||||
else if(block.beginsWith("<html>\n") && settings.allowHTML) {
|
else if(block.beginsWith("<html>\n") && settings.allowHTML) {
|
||||||
for(auto n : range(lines.size())) {
|
for(auto n : range(lines.size())) {
|
||||||
|
@ -106,52 +109,18 @@ inline auto DML::parseBlock(string& block, const string& pathname, uint depth) -
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//attribute
|
|
||||||
else if(block.beginsWith("! ")) {
|
|
||||||
for(auto& line : lines) {
|
|
||||||
auto parts = line.trimLeft("! ", 1L).split(":", 1L);
|
|
||||||
if(parts.size() == 2) attributes.append({parts[0].strip(), parts[1].strip()});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//description
|
|
||||||
else if(block.beginsWith("? ")) {
|
|
||||||
while(lines) {
|
|
||||||
state.description.append(lines.takeLeft().trimLeft("? ", 1L), " ");
|
|
||||||
}
|
|
||||||
state.description.strip();
|
|
||||||
}
|
|
||||||
|
|
||||||
//section
|
|
||||||
else if(block.beginsWith("# ")) {
|
|
||||||
if(settings.sectioned) {
|
|
||||||
if(state.sections++) state.output.append("</section>");
|
|
||||||
state.output.append("<section>");
|
|
||||||
}
|
|
||||||
auto content = lines.takeLeft().trimLeft("# ", 1L).split("::", 1L).strip();
|
|
||||||
auto data = markup(content[0]);
|
|
||||||
auto name = escape(content(1, data.hash()));
|
|
||||||
state.subtitle = content[0];
|
|
||||||
state.output.append("<h2 id=\"", name, "\">", data);
|
|
||||||
for(auto& line : lines) {
|
|
||||||
if(!line.beginsWith("# ")) continue;
|
|
||||||
state.output.append("<span>", line.trimLeft("# ", 1L), "</span>");
|
|
||||||
}
|
|
||||||
state.output.append("</h2>\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
//header
|
//header
|
||||||
else if(auto depth = count(block, '=')) {
|
else if(auto depth = count(block, '#')) {
|
||||||
auto content = slice(lines.takeLeft(), depth + 1).split("::", 1L).strip();
|
auto content = slice(lines.takeLeft(), depth + 1).split("::", 1L).strip();
|
||||||
auto data = markup(content[0]);
|
auto data = markup(content[0]);
|
||||||
auto name = escape(content(1, data.hash()));
|
auto name = escape(content(1, data.hash()));
|
||||||
if(depth <= 4) {
|
if(depth <= 5) {
|
||||||
state.output.append("<h", depth + 2, " id=\"", name, "\">", data);
|
state.output.append("<h", depth + 1, " id=\"", name, "\">", data);
|
||||||
for(auto& line : lines) {
|
for(auto& line : lines) {
|
||||||
if(count(line, '=') != depth) continue;
|
if(count(line, '#') != depth) continue;
|
||||||
state.output.append("<span>", slice(line, depth + 1), "</span>");
|
state.output.append("<span>", slice(line, depth + 1), "</span>");
|
||||||
}
|
}
|
||||||
state.output.append("</h", depth + 2, ">\n");
|
state.output.append("</h", depth + 1, ">\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,6 +208,29 @@ inline auto DML::count(const string& text, char value) -> uint {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// . => domain
|
||||||
|
// ./* => domain/*
|
||||||
|
// ../subdomain => subdomain.domain
|
||||||
|
// ../subdomain/* => subdomain.domain/*
|
||||||
|
inline auto DML::address(string s) -> string {
|
||||||
|
if(s.beginsWith("../")) {
|
||||||
|
s.trimLeft("../", 1L);
|
||||||
|
if(auto p = s.find("/")) {
|
||||||
|
return {"//", s.slice(0, *p), ".", settings.host, s.slice(*p)};
|
||||||
|
} else {
|
||||||
|
return {"//", s, ".", settings.host};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(s.beginsWith("./")) {
|
||||||
|
s.trimLeft(".", 1L);
|
||||||
|
return {"//", settings.host, s};
|
||||||
|
}
|
||||||
|
if(s == ".") {
|
||||||
|
return {"//", settings.host};
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
inline auto DML::escape(const string& text) -> string {
|
inline auto DML::escape(const string& text) -> string {
|
||||||
string output;
|
string output;
|
||||||
for(auto c : text) {
|
for(auto c : text) {
|
||||||
|
@ -281,8 +273,8 @@ inline auto DML::markup(const string& s) -> string {
|
||||||
|
|
||||||
if(link && !image && a == ']' && b == ']') {
|
if(link && !image && a == ']' && b == ']') {
|
||||||
auto list = slice(s, link(), n - link()).split("::", 1L);
|
auto list = slice(s, link(), n - link()).split("::", 1L);
|
||||||
string uri = list.last();
|
string uri = address(list.last());
|
||||||
string name = list.size() == 2 ? list.first() : list.last().split("//", 1L).last();
|
string name = list.size() == 2 ? list.first() : uri.split("//", 1L).last();
|
||||||
|
|
||||||
t.append("<a href=\"", escape(uri), "\">", escape(name), "</a>");
|
t.append("<a href=\"", escape(uri), "\">", escape(name), "</a>");
|
||||||
|
|
||||||
|
@ -294,8 +286,8 @@ inline auto DML::markup(const string& s) -> string {
|
||||||
if(image && !link && a == '}' && b == '}') {
|
if(image && !link && a == '}' && b == '}') {
|
||||||
auto side = slice(s, image(), n - image()).split("}{", 1L);
|
auto side = slice(s, image(), n - image()).split("}{", 1L);
|
||||||
auto list = side(0).split("::", 1L);
|
auto list = side(0).split("::", 1L);
|
||||||
string uri = list.last();
|
string uri = address(list.last());
|
||||||
string name = list.size() == 2 ? list.first() : list.last().split("//", 1L).last();
|
string name = list.size() == 2 ? list.first() : uri.split("//", 1L).last();
|
||||||
list = side(1).split("; ");
|
list = side(1).split("; ");
|
||||||
boolean link, title, caption;
|
boolean link, title, caption;
|
||||||
string width, height;
|
string width, height;
|
||||||
|
@ -322,7 +314,6 @@ inline auto DML::markup(const string& s) -> string {
|
||||||
if(link) t.append("<a href=\"", escape(uri), "\">");
|
if(link) t.append("<a href=\"", escape(uri), "\">");
|
||||||
t.append("<img loading=\"lazy\" src=\"", escape(uri), "\" alt=\"", escape(name ? name : uri.hash()), "\"");
|
t.append("<img loading=\"lazy\" src=\"", escape(uri), "\" alt=\"", escape(name ? name : uri.hash()), "\"");
|
||||||
if(title) t.append(" title=\"", escape(name), "\"");
|
if(title) t.append(" title=\"", escape(name), "\"");
|
||||||
if(width && height) t.append(" style=\"width: ", escape(width), "px; max-height: ", escape(height), "px;\"");
|
|
||||||
if(width) t.append(" width=\"", escape(width), "\"");
|
if(width) t.append(" width=\"", escape(width), "\"");
|
||||||
if(height) t.append(" height=\"", escape(height), "\"");
|
if(height) t.append(" height=\"", escape(height), "\"");
|
||||||
t.append(">");
|
t.append(">");
|
||||||
|
|
|
@ -95,7 +95,7 @@ auto slice(string_view self, int offset, int length) -> string {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto string::slice(int offset, int length) -> string {
|
auto string::slice(int offset, int length) const -> string {
|
||||||
return nall::slice(*this, offset, length);
|
return nall::slice(*this, offset, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
ifeq ($(ruby),)
|
ifeq ($(ruby),)
|
||||||
ifeq ($(platform),windows)
|
ifeq ($(platform),windows)
|
||||||
ruby += video.wgl video.direct3d video.directdraw video.gdi
|
ruby += video.wgl video.direct3d video.directdraw video.gdi
|
||||||
ruby += audio.wasapi audio.xaudio2 audio.directsound audio.waveout #audio.asio
|
ruby += audio.asio audio.wasapi audio.xaudio2 audio.directsound audio.waveout
|
||||||
ruby += input.windows
|
ruby += input.windows
|
||||||
else ifeq ($(platform),macos)
|
else ifeq ($(platform),macos)
|
||||||
ruby += video.cgl
|
ruby += video.cgl
|
||||||
|
@ -13,7 +13,7 @@ ifeq ($(ruby),)
|
||||||
ruby += input.sdl input.xlib input.udev
|
ruby += input.sdl input.xlib input.udev
|
||||||
else ifeq ($(platform),bsd)
|
else ifeq ($(platform),bsd)
|
||||||
ruby += video.glx video.glx2 video.xvideo video.xshm
|
ruby += video.glx video.glx2 video.xvideo video.xshm
|
||||||
ruby += audio.oss #audio.pulseaudio
|
ruby += audio.oss audio.openal #audio.pulseaudio
|
||||||
ruby += input.sdl input.xlib
|
ruby += input.sdl input.xlib
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
@ -65,7 +65,8 @@ endif
|
||||||
|
|
||||||
ifeq ($(platform),bsd)
|
ifeq ($(platform),bsd)
|
||||||
ruby.options += -lX11 -lXext -lXrandr
|
ruby.options += -lX11 -lXext -lXrandr
|
||||||
ruby.options += $(if $(findstring audio.openal,$(ruby)),-lopenal)
|
ruby.options += $(if $(findstring audio.openal,$(ruby)),-lopenal -fuse-ld=bfd)
|
||||||
|
# -fuse-ld=bfd: see FreeBSD bug 219089
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ruby.objects := $(object.path)/ruby.o
|
ruby.objects := $(object.path)/ruby.o
|
||||||
|
|
|
@ -17,6 +17,7 @@ struct AudioALSA : AudioDriver {
|
||||||
auto ready() -> bool override { return _ready; }
|
auto ready() -> bool override { return _ready; }
|
||||||
|
|
||||||
auto hasBlocking() -> bool override { return true; }
|
auto hasBlocking() -> bool override { return true; }
|
||||||
|
auto hasDynamic() -> bool override { return true; }
|
||||||
|
|
||||||
auto hasDevices() -> vector<string> override {
|
auto hasDevices() -> vector<string> override {
|
||||||
vector<string> devices;
|
vector<string> devices;
|
||||||
|
@ -55,20 +56,27 @@ struct AudioALSA : AudioDriver {
|
||||||
auto setLatency(uint latency) -> bool override { return initialize(); }
|
auto setLatency(uint latency) -> bool override { return initialize(); }
|
||||||
|
|
||||||
auto level() -> double override {
|
auto level() -> double override {
|
||||||
snd_pcm_sframes_t available = snd_pcm_avail_update(_interface);
|
snd_pcm_sframes_t available;
|
||||||
if(available < 0) return 0.5;
|
for(uint timeout : range(256)) {
|
||||||
|
available = snd_pcm_avail_update(_interface);
|
||||||
|
if(available >= 0) break;
|
||||||
|
snd_pcm_recover(_interface, available, 1);
|
||||||
|
}
|
||||||
return (double)(_bufferSize - available) / _bufferSize;
|
return (double)(_bufferSize - available) / _bufferSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto output(const double samples[]) -> void override {
|
auto output(const double samples[]) -> void override {
|
||||||
_buffer[_offset] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0;
|
_buffer[_offset] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0;
|
||||||
_buffer[_offset] |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16;
|
_buffer[_offset] |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16;
|
||||||
_offset++;
|
if(++_offset < _periodSize) return;
|
||||||
|
|
||||||
snd_pcm_sframes_t available;
|
snd_pcm_sframes_t available;
|
||||||
do {
|
do {
|
||||||
available = snd_pcm_avail_update(_interface);
|
available = snd_pcm_avail_update(_interface);
|
||||||
if(available < 0) snd_pcm_recover(_interface, available, 1);
|
if(available < 0) {
|
||||||
|
snd_pcm_recover(_interface, available, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if(available < _offset) {
|
if(available < _offset) {
|
||||||
if(!self.blocking) {
|
if(!self.blocking) {
|
||||||
_offset = 0;
|
_offset = 0;
|
||||||
|
|
|
@ -52,6 +52,11 @@ struct AudioASIO : AudioDriver {
|
||||||
}
|
}
|
||||||
latencies.append(latency);
|
latencies.append(latency);
|
||||||
}
|
}
|
||||||
|
//it is possible that no latencies in the hard-coded list above will match; so ensure driver-declared latencies are available
|
||||||
|
if(!latencies.find(self.activeDevice.minimumBufferSize)) latencies.append(self.activeDevice.minimumBufferSize);
|
||||||
|
if(!latencies.find(self.activeDevice.maximumBufferSize)) latencies.append(self.activeDevice.maximumBufferSize);
|
||||||
|
if(!latencies.find(self.activeDevice.preferredBufferSize)) latencies.append(self.activeDevice.preferredBufferSize);
|
||||||
|
latencies.sort();
|
||||||
return latencies;
|
return latencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +80,15 @@ struct AudioASIO : AudioDriver {
|
||||||
|
|
||||||
auto output(const double samples[]) -> void override {
|
auto output(const double samples[]) -> void override {
|
||||||
if(!ready()) return;
|
if(!ready()) return;
|
||||||
|
//defer call to IASIO::start(), because the drivers themselves will sometimes crash internally.
|
||||||
|
//if software initializes AudioASIO but does not play music at startup, this can prevent a crash loop.
|
||||||
|
if(!_started) {
|
||||||
|
_started = true;
|
||||||
|
if(_asio->start() != ASE_OK) {
|
||||||
|
_ready = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if(self.blocking) {
|
if(self.blocking) {
|
||||||
while(_queue.count >= self.latency);
|
while(_queue.count >= self.latency);
|
||||||
}
|
}
|
||||||
|
@ -151,13 +165,14 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
_ready = true;
|
_ready = true;
|
||||||
|
_started = false;
|
||||||
clear();
|
clear();
|
||||||
if(_asio->start() != ASE_OK) return _ready = false;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto terminate() -> void {
|
auto terminate() -> void {
|
||||||
_ready = false;
|
_ready = false;
|
||||||
|
_started = false;
|
||||||
self.activeDevice = {};
|
self.activeDevice = {};
|
||||||
if(_asio) {
|
if(_asio) {
|
||||||
_asio->stop();
|
_asio->stop();
|
||||||
|
@ -244,6 +259,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _ready = false;
|
bool _ready = false;
|
||||||
|
bool _started = false;
|
||||||
|
|
||||||
struct Queue {
|
struct Queue {
|
||||||
double samples[65536][8];
|
double samples[65536][8];
|
||||||
|
|
|
@ -242,10 +242,10 @@ auto Audio::hasDrivers() -> vector<string> {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Audio::optimalDriver() -> string {
|
auto Audio::optimalDriver() -> string {
|
||||||
#if defined(AUDIO_ASIO)
|
#if defined(AUDIO_WASAPI)
|
||||||
return "ASIO";
|
|
||||||
#elif defined(AUDIO_WASAPI)
|
|
||||||
return "WASAPI";
|
return "WASAPI";
|
||||||
|
#elif defined(AUDIO_ASIO)
|
||||||
|
return "ASIO";
|
||||||
#elif defined(AUDIO_XAUDIO2)
|
#elif defined(AUDIO_XAUDIO2)
|
||||||
return "XAudio 2.1";
|
return "XAudio 2.1";
|
||||||
#elif defined(AUDIO_DIRECTSOUND)
|
#elif defined(AUDIO_DIRECTSOUND)
|
||||||
|
|
|
@ -15,6 +15,7 @@ struct AudioPulseAudio : AudioDriver {
|
||||||
auto ready() -> bool override { return _ready; }
|
auto ready() -> bool override { return _ready; }
|
||||||
|
|
||||||
auto hasBlocking() -> bool override { return true; }
|
auto hasBlocking() -> bool override { return true; }
|
||||||
|
auto hasDynamic() -> bool override { return true; }
|
||||||
|
|
||||||
auto hasFrequencies() -> vector<uint> override {
|
auto hasFrequencies() -> vector<uint> override {
|
||||||
return {44100, 48000, 96000};
|
return {44100, 48000, 96000};
|
||||||
|
@ -28,6 +29,12 @@ struct AudioPulseAudio : AudioDriver {
|
||||||
auto setFrequency(uint frequency) -> bool override { return initialize(); }
|
auto setFrequency(uint frequency) -> bool override { return initialize(); }
|
||||||
auto setLatency(uint latency) -> bool override { return initialize(); }
|
auto setLatency(uint latency) -> bool override { return initialize(); }
|
||||||
|
|
||||||
|
auto level() -> double override {
|
||||||
|
pa_mainloop_iterate(_mainLoop, 0, nullptr);
|
||||||
|
auto length = pa_stream_writable_size(_stream);
|
||||||
|
return (double)(_bufferSize - length) / _bufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
auto output(const double samples[]) -> void override {
|
auto output(const double samples[]) -> void override {
|
||||||
pa_stream_begin_write(_stream, (void**)&_buffer, &_period);
|
pa_stream_begin_write(_stream, (void**)&_buffer, &_period);
|
||||||
_buffer[_offset] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0;
|
_buffer[_offset] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0;
|
||||||
|
@ -35,13 +42,8 @@ struct AudioPulseAudio : AudioDriver {
|
||||||
if((++_offset + 1) * pa_frame_size(&_specification) <= _period) return;
|
if((++_offset + 1) * pa_frame_size(&_specification) <= _period) return;
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
if(_first) {
|
|
||||||
_first = false;
|
|
||||||
pa_mainloop_iterate(_mainLoop, 0, nullptr);
|
pa_mainloop_iterate(_mainLoop, 0, nullptr);
|
||||||
} else {
|
auto length = pa_stream_writable_size(_stream);
|
||||||
pa_mainloop_iterate(_mainLoop, 1, nullptr);
|
|
||||||
}
|
|
||||||
uint length = pa_stream_writable_size(_stream);
|
|
||||||
if(length >= _offset * pa_frame_size(&_specification)) break;
|
if(length >= _offset * pa_frame_size(&_specification)) break;
|
||||||
if(!self.blocking) {
|
if(!self.blocking) {
|
||||||
_offset = 0;
|
_offset = 0;
|
||||||
|
@ -91,9 +93,10 @@ private:
|
||||||
if(!PA_STREAM_IS_GOOD(streamState)) return false;
|
if(!PA_STREAM_IS_GOOD(streamState)) return false;
|
||||||
} while(streamState != PA_STREAM_READY);
|
} while(streamState != PA_STREAM_READY);
|
||||||
|
|
||||||
_period = 960;
|
pa_buffer_attr* attributes = pa_stream_get_buffer_attr(_stream);
|
||||||
|
_period = attributes->minreq;
|
||||||
|
_bufferSize = attributes->tlength;
|
||||||
_offset = 0;
|
_offset = 0;
|
||||||
_first = true;
|
|
||||||
return _ready = true;
|
return _ready = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,11 +130,11 @@ private:
|
||||||
|
|
||||||
uint32_t* _buffer = nullptr;
|
uint32_t* _buffer = nullptr;
|
||||||
size_t _period = 0;
|
size_t _period = 0;
|
||||||
|
size_t _bufferSize = 0;
|
||||||
uint _offset = 0;
|
uint _offset = 0;
|
||||||
|
|
||||||
pa_mainloop* _mainLoop = nullptr;
|
pa_mainloop* _mainLoop = nullptr;
|
||||||
pa_context* _context = nullptr;
|
pa_context* _context = nullptr;
|
||||||
pa_stream* _stream = nullptr;
|
pa_stream* _stream = nullptr;
|
||||||
pa_sample_spec _specification;
|
pa_sample_spec _specification;
|
||||||
bool _first = true;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,7 +14,6 @@ struct InputJoypadIOKit {
|
||||||
for(uint n : range(CFArrayGetCount(elements))) {
|
for(uint n : range(CFArrayGetCount(elements))) {
|
||||||
IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, n);
|
IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, n);
|
||||||
IOHIDElementType type = IOHIDElementGetType(element);
|
IOHIDElementType type = IOHIDElementGetType(element);
|
||||||
uint32_t page = IOHIDElementGetUsagePage(element);
|
|
||||||
uint32_t usage = IOHIDElementGetUsage(element);
|
uint32_t usage = IOHIDElementGetUsage(element);
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case kIOHIDElementTypeInput_Button:
|
case kIOHIDElementTypeInput_Button:
|
||||||
|
@ -22,7 +21,6 @@ struct InputJoypadIOKit {
|
||||||
break;
|
break;
|
||||||
case kIOHIDElementTypeInput_Axis:
|
case kIOHIDElementTypeInput_Axis:
|
||||||
case kIOHIDElementTypeInput_Misc:
|
case kIOHIDElementTypeInput_Misc:
|
||||||
if(page != kHIDPage_GenericDesktop && page != kHIDPage_Simulation) break;
|
|
||||||
if(usage == kHIDUsage_Sim_Accelerator || usage == kHIDUsage_Sim_Brake
|
if(usage == kHIDUsage_Sim_Accelerator || usage == kHIDUsage_Sim_Brake
|
||||||
|| usage == kHIDUsage_Sim_Rudder || usage == kHIDUsage_Sim_Throttle
|
|| usage == kHIDUsage_Sim_Rudder || usage == kHIDUsage_Sim_Throttle
|
||||||
|| usage == kHIDUsage_GD_X || usage == kHIDUsage_GD_Y || usage == kHIDUsage_GD_Z
|
|| usage == kHIDUsage_GD_X || usage == kHIDUsage_GD_Y || usage == kHIDUsage_GD_Z
|
||||||
|
|
|
@ -66,6 +66,10 @@ struct VideoCGL : VideoDriver, OpenGL {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto focused() -> bool override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
auto clear() -> void override {
|
auto clear() -> void override {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
[view lockFocus];
|
[view lockFocus];
|
||||||
|
|
|
@ -34,6 +34,12 @@ struct VideoDirect3D : VideoDriver {
|
||||||
auto setBlocking(bool blocking) -> bool override { return true; }
|
auto setBlocking(bool blocking) -> bool override { return true; }
|
||||||
auto setShader(string shader) -> bool override { return updateFilter(); }
|
auto setShader(string shader) -> bool override { return updateFilter(); }
|
||||||
|
|
||||||
|
auto focused() -> bool override {
|
||||||
|
if(self.fullScreen && self.exclusive) return true;
|
||||||
|
auto focused = GetFocus();
|
||||||
|
return _context == focused || IsChild(_context, focused);
|
||||||
|
}
|
||||||
|
|
||||||
auto clear() -> void override {
|
auto clear() -> void override {
|
||||||
if(_lost && !recover()) return;
|
if(_lost && !recover()) return;
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,12 @@ struct VideoDirectDraw : VideoDriver {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto focused() -> bool override {
|
||||||
|
if(self.fullScreen && self.exclusive) return true;
|
||||||
|
auto focused = GetFocus();
|
||||||
|
return _context == focused || IsChild(_context, focused);
|
||||||
|
}
|
||||||
|
|
||||||
auto clear() -> void override {
|
auto clear() -> void override {
|
||||||
DDBLTFX fx{};
|
DDBLTFX fx{};
|
||||||
fx.dwSize = sizeof(DDBLTFX);
|
fx.dwSize = sizeof(DDBLTFX);
|
||||||
|
|
|
@ -24,6 +24,12 @@ struct VideoGDI : VideoDriver {
|
||||||
auto setMonitor(string monitor) -> bool override { return initialize(); }
|
auto setMonitor(string monitor) -> bool override { return initialize(); }
|
||||||
auto setContext(uintptr context) -> bool override { return initialize(); }
|
auto setContext(uintptr context) -> bool override { return initialize(); }
|
||||||
|
|
||||||
|
auto focused() -> bool override {
|
||||||
|
if(self.fullScreen && self.exclusive) return true;
|
||||||
|
auto focused = GetFocus();
|
||||||
|
return _context == focused || IsChild(_context, focused);
|
||||||
|
}
|
||||||
|
|
||||||
auto size(uint& width, uint& height) -> void override {
|
auto size(uint& width, uint& height) -> void override {
|
||||||
RECT rectangle;
|
RECT rectangle;
|
||||||
GetClientRect(_context, &rectangle);
|
GetClientRect(_context, &rectangle);
|
||||||
|
|
|
@ -74,6 +74,10 @@ struct VideoGLX : VideoDriver, OpenGL {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto focused() -> bool override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
auto clear() -> void override {
|
auto clear() -> void override {
|
||||||
OpenGL::clear();
|
OpenGL::clear();
|
||||||
if(_doubleBuffer) glXSwapBuffers(_display, _glXWindow);
|
if(_doubleBuffer) glXSwapBuffers(_display, _glXWindow);
|
||||||
|
|
|
@ -83,6 +83,10 @@ struct VideoGLX2 : VideoDriver {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto focused() -> bool override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
auto clear() -> void override {
|
auto clear() -> void override {
|
||||||
memory::fill<uint32_t>(_glBuffer, _glWidth * _glHeight);
|
memory::fill<uint32_t>(_glBuffer, _glWidth * _glHeight);
|
||||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||||
|
|
|
@ -94,6 +94,10 @@ auto Video::setShader(string shader) -> bool {
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
auto Video::focused() -> bool {
|
||||||
|
return instance->focused();
|
||||||
|
}
|
||||||
|
|
||||||
auto Video::clear() -> void {
|
auto Video::clear() -> void {
|
||||||
return instance->clear();
|
return instance->clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ struct VideoDriver {
|
||||||
virtual auto setFormat(string format) -> bool { return true; }
|
virtual auto setFormat(string format) -> bool { return true; }
|
||||||
virtual auto setShader(string shader) -> bool { return true; }
|
virtual auto setShader(string shader) -> bool { return true; }
|
||||||
|
|
||||||
|
virtual auto focused() -> bool { return true; }
|
||||||
virtual auto clear() -> void {}
|
virtual auto clear() -> void {}
|
||||||
virtual auto size(uint& width, uint& height) -> void {}
|
virtual auto size(uint& width, uint& height) -> void {}
|
||||||
virtual auto acquire(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { return false; }
|
virtual auto acquire(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { return false; }
|
||||||
|
@ -108,6 +109,7 @@ struct Video {
|
||||||
auto setFormat(string format) -> bool;
|
auto setFormat(string format) -> bool;
|
||||||
auto setShader(string shader) -> bool;
|
auto setShader(string shader) -> bool;
|
||||||
|
|
||||||
|
auto focused() -> bool;
|
||||||
auto clear() -> void;
|
auto clear() -> void;
|
||||||
struct Size {
|
struct Size {
|
||||||
uint width = 0;
|
uint width = 0;
|
||||||
|
|
|
@ -53,6 +53,12 @@ struct VideoWGL : VideoDriver, OpenGL {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto focused() -> bool override {
|
||||||
|
if(self.fullScreen && self.exclusive) return true;
|
||||||
|
auto focused = GetFocus();
|
||||||
|
return _context == focused || IsChild(_context, focused);
|
||||||
|
}
|
||||||
|
|
||||||
auto clear() -> void override {
|
auto clear() -> void override {
|
||||||
OpenGL::clear();
|
OpenGL::clear();
|
||||||
SwapBuffers(_display);
|
SwapBuffers(_display);
|
||||||
|
|
|
@ -31,6 +31,10 @@ struct VideoXShm : VideoDriver {
|
||||||
auto setContext(uintptr context) -> bool override { return initialize(); }
|
auto setContext(uintptr context) -> bool override { return initialize(); }
|
||||||
auto setShader(string shader) -> bool override { return true; }
|
auto setShader(string shader) -> bool override { return true; }
|
||||||
|
|
||||||
|
auto focused() -> bool override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
auto clear() -> void override {
|
auto clear() -> void override {
|
||||||
auto dp = _inputBuffer;
|
auto dp = _inputBuffer;
|
||||||
uint length = _inputWidth * _inputHeight;
|
uint length = _inputWidth * _inputHeight;
|
||||||
|
|
|
@ -57,6 +57,10 @@ struct VideoXVideo : VideoDriver {
|
||||||
return initialize();
|
return initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto focused() -> bool override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
auto clear() -> void override {
|
auto clear() -> void override {
|
||||||
memory::fill<uint32_t>(_buffer, _bufferWidth * _bufferHeight);
|
memory::fill<uint32_t>(_buffer, _bufferWidth * _bufferHeight);
|
||||||
//clear twice in case video is double buffered ...
|
//clear twice in case video is double buffered ...
|
||||||
|
|
Loading…
Reference in New Issue