Update to 20180729 release.

byuu wrote:

Sigh ...

asio.hpp needs #include <nall/windows/registry.hpp>

[Since the last WIP, byuu also posted the following message. -Ed.]

ruby drivers have all been updated (but not tested outside of BSD), and
I redesigned the settings window. The driver functionality all exists on
a new "Drivers" panel, the emulator/hack settings go to a
"Configuration" panel, and the video/audio panels lose driver settings.
As does the settings menu and its synchronize options.

I want to start pushing toward a v107 release. Critically, I will need
DirectSound and ALSA to support dynamic rate control. I'd also like to
eliminate the other system manifest.bml files. I need to update the
cheat code database format, and bundle at least a few quark shaders --
although I still need to default to Direct3D on Windows.

Turbo keys would be nice, if it's not too much effort. Aside from
netplay, it's the last significant feature I'm missing.

I think for v107, higan is going to be a bit rough around the edges
compared to bsnes. And I don't think it's practical to finish the bsnes
localization support.

I'm thinking we probably want another WIP to iron out any critical
issues, but this time there should be a feature freeze with the next
WIP.
This commit is contained in:
Tim Allen 2018-07-29 23:24:38 +10:00
parent 716c95f279
commit 5deba5cbc1
182 changed files with 1533 additions and 2674 deletions

View File

@ -50,13 +50,10 @@ template<uint Frequency> auto SMP::Timer<Frequency>::step(uint clocks) -> void {
}
template<uint Frequency> auto SMP::Timer<Frequency>::synchronizeStage1() -> void {
bool newLine = stage1;
if(!smp.io.timersEnable) newLine = false;
if(smp.io.timersDisable) newLine = false;
bool oldLine = line;
line = newLine;
if(oldLine != 1 || newLine != 0) return; //only pulse on 1->0 transition
bool level = stage1;
if(!smp.io.timersEnable) level = false;
if(smp.io.timersDisable) level = false;
if(!line.lower(level)) return; //only pulse on 1->0 transition
//stage 2 increment
if(!enable) return;

View File

@ -27,7 +27,7 @@ auto nall::main(vector<string> arguments) -> void {
}
}
Application::setName("bsnes");
Application::setScreenSaver(false);
Application::setScreenSaver(!settings["UserInterface/SuppressScreenSaver"].boolean());
Application::locale().scan(locate("locales/"));
Application::locale().select(locale);
emulator = new SuperFamicom::Interface;

View File

@ -102,6 +102,12 @@ auto InputMapping::unbind() -> void {
}
auto InputMapping::poll() -> int16 {
if(turboID) {
auto& mapping = inputManager->ports[portID].devices[deviceID].mappings[turboID()];
auto result = mapping.poll();
if(result) return inputManager->turboCounter >= inputManager->turboFrequency;
}
int16 result;
for(auto& mapping : mappings) {
@ -185,23 +191,53 @@ auto InputManager::initialize() -> void {
if(!input) return;
input->onChange({&InputManager::onChange, this});
lastPoll = chrono::millisecond();
frequency = max(1u, settings["Input/Frequency"].natural());
for(auto& port : emulator->ports()) {
turboCounter = 0;
turboFrequency = max(1, settings["Input/Turbo/Frequency"].natural());
auto information = emulator->information();
auto ports = emulator->ports();
for(uint portID : range(ports.size())) {
auto& port = ports[portID];
InputPort inputPort{port.id, port.name};
for(auto& device : emulator->devices(port.id)) {
auto devices = emulator->devices(port.id);
for(uint deviceID : range(devices.size())) {
auto& device = devices[deviceID];
InputDevice inputDevice{device.id, device.name};
for(auto& input : emulator->inputs(device.id)) {
auto inputs = emulator->inputs(device.id);
for(uint inputID : range(inputs.size())) {
auto& input = inputs[inputID];
InputMapping inputMapping;
inputMapping.portID = portID;
inputMapping.deviceID = deviceID;
inputMapping.inputID = inputID;
inputMapping.name = input.name;
inputMapping.type = input.type;
inputMapping.path = string{"Emulator/", inputPort.name, "/", inputDevice.name, "/", inputMapping.name}.replace(" ", "");
inputMapping.path = string{information.name, "/", inputPort.name, "/", inputDevice.name, "/", inputMapping.name}.replace(" ", "");
inputMapping.assignment = settings(inputMapping.path).text();
inputDevice.mappings.append(inputMapping);
}
for(uint inputID : range(inputs.size())) {
auto& input = inputs[inputID];
if(input.type != InputMapping::Type::Button && input.type != InputMapping::Type::Trigger) continue;
uint turboID = inputDevice.mappings.size();
InputMapping inputMapping;
inputMapping.portID = portID;
inputMapping.deviceID = deviceID;
inputMapping.inputID = turboID;
inputMapping.name = string{"Turbo ", input.name};
inputMapping.type = input.type;
inputMapping.path = string{information.name, "/", inputPort.name, "/", inputDevice.name, "/", inputMapping.name}.replace(" ", "");
inputMapping.assignment = settings(inputMapping.path).text();
inputDevice.mappings.append(inputMapping);
inputDevice.mappings[inputID].turboID = turboID;
}
inputPort.devices.append(inputDevice);
}
ports.append(inputPort);
this->ports.append(inputPort);
}
bindHotkeys();
@ -242,6 +278,10 @@ auto InputManager::poll() -> void {
}
}
auto InputManager::frame() -> void {
if(++turboCounter >= turboFrequency * 2) turboCounter = 0;
}
auto InputManager::onChange(shared_pointer<HID::Device> device, uint group, uint input, int16_t oldValue, int16_t newValue) -> void {
if(settingsWindow->focused()) {
settingsWindow->input.inputEvent(device, group, input, oldValue, newValue);

View File

@ -18,6 +18,11 @@ struct InputMapping {
return type == Type::Rumble;
}
uint portID = 0;
uint deviceID = 0;
uint inputID = 0;
maybe<uint> turboID;
string path; //configuration file key path
string name; //input name (human readable)
uint type = 0;
@ -34,6 +39,8 @@ struct InputMapping {
Qualifier qualifier = Qualifier::None;
};
vector<Mapping> mappings;
uint3 turboCounter = 0;
};
struct InputHotkey : InputMapping {
@ -64,6 +71,7 @@ struct InputManager {
auto initialize() -> void;
auto bind() -> void;
auto poll() -> void;
auto frame() -> void;
auto onChange(shared_pointer<HID::Device> device, uint group, uint input, int16_t oldValue, int16_t newValue) -> void;
auto mapping(uint port, uint device, uint input) -> maybe<InputMapping&>;
auto findMouse() -> shared_pointer<HID::Device>;
@ -79,6 +87,9 @@ public:
uint64 lastPoll; //time in milliseconds since last call to poll()
uint64 frequency; //minimum time in milliseconds before poll() can be called again
uint turboCounter = 0;
uint turboFrequency = 0;
};
extern unique_pointer<InputManager> inputManager;

View File

@ -56,14 +56,6 @@ Presentation::Presentation() {
emulator->configure("video/blurEmulation", blurEmulation.checked());
}).doToggle();
shaderMenu.setIcon(Icon::Emblem::Image).setText("Shader");
synchronizeVideo.setText("Synchronize Video").setChecked(settings["Video/Blocking"].boolean()).onToggle([&] {
settings["Video/Blocking"].setValue(synchronizeVideo.checked());
program->updateVideoBlocking();
});
synchronizeAudio.setText("Synchronize Audio").setChecked(settings["Audio/Blocking"].boolean()).onToggle([&] {
settings["Audio/Blocking"].setValue(synchronizeAudio.checked());
program->updateAudioBlocking();
});
muteAudio.setText("Mute Audio").setChecked(settings["Audio/Mute"].boolean()).onToggle([&] {
settings["Audio/Mute"].setValue(muteAudio.checked());
program->updateAudioEffects();
@ -82,28 +74,47 @@ Presentation::Presentation() {
inputSettings.setIcon(Icon::Device::Joypad).setText("Input ...").onActivate([&] { settingsWindow->show(2); });
hotkeySettings.setIcon(Icon::Device::Keyboard).setText("Hotkeys ...").onActivate([&] { settingsWindow->show(3); });
pathSettings.setIcon(Icon::Emblem::Folder).setText("Paths ...").onActivate([&] { settingsWindow->show(4); });
advancedSettings.setIcon(Icon::Action::Settings).setText("Advanced ...").onActivate([&] { settingsWindow->show(5); });
configurationSettings.setIcon(Icon::Action::Settings).setText("Configuration ...").onActivate([&] { settingsWindow->show(5); });
driverSettings.setIcon(Icon::Place::Settings).setText("Drivers ...").onActivate([&] { settingsWindow->show(6); });
toolsMenu.setText(tr("Tools")).setVisible(false);
saveState.setIcon(Icon::Action::Save).setText("Save State");
for(uint index : range(QuickStates)) {
saveState.append(MenuItem().setText({"Slot ", 1 + index}).onActivate([=] {
program->saveState({"quick/slot ", 1 + index});
}));
MenuItem item{&saveState};
item.setProperty("name", {"quick/slot ", 1 + index});
item.setProperty("title", {"Slot ", 1 + index});
item.setText({"Slot ", 1 + index});
item.onActivate([=] { program->saveState({"quick/slot ", 1 + index}); });
}
loadState.setIcon(Icon::Media::Play).setText("Load State");
for(uint index : range(QuickStates)) {
loadState.append(MenuItem().setText({"Slot ", 1 + index}).onActivate([=] {
program->loadState({"quick/slot ", 1 + index});
}));
MenuItem item{&loadState};
item.setProperty("name", {"quick/slot ", 1 + index});
item.setProperty("title", {"Slot ", 1 + index});
item.setText({"Slot ", 1 + index});
item.onActivate([=] { program->loadState({"quick/slot ", 1 + index}); });
}
loadState.append(MenuSeparator());
loadState.append(MenuItem().setIcon(Icon::Edit::Undo).setText("Undo Last Save").onActivate([&] {
loadState.append(MenuItem()
.setProperty("name", "quick/undo")
.setProperty("title", "Undo Last Save")
.setIcon(Icon::Edit::Undo).setText("Undo Last Save").onActivate([&] {
program->loadState("quick/undo");
}));
loadState.append(MenuItem().setIcon(Icon::Edit::Redo).setText("Redo Last Undo").onActivate([&] {
loadState.append(MenuItem()
.setProperty("name", "quick/redo")
.setProperty("title", "Redo Last Undo")
.setIcon(Icon::Edit::Redo).setText("Redo Last Undo").onActivate([&] {
program->loadState("quick/redo");
}));
loadState.append(MenuItem().setIcon(Icon::Edit::Clear).setText("Remove All States").onActivate([&] {
if(MessageDialog("Are you sure you want to permanently remove all quick states for this game?").setParent(*this).question() == "Yes") {
for(uint index : range(QuickStates)) program->removeState({"quick/slot ", 1 + index});
program->removeState("quick/undo");
program->removeState("quick/redo");
updateStateMenus();
}
}));
speedMenu.setIcon(Icon::Device::Clock).setText("Speed");
speedSlowest.setText("50% (Slowest)").setProperty("multiplier", "2.0").onActivate([&] { program->updateAudioFrequency(); });
speedSlow.setText("75% (Slow)").setProperty("multiplier", "1.333").onActivate([&] { program->updateAudioFrequency(); });
@ -230,13 +241,13 @@ auto Presentation::clearViewport() -> void {
uint length;
uint width = 16;
uint height = 16;
if(video->lock(output, length, width, height)) {
if(video->acquire(output, length, width, height)) {
for(uint y : range(height)) {
auto line = output + y * (length >> 2);
for(uint x : range(width)) *line++ = 0xff000000;
}
if(!emulator->loaded()) drawIcon(output, length, width, height);
video->unlock();
video->release();
video->output();
}
}
@ -338,6 +349,7 @@ auto Presentation::updateDeviceMenu() -> void {
controllerPort2.reset();
expansionPort.reset();
auto information = emulator->information();
for(auto& port : emulator->ports()) {
Menu* menu = nullptr;
if(port.name == "Controller Port 1") menu = &controllerPort1;
@ -345,7 +357,7 @@ auto Presentation::updateDeviceMenu() -> void {
if(port.name == "Expansion Port") menu = &expansionPort;
if(!menu) continue;
auto path = string{"Emulator/", port.name}.replace(" ", "");
auto path = string{information.name, "/", port.name}.replace(" ", "");
auto deviceName = settings(path).text();
auto deviceID = emulator->connected(port.id);
@ -427,6 +439,38 @@ auto Presentation::updateSizeMenu() -> void {
}));
}
auto Presentation::updateStateMenus() -> void {
auto states = program->availableStates("quick/");
for(auto& action : saveState.actions()) {
if(auto item = action.cast<MenuItem>()) {
if(auto name = item.property("name")) {
if(states.find(name)) {
auto timestamp = program->stateTimestamp(item.property("name"));
item.setText({item.property("title"), " [", chrono::local::datetime(timestamp), "]"});
} else {
item.setText({item.property("title"), " [Empty]"});
}
}
}
}
for(auto& action : loadState.actions()) {
if(auto item = action.cast<MenuItem>()) {
if(auto name = item.property("name")) {
if(states.find(name)) {
auto timestamp = program->stateTimestamp(item.property("name"));
item.setEnabled(true);
item.setText({item.property("title"), " [", chrono::local::datetime(timestamp), "]"});
} else {
item.setEnabled(false);
item.setText({item.property("title"), " [Empty]"});
}
}
}
}
}
auto Presentation::updateRecentGames() -> void {
loadRecentGame.reset();
@ -469,7 +513,7 @@ auto Presentation::updateRecentGames() -> void {
program->load();
});
} else {
item.setText(tr("Empty"));
item.setText({"[", tr("Empty"), "]"});
item.setEnabled(false);
}
loadRecentGame.append(item);

View File

@ -36,6 +36,7 @@ struct Presentation : Window {
auto updateDeviceMenu() -> void;
auto updateDeviceSelections() -> void;
auto updateSizeMenu() -> void;
auto updateStateMenus() -> void;
auto clearRecentGames() -> void;
auto updateRecentGames() -> void;
auto addRecentGame(string location) -> void;
@ -67,8 +68,6 @@ struct Presentation : Window {
MenuCheckItem blurEmulation{&outputMenu};
Menu shaderMenu{&settingsMenu};
MenuSeparator settingsSeparatorA{&settingsMenu};
MenuCheckItem synchronizeVideo{&settingsMenu};
MenuCheckItem synchronizeAudio{&settingsMenu};
MenuCheckItem muteAudio{&settingsMenu};
MenuCheckItem showStatusBar{&settingsMenu};
MenuSeparator settingsSeparatorB{&settingsMenu};
@ -77,7 +76,8 @@ struct Presentation : Window {
MenuItem inputSettings{&settingsMenu};
MenuItem hotkeySettings{&settingsMenu};
MenuItem pathSettings{&settingsMenu};
MenuItem advancedSettings{&settingsMenu};
MenuItem configurationSettings{&settingsMenu};
MenuItem driverSettings{&settingsMenu};
Menu toolsMenu{&menuBar};
Menu saveState{&toolsMenu};
Menu loadState{&toolsMenu};

View File

@ -1,56 +1,56 @@
auto Program::updateAudioDriver() -> void {
auto Program::updateAudioDriver(Window parent) -> void {
auto changed = (bool)audio;
audio = Audio::create(settings["Audio/Driver"].text());
audio->setContext(presentation->viewport.handle());
audio->setChannels(2);
if(changed) {
settings["Audio/Device"].setValue(audio->defaultDevice());
settings["Audio/Frequency"].setValue(audio->defaultFrequency());
settings["Audio/Latency"].setValue(audio->defaultLatency());
}
updateAudioExclusive();
updateAudioDevice();
updateAudioBlocking();
settingsWindow->advanced.updateAudioDriver();
updateAudioDynamic();
if(!audio->ready()) {
MessageDialog({
"Error: failed to initialize [", settings["Audio/Driver"].text(), "] audio driver."
}).error();
}).setParent(parent).error();
settings["Audio/Driver"].setValue("None");
return updateAudioDriver();
return updateAudioDriver(parent);
}
}
auto Program::updateAudioExclusive() -> void {
if(!audio) return;
if(audio->hasExclusive()) {
audio->setExclusive(settings["Audio/Exclusive"].boolean());
settingsWindow->audio.exclusiveMode.setEnabled(true).setChecked(settings["Audio/Exclusive"].boolean());
audio->clear();
updateAudioFrequency();
updateAudioLatency();
} else {
settingsWindow->audio.exclusiveMode.setEnabled(false).setChecked(false);
}
}
auto Program::updateAudioDevice() -> void {
if(!audio) return;
audio->clear();
if(!audio->availableDevices().find(settings["Audio/Device"].text())) {
settings["Audio/Device"].setValue(audio->availableDevices()(0));
settings["Audio/Device"].setValue(audio->defaultDevice());
}
audio->setDevice(settings["Audio/Device"].text());
updateAudioFrequency();
updateAudioLatency();
settingsWindow->audio.updateDevice();
}
auto Program::updateAudioBlocking() -> void {
audio->clear();
audio->setBlocking(settings["Audio/Blocking"].boolean());
}
auto Program::updateAudioDynamic() -> void {
audio->setDynamic(settings["Audio/Dynamic"].boolean());
}
auto Program::updateAudioFrequency() -> void {
if(!audio) return;
audio->clear();
if(!audio->availableFrequencies().find(settings["Audio/Frequency"].real())) {
settings["Audio/Frequency"].setValue(audio->availableFrequencies()(0));
settings["Audio/Frequency"].setValue(audio->defaultFrequency());
}
audio->setFrequency(settings["Audio/Frequency"].real());
double frequency = settings["Audio/Frequency"].real() + settings["Audio/Skew"].integer();
@ -58,17 +58,14 @@ auto Program::updateAudioFrequency() -> void {
if(item.checked()) frequency *= item.property("multiplier").real();
}
Emulator::audio.setFrequency(frequency);
settingsWindow->audio.updateFrequency();
}
auto Program::updateAudioLatency() -> void {
if(!audio) return;
audio->clear();
if(!audio->availableLatencies().find(settings["Audio/Latency"].natural())) {
settings["Audio/Latency"].setValue(audio->availableLatencies()(0));
settings["Audio/Latency"].setValue(audio->defaultLatency());
}
audio->setLatency(settings["Audio/Latency"].natural());
settingsWindow->audio.updateLatency();
}
auto Program::updateAudioEffects() -> void {

View File

@ -3,14 +3,14 @@ auto Program::load() -> void {
if(auto configuration = string::read(locate("configuration.bml"))) {
emulator->configure(configuration);
settingsWindow->advanced.updateConfiguration();
settingsWindow->configuration.updateConfiguration();
}
if(!emulator->load()) return;
gameQueue = {};
screenshot = {};
frameAdvance = false;
if(!verified() && settingsWindow->advanced.warnOnUnverifiedGames.checked()) {
if(!verified() && settingsWindow->configuration.warnOnUnverifiedGames.checked()) {
auto response = MessageDialog(
"Warning: this game image is unverified.\n"
"Running it *may* be a security risk.\n\n"
@ -21,12 +21,12 @@ auto Program::load() -> void {
return showMessage("Game loading cancelled");
}
if(response == "Always") {
settingsWindow->advanced.warnOnUnverifiedGames.setChecked(false).doToggle();
settingsWindow->configuration.warnOnUnverifiedGames.setChecked(false).doToggle();
}
}
hackCompatibility();
emulator->power();
if(settingsWindow->advanced.autoLoadStateOnLoad.checked()) {
if(settingsWindow->configuration.autoLoadStateOnLoad.checked()) {
program->loadState("quick/undo");
}
showMessage({
@ -37,6 +37,7 @@ auto Program::load() -> void {
presentation->resetSystem.setEnabled(true);
presentation->unloadGame.setEnabled(true);
presentation->toolsMenu.setVisible(true);
presentation->updateStateMenus();
presentation->speedNormal.setChecked();
presentation->pauseEmulation.setChecked(false);
presentation->updateStatusIcon();
@ -284,7 +285,7 @@ auto Program::unload() -> void {
if(!emulator->loaded()) return;
toolsWindow->cheatEditor.saveCheats();
toolsWindow->setVisible(false);
if(settingsWindow->advanced.autoSaveStateOnUnload.checked()) {
if(settingsWindow->configuration.autoSaveStateOnUnload.checked()) {
saveUndoState();
}
if(auto configuration = emulator->configuration()) {

View File

@ -1,8 +1,8 @@
auto Program::hackCompatibility() -> void {
bool fastPPU = settingsWindow->advanced.fastPPUOption.checked();
bool fastPPUNoSpriteLimit = settingsWindow->advanced.noSpriteLimit.checked();
bool fastPPUHiresMode7 = settingsWindow->advanced.hiresMode7.checked();
bool fastDSP = settingsWindow->advanced.fastDSPOption.checked();
bool fastPPU = settingsWindow->configuration.fastPPUOption.checked();
bool fastPPUNoSpriteLimit = settingsWindow->configuration.noSpriteLimit.checked();
bool fastPPUHiresMode7 = settingsWindow->configuration.hiresMode7.checked();
bool fastDSP = settingsWindow->configuration.fastDSPOption.checked();
auto label = superFamicom.label;
if(label == "AIR STRIKE PATROL" || label == "DESERT FIGHTER") fastPPU = false;
@ -33,7 +33,7 @@ auto Program::hackPatchMemory(vector<uint8_t>& data) -> void {
auto Program::hackOverclockSuperFX() -> void {
//todo: implement a better way of detecting SuperFX games
//todo: apply multiplier changes on reset, not just on game load?
double multiplier = settingsWindow->advanced.superFXValue.text().natural() / 100.0;
double multiplier = settingsWindow->configuration.superFXValue.text().natural() / 100.0;
if(multiplier == 1.0) return;
auto label = superFamicom.label;

View File

@ -1,17 +1,19 @@
auto Program::updateInputDriver() -> void {
auto Program::updateInputDriver(Window parent) -> void {
auto changed = (bool)input;
input = Input::create(settings["Input/Driver"].text());
input->setContext(presentation->viewport.handle());
if(changed) {
}
inputManager->initialize();
settingsWindow->input.reloadPorts();
settingsWindow->hotkeys.reloadMappings();
settingsWindow->advanced.updateInputDriver();
if(!input->ready()) {
MessageDialog({
"Error: failed to initialize [", settings["Input/Driver"].text(), "] input driver."
}).error();
}).setParent(parent).error();
settings["Input/Driver"].setValue("None");
return updateInputDriver();
return updateInputDriver(parent);
}
}

View File

@ -210,17 +210,19 @@ auto Program::videoRefresh(uint display, const uint32* data, uint pitch, uint wi
screenshot.width = width;
screenshot.height = height;
if(video->lock(output, length, width, height)) {
if(video->acquire(output, length, width, height)) {
length >>= 2;
for(auto y : range(height)) {
memory::copy<uint32>(output + y * length, data + y * pitch, width);
}
video->unlock();
video->release();
video->output();
}
inputManager->frame();
if(frameAdvance) {
frameAdvance = false;
presentation->pauseEmulation.setChecked();
@ -243,7 +245,7 @@ auto Program::audioSample(const double* samples, uint channels) -> void {
}
auto Program::inputPoll(uint port, uint device, uint input) -> int16 {
if(focused() || settingsWindow->input.allowInput().checked()) {
if(focused() || settingsWindow->configuration.allowInput().checked()) {
inputManager->poll();
if(auto mapping = inputManager->mapping(port, device, input)) {
return mapping->poll();
@ -253,7 +255,7 @@ auto Program::inputPoll(uint port, uint device, uint input) -> int16 {
}
auto Program::inputRumble(uint port, uint device, uint input, bool enable) -> void {
if(focused() || settingsWindow->input.allowInput().checked() || !enable) {
if(focused() || settingsWindow->configuration.allowInput().checked() || !enable) {
if(auto mapping = inputManager->mapping(port, device, input)) {
return mapping->rumble(enable);
}

View File

@ -40,12 +40,16 @@ Program::Program(vector<string> arguments) {
settings["Crashed"].setValue(true);
settings.save();
updateVideoDriver();
updateAudioDriver();
updateInputDriver();
updateVideoDriver(*presentation);
updateAudioDriver(*presentation);
updateInputDriver(*presentation);
settings["Crashed"].setValue(false);
settings.save();
settingsWindow->drivers.videoDriverChanged();
settingsWindow->drivers.audioDriverChanged();
settingsWindow->drivers.inputDriverChanged();
arguments.takeLeft(); //ignore program location in argument parsing
for(auto& argument : arguments) {
if(argument == "--fullscreen") {
@ -74,7 +78,7 @@ auto Program::main() -> void {
}
emulator->run();
if(settingsWindow->advanced.autoSaveMemory.checked()) {
if(settingsWindow->configuration.autoSaveMemory.checked()) {
auto currentTime = chrono::timestamp();
if(currentTime - autoSaveTime >= settings["Emulator/AutoSaveMemory/Interval"].natural()) {
autoSaveTime = currentTime;

View File

@ -49,7 +49,8 @@ struct Program : Emulator::Platform {
auto screenshotPath() -> string;
//states.cpp
auto managedStates() -> vector<string>;
auto availableStates(string type) -> vector<string>;
auto stateTimestamp(string filename) -> uint64_t;
auto loadState(string filename) -> bool;
auto saveState(string filename) -> bool;
auto saveUndoState() -> bool;
@ -58,23 +59,26 @@ struct Program : Emulator::Platform {
auto renameState(string from, string to) -> bool;
//video.cpp
auto updateVideoDriver(Window parent) -> void;
auto updateVideoExclusive() -> void;
auto updateVideoDriver() -> void;
auto updateVideoBlocking() -> void;
auto updateVideoFlush() -> void;
auto updateVideoFormat() -> void;
auto updateVideoShader() -> void;
auto updateVideoPalette() -> void;
//audio.cpp
auto updateAudioDriver() -> void;
auto updateAudioDriver(Window parent) -> void;
auto updateAudioExclusive() -> void;
auto updateAudioDevice() -> void;
auto updateAudioBlocking() -> void;
auto updateAudioDynamic() -> void;
auto updateAudioFrequency() -> void;
auto updateAudioLatency() -> void;
auto updateAudioEffects() -> void;
//input.cpp
auto updateInputDriver() -> void;
auto updateInputDriver(Window parent) -> void;
//utility.cpp
auto showMessage(string text) -> void;

View File

@ -1,21 +1,45 @@
auto Program::managedStates() -> vector<string> {
if(!emulator->loaded()) return {};
auto Program::availableStates(string type) -> vector<string> {
vector<string> result;
if(!emulator->loaded()) return result;
if(gamePath().endsWith("/")) {
return directory::ifiles({statePath(), "managed/"}, "*.bst");
for(auto& file : directory::ifiles({statePath(), type}, "*.bst")) {
result.append({type, file.trimRight(".bst", 1L)});
}
} else {
Decode::ZIP input;
if(input.open(statePath())) {
vector<string> filenames;
for(auto& file : input.file) {
if(file.name.match("managed/*.bst")) filenames.append(file.name.trimLeft("managed/", 1L));
if(file.name.match({type, "*.bst"})) result.append(file.name.trimRight(".bst", 1L));
}
filenames.isort();
return filenames;
}
}
return {};
result.isort();
return result;
}
auto Program::stateTimestamp(string filename) -> uint64_t {
auto timestamp = chrono::timestamp();
if(!emulator->loaded()) return timestamp;
if(gamePath().endsWith("/")) {
string location = {statePath(), filename, ".bst"};
timestamp = file::timestamp(location, file::time::modify);
} else {
string location = {filename, ".bst"};
Decode::ZIP input;
if(input.open(statePath())) {
for(auto& file : input.file) {
if(file.name != location) continue;
timestamp = file.timestamp;
break;
}
}
}
return timestamp;
}
auto Program::loadState(string filename) -> bool {
@ -26,9 +50,6 @@ auto Program::loadState(string filename) -> bool {
if(gamePath().endsWith("/")) {
string location = {statePath(), filename, ".bst"};
if(!file::exists(location)) return showMessage({"[", prefix, "] not found"}), false;
if(filename != "quick/undo") saveUndoState();
if(filename == "quick/undo") saveRedoState();
memory = file::read(location);
} else {
string location = {filename, ".bst"};
@ -43,6 +64,8 @@ auto Program::loadState(string filename) -> bool {
}
if(memory) {
if(filename != "quick/undo") saveUndoState();
if(filename == "quick/undo") saveRedoState();
serializer s{memory.data(), memory.size()};
if(!emulator->unserialize(s)) return showMessage({"[", prefix, "] is in incompatible format"}), false;
return showMessage({"Loaded [", prefix, "]"}), true;
@ -65,7 +88,7 @@ auto Program::saveState(string filename) -> bool {
} else {
string location = {filename, ".bst"};
struct State { string name; vector<uint8_t> memory; };
struct State { string name; time_t timestamp; vector<uint8_t> memory; };
vector<State> states;
Decode::ZIP input;
@ -73,18 +96,19 @@ auto Program::saveState(string filename) -> bool {
for(auto& file : input.file) {
if(!file.name.endsWith(".bst")) continue;
if(file.name == location) continue;
states.append({file.name, input.extract(file)});
states.append({file.name, file.timestamp, input.extract(file)});
}
}
input.close();
Encode::ZIP output{statePath()};
for(auto& state : states) {
output.append(state.name, state.memory.data(), state.memory.size());
output.append(state.name, state.memory.data(), state.memory.size(), state.timestamp);
}
output.append(location, s.data(), s.size());
}
if(filename.beginsWith("quick/")) presentation->updateStateMenus();
return showMessage({"Saved [", prefix, "]"}), true;
}
@ -116,14 +140,14 @@ auto Program::removeState(string filename) -> bool {
bool found = false;
string location = {filename, ".bst"};
struct State { string name; vector<uint8_t> memory; };
struct State { string name; time_t timestamp; vector<uint8_t> memory; };
vector<State> states;
Decode::ZIP input;
if(input.open(statePath())) {
for(auto& file : input.file) {
if(file.name == location) { found = true; continue; }
states.append({file.name, input.extract(file)});
states.append({file.name, file.timestamp, input.extract(file)});
}
}
input.close();
@ -131,7 +155,7 @@ auto Program::removeState(string filename) -> bool {
if(states) {
Encode::ZIP output{statePath()};
for(auto& state : states) {
output.append(state.name, state.memory.data(), state.memory.size());
output.append(state.name, state.memory.data(), state.memory.size(), state.timestamp);
}
} else {
//remove .bsz file if there are no states left in the archive
@ -154,21 +178,21 @@ auto Program::renameState(string from, string to) -> bool {
from = {from, ".bst"};
to = {to, ".bst"};
struct State { string name; vector<uint8_t> memory; };
struct State { string name; time_t timestamp; vector<uint8_t> memory; };
vector<State> states;
Decode::ZIP input;
if(input.open(statePath())) {
for(auto& file : input.file) {
if(file.name == from) { found = true; file.name = to; }
states.append({file.name, input.extract(file)});
states.append({file.name, file.timestamp, input.extract(file)});
}
}
input.close();
Encode::ZIP output{statePath()};
for(auto& state : states) {
output.append(state.name, state.memory.data(), state.memory.size());
output.append(state.name, state.memory.data(), state.memory.size(), state.timestamp);
}
return found;

View File

@ -21,7 +21,7 @@ auto Program::updateStatus() -> void {
frameRate = tr("Unloaded");
} else if(presentation->pauseEmulation.checked()) {
frameRate = tr("Paused");
} else if(!focused() && settingsWindow->input.pauseEmulation.checked()) {
} else if(!focused() && settingsWindow->configuration.pauseEmulation.checked()) {
frameRate = tr("Paused");
} else {
frameRate = statusFrameRate;
@ -48,7 +48,7 @@ auto Program::captureScreenshot() -> bool {
auto Program::paused() -> bool {
if(!emulator->loaded()) return true;
if(presentation->pauseEmulation.checked()) return true;
if(!focused() && settingsWindow->input.pauseEmulation.checked()) return true;
if(!focused() && settingsWindow->configuration.pauseEmulation.checked()) return true;
return false;
}

View File

@ -1,8 +1,14 @@
auto Program::updateVideoDriver() -> void {
auto Program::updateVideoDriver(Window parent) -> void {
auto changed = (bool)video;
video = Video::create(settings["Video/Driver"].text());
video->setContext(presentation->viewport.handle());
if(changed) {
settings["Video/Format"].setValue(video->defaultFormat());
}
updateVideoExclusive();
updateVideoBlocking();
updateVideoFlush();
updateVideoFormat();
updateVideoShader();
if(video->ready()) {
@ -14,32 +20,37 @@ auto Program::updateVideoDriver() -> void {
if(!emulator->loaded()) presentation->clearViewport();
});
settingsWindow->advanced.updateVideoDriver();
if(!video->ready()) {
MessageDialog({
"Error: failed to initialize [", settings["Video/Driver"].text(), "] video driver."
}).error();
}).setParent(parent).error();
settings["Video/Driver"].setValue("None");
return updateVideoDriver();
return updateVideoDriver(parent);
}
presentation->updateShaders();
}
auto Program::updateVideoExclusive() -> void {
//only enabled in fullscreen mode via Presentation::toggleFullScreen()
video->setExclusive(false);
if(video->hasExclusive()) {
settingsWindow->video.exclusiveMode.setEnabled(true).setChecked(settings["Video/Exclusive"].boolean());
} else {
settingsWindow->video.exclusiveMode.setEnabled(false).setChecked(false);
}
}
auto Program::updateVideoBlocking() -> void {
video->setBlocking(settings["Video/Blocking"].boolean());
}
auto Program::updateVideoFlush() -> void {
video->setFlush(settings["Video/Flush"].boolean());
}
auto Program::updateVideoFormat() -> void {
if(!video->availableFormats().find(settings["Video/Format"].text())) {
settings["Video/Format"].setValue(video->defaultFormat());
}
video->setFormat(settings["Video/Format"].text());
}
auto Program::updateVideoShader() -> void {
if(settings["Video/Driver"].text() == "OpenGL"
&& settings["Video/Shader"].text() != "None"

View File

@ -1,148 +0,0 @@
AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
setIcon(Icon::Action::Settings);
setText("Advanced");
layout.setPadding(5);
driversLabel.setText("Drivers").setFont(Font().setBold());
videoDriverLabel.setText("Video:");
videoDriverOption.onChange([&] {
auto item = videoDriverOption.selected();
settings["Video/Driver"].setValue(item.text());
if(!emulator->loaded() || item.text() == "None" || MessageDialog(
"Warning: incompatible drivers may cause bsnes to crash.\n"
"It is highly recommended you unload your game first to avoid data loss.\n"
"Do you wish to proceed with the video driver change now anyway?"
).setParent(*settingsWindow).question() == "Yes") {
program->save();
program->saveUndoState();
settings["Crashed"].setValue(true);
settings.save();
program->updateVideoDriver();
settings["Crashed"].setValue(false);
settings.save();
}
});
audioDriverLabel.setText("Audio:");
audioDriverOption.onChange([&] {
auto item = audioDriverOption.selected();
settings["Audio/Driver"].setValue(item.text());
if(!emulator->loaded() || item.text() == "None" || MessageDialog(
"Warning: incompatible drivers may cause bsnes to crash.\n"
"It is highly recommended you unload your game first to avoid data loss.\n"
"Do you wish to proceed with the audio driver change now anyway?"
).setParent(*settingsWindow).question() == "Yes") {
program->save();
program->saveUndoState();
settings["Crashed"].setValue(true);
settings.save();
program->updateAudioDriver();
settings["Crashed"].setValue(false);
settings.save();
}
});
inputDriverLabel.setText("Input:");
inputDriverOption.onChange([&] {
auto item = inputDriverOption.selected();
settings["Input/Driver"].setValue(item.text());
if(!emulator->loaded() || item.text() == "None" || MessageDialog(
"Warning: incompatible drivers may cause bsnes to crash.\n"
"It is highly recommended you unload your game first to avoid data loss.\n"
"Do you wish to proceed with the input driver change now anyway?"
).setParent(*settingsWindow).question() == "Yes") {
program->save();
program->saveUndoState();
settings["Crashed"].setValue(true);
settings.save();
program->updateInputDriver();
settings["Crashed"].setValue(false);
settings.save();
}
});
optionsLabel.setText("Options").setFont(Font().setBold());
warnOnUnverifiedGames.setText("Warn when loading games that have not been verified").setChecked(settings["Emulator/WarnOnUnverifiedGames"].boolean()).onToggle([&] {
settings["Emulator/WarnOnUnverifiedGames"].setValue(warnOnUnverifiedGames.checked());
});
autoSaveMemory.setText("Auto-save memory periodically").setChecked(settings["Emulator/AutoSaveMemory/Enable"].boolean()).onToggle([&] {
settings["Emulator/AutoSaveMemory/Enable"].setValue(autoSaveMemory.checked());
});
autoSaveStateOnUnload.setText("Auto-save undo state when unloading games").setChecked(settings["Emulator/AutoSaveStateOnUnload"].boolean()).onToggle([&] {
settings["Emulator/AutoSaveStateOnUnload"].setValue(autoSaveStateOnUnload.checked());
if(!autoSaveStateOnUnload.checked()) {
autoLoadStateOnLoad.setEnabled(false).setChecked(false).doToggle();
} else {
autoLoadStateOnLoad.setEnabled(true);
}
}).doToggle();
autoLoadStateOnLoad.setText("Auto-resume on load").setChecked(settings["Emulator/AutoLoadStateOnLoad"].boolean()).onToggle([&] {
settings["Emulator/AutoLoadStateOnLoad"].setValue(autoLoadStateOnLoad.checked());
});
hacksLabel.setText("Emulator Hacks").setFont(Font().setBold());
fastPPUOption.setText("Fast PPU").setChecked(settings["Emulator/Hack/FastPPU"].boolean()).onToggle([&] {
settings["Emulator/Hack/FastPPU"].setValue(fastPPUOption.checked());
if(!fastPPUOption.checked()) {
noSpriteLimit.setEnabled(false).setChecked(false).doToggle();
hiresMode7.setEnabled(false).setChecked(false).doToggle();
} else {
noSpriteLimit.setEnabled(true);
hiresMode7.setEnabled(true);
}
}).doToggle();
noSpriteLimit.setText("No sprite limit").setChecked(settings["Emulator/Hack/FastPPU/NoSpriteLimit"].boolean()).onToggle([&] {
settings["Emulator/Hack/FastPPU/NoSpriteLimit"].setValue(noSpriteLimit.checked());
});
hiresMode7.setText("Hires mode 7").setChecked(settings["Emulator/Hack/FastPPU/HiresMode7"].boolean()).onToggle([&] {
settings["Emulator/Hack/FastPPU/HiresMode7"].setValue(hiresMode7.checked());
});
fastDSPOption.setText("Fast DSP").setChecked(settings["Emulator/Hack/FastDSP"].boolean()).onToggle([&] {
settings["Emulator/Hack/FastDSP"].setValue(fastDSPOption.checked());
});
superFXLabel.setText("SuperFX clock speed:");
superFXValue.setAlignment(0.5);
superFXClock.setLength(71).setPosition((settings["Emulator/Hack/FastSuperFX"].natural() - 100) / 10).onChange([&] {
settings["Emulator/Hack/FastSuperFX"].setValue({superFXClock.position() * 10 + 100, "%"});
superFXValue.setText(settings["Emulator/Hack/FastSuperFX"].text());
}).doChange();
hacksNote.setForegroundColor({224, 0, 0}).setText("Note: some hack setting changes do not take effect until after reloading games.");
}
auto AdvancedSettings::updateVideoDriver() -> void {
videoDriverOption.reset();
for(auto& driver : Video::availableDrivers()) {
ComboButtonItem item;
item.setText(driver);
videoDriverOption.append(item);
if(video && video->driver() == driver) item.setSelected();
}
}
auto AdvancedSettings::updateAudioDriver() -> void {
audioDriverOption.reset();
for(auto& driver : Audio::availableDrivers()) {
ComboButtonItem item;
item.setText(driver);
audioDriverOption.append(item);
if(audio && audio->driver() == driver) item.setSelected();
}
}
auto AdvancedSettings::updateInputDriver() -> void {
inputDriverOption.reset();
for(auto& driver : Input::availableDrivers()) {
ComboButtonItem item;
item.setText(driver);
inputDriverOption.append(item);
if(input && input->driver() == driver) item.setSelected();
}
}
auto AdvancedSettings::updateConfiguration() -> void {
emulator->configure("hacks/ppuFast/enable", fastPPUOption.checked());
emulator->configure("hacks/ppuFast/noSpriteLimit", noSpriteLimit.checked());
emulator->configure("hacks/ppuFast/hiresMode7", hiresMode7.checked());
emulator->configure("hacks/dspFast/enable", fastDSPOption.checked());
}

View File

@ -4,27 +4,6 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
layout.setPadding(5);
driverLabel.setFont(Font().setBold()).setText("Driver");
deviceLabel.setText("Device:");
deviceList.onChange([&] {
settings["Audio/Device"].setValue(deviceList.selected().text());
program->updateAudioDevice();
});
frequencyLabel.setText("Frequency:");
frequencyList.onChange([&] {
settings["Audio/Frequency"].setValue(frequencyList.selected().text());
program->updateAudioFrequency();
});
latencyLabel.setText("Latency:");
latencyList.onChange([&] {
settings["Audio/Latency"].setValue(latencyList.selected().text());
program->updateAudioLatency();
});
exclusiveMode.setText("Exclusive mode").setChecked(settings["Audio/Exclusive"].boolean()).onToggle([&] {
settings["Audio/Exclusive"].setValue(exclusiveMode.checked());
program->updateAudioExclusive();
});
effectsLabel.setFont(Font().setBold()).setText("Effects");
effectsLayout.setSize({3, 3});
effectsLayout.column(0).setAlignment(1.0);
@ -34,7 +13,7 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
string value = {skewSlider.position() > 5000 ? "+" : "", (int)skewSlider.position() - 5000};
settings["Audio/Skew"].setValue(value);
skewValue.setText(value);
program->updateAudioFrequency();
if(audio) program->updateAudioFrequency();
}).doChange();
volumeLabel.setText("Volume:");
volumeValue.setAlignment(0.5);
@ -42,7 +21,7 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
string value = {volumeSlider.position(), "%"};
settings["Audio/Volume"].setValue(value);
volumeValue.setText(value);
program->updateAudioEffects();
if(audio) program->updateAudioEffects();
}).doChange();
balanceLabel.setText("Balance:");
balanceValue.setAlignment(0.5);
@ -50,36 +29,6 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
string value = {balanceSlider.position(), "%"};
settings["Audio/Balance"].setValue(value);
balanceValue.setText(value);
program->updateAudioEffects();
if(audio) program->updateAudioEffects();
}).doChange();
}
auto AudioSettings::updateDevice() -> void {
deviceList.reset();
for(auto& device : audio->availableDevices()) {
deviceList.append(ComboButtonItem().setText(device));
if(device == settings["Audio/Device"].text()) {
deviceList.items().right().setSelected();
}
}
}
auto AudioSettings::updateFrequency() -> void {
frequencyList.reset();
for(auto& frequency : audio->availableFrequencies()) {
frequencyList.append(ComboButtonItem().setText((uint)frequency));
if(frequency == settings["Audio/Frequency"].real()) {
frequencyList.items().right().setSelected();
}
}
}
auto AudioSettings::updateLatency() -> void {
latencyList.reset();
for(auto& latency : audio->availableLatencies()) {
latencyList.append(ComboButtonItem().setText(latency));
if(latency == settings["Audio/Latency"].natural()) {
latencyList.items().right().setSelected();
}
}
}

View File

@ -0,0 +1,77 @@
ConfigurationSettings::ConfigurationSettings(TabFrame* parent) : TabFrameItem(parent) {
setIcon(Icon::Action::Settings);
setText("Configuration");
layout.setPadding(5);
optionsLabel.setText("Options").setFont(Font().setBold());
inputFocusLabel.setText("When focus is lost:");
pauseEmulation.setText("Pause emulation").onActivate([&] {
settings["Input/Defocus"].setValue("Pause");
});
blockInput.setText("Block input").onActivate([&] {
settings["Input/Defocus"].setValue("Block");
});
allowInput.setText("Allow input").onActivate([&] {
settings["Input/Defocus"].setValue("Allow");
});
if(settings["Input/Defocus"].text() == "Pause") pauseEmulation.setChecked();
if(settings["Input/Defocus"].text() == "Block") blockInput.setChecked();
if(settings["Input/Defocus"].text() == "Allow") allowInput.setChecked();
warnOnUnverifiedGames.setText("Warn when loading games that have not been verified").setChecked(settings["Emulator/WarnOnUnverifiedGames"].boolean()).onToggle([&] {
settings["Emulator/WarnOnUnverifiedGames"].setValue(warnOnUnverifiedGames.checked());
});
autoSaveMemory.setText("Auto-save memory periodically").setChecked(settings["Emulator/AutoSaveMemory/Enable"].boolean()).onToggle([&] {
settings["Emulator/AutoSaveMemory/Enable"].setValue(autoSaveMemory.checked());
});
autoSaveStateOnUnload.setText("Auto-save undo state when unloading games").setChecked(settings["Emulator/AutoSaveStateOnUnload"].boolean()).onToggle([&] {
settings["Emulator/AutoSaveStateOnUnload"].setValue(autoSaveStateOnUnload.checked());
if(!autoSaveStateOnUnload.checked()) {
autoLoadStateOnLoad.setEnabled(false).setChecked(false).doToggle();
} else {
autoLoadStateOnLoad.setEnabled(true);
}
}).doToggle();
autoLoadStateOnLoad.setText("Auto-resume on load").setChecked(settings["Emulator/AutoLoadStateOnLoad"].boolean()).onToggle([&] {
settings["Emulator/AutoLoadStateOnLoad"].setValue(autoLoadStateOnLoad.checked());
});
suppressScreenSaver.setText("Suppress screen saver").setChecked(settings["UserInterface/SuppressScreenSaver"].boolean()).onToggle([&] {
settings["UserInterface/SuppressScreenSaver"].setValue(suppressScreenSaver.checked());
Application::setScreenSaver(!suppressScreenSaver.checked());
});
hacksLabel.setText("Emulator Hacks").setFont(Font().setBold());
fastPPUOption.setText("Fast PPU").setChecked(settings["Emulator/Hack/FastPPU"].boolean()).onToggle([&] {
settings["Emulator/Hack/FastPPU"].setValue(fastPPUOption.checked());
if(!fastPPUOption.checked()) {
noSpriteLimit.setEnabled(false).setChecked(false).doToggle();
hiresMode7.setEnabled(false).setChecked(false).doToggle();
} else {
noSpriteLimit.setEnabled(true);
hiresMode7.setEnabled(true);
}
}).doToggle();
noSpriteLimit.setText("No sprite limit").setChecked(settings["Emulator/Hack/FastPPU/NoSpriteLimit"].boolean()).onToggle([&] {
settings["Emulator/Hack/FastPPU/NoSpriteLimit"].setValue(noSpriteLimit.checked());
});
hiresMode7.setText("Hires mode 7").setChecked(settings["Emulator/Hack/FastPPU/HiresMode7"].boolean()).onToggle([&] {
settings["Emulator/Hack/FastPPU/HiresMode7"].setValue(hiresMode7.checked());
});
fastDSPOption.setText("Fast DSP").setChecked(settings["Emulator/Hack/FastDSP"].boolean()).onToggle([&] {
settings["Emulator/Hack/FastDSP"].setValue(fastDSPOption.checked());
});
superFXLabel.setText("SuperFX clock speed:");
superFXValue.setAlignment(0.5);
superFXClock.setLength(71).setPosition((settings["Emulator/Hack/FastSuperFX"].natural() - 100) / 10).onChange([&] {
settings["Emulator/Hack/FastSuperFX"].setValue({superFXClock.position() * 10 + 100, "%"});
superFXValue.setText(settings["Emulator/Hack/FastSuperFX"].text());
}).doChange();
hacksNote.setForegroundColor({224, 0, 0}).setText("Note: some hack setting changes do not take effect until after reloading games.");
}
auto ConfigurationSettings::updateConfiguration() -> void {
emulator->configure("hacks/ppuFast/enable", fastPPUOption.checked());
emulator->configure("hacks/ppuFast/noSpriteLimit", noSpriteLimit.checked());
emulator->configure("hacks/ppuFast/hiresMode7", hiresMode7.checked());
emulator->configure("hacks/dspFast/enable", fastDSPOption.checked());
}

View File

@ -0,0 +1,246 @@
DriverSettings::DriverSettings(TabFrame* parent) : TabFrameItem(parent) {
setIcon(Icon::Place::Settings);
setText("Drivers");
layout.setPadding(5);
videoLabel.setText("Video").setFont(Font().setBold());
videoLayout.setSize({2, 2});
videoDriverLabel.setText("Driver:");
videoDriverOption.onChange([&] {
videoDriverUpdate.setEnabled(videoDriverOption.selected().text() != video->driver());
});
videoDriverUpdate.setText("Change").onActivate([&] { videoDriverChange(); });
videoFormatLabel.setText("Format:");
videoFormatOption.onChange([&] { videoFormatChange(); });
videoExclusiveToggle.setText("Exclusive fullscreen").onToggle([&] {
settings["Video/Exclusive"].setValue(videoExclusiveToggle.checked());
program->updateVideoExclusive();
});
videoBlockingToggle.setText("Synchronize").onToggle([&] {
settings["Video/Blocking"].setValue(videoBlockingToggle.checked());
program->updateVideoBlocking();
});
videoFlushToggle.setText("GPU sync").onToggle([&] {
settings["Video/Flush"].setValue(videoFlushToggle.checked());
program->updateVideoFlush();
});
audioLabel.setText("Audio").setFont(Font().setBold());
audioLayout.setSize({2, 2});
audioDriverLabel.setText("Driver:");
audioDriverOption.onChange([&] {
audioDriverUpdate.setEnabled(audioDriverOption.selected().text() != audio->driver());
});
audioDriverUpdate.setText("Change").onActivate([&] { audioDriverChange(); });
audioDeviceLabel.setText("Device:");
audioDeviceOption.onChange([&] { audioDeviceChange(); });
audioFrequencyLabel.setText("Frequency:");
audioFrequencyOption.onChange([&] { audioFrequencyChange(); });
audioLatencyLabel.setText("Latency:");
audioLatencyOption.onChange([&] { audioLatencyChange(); });
audioExclusiveToggle.setText("Exclusive").onToggle([&] {
settings["Audio/Exclusive"].setValue(audioExclusiveToggle.checked());
program->updateAudioExclusive();
});
audioBlockingToggle.setText("Synchronize").onToggle([&] {
settings["Audio/Blocking"].setValue(audioBlockingToggle.checked());
program->updateAudioBlocking();
});
audioDynamicToggle.setText("Dynamic rate").onToggle([&] {
settings["Audio/Dynamic"].setValue(audioDynamicToggle.checked());
program->updateAudioDynamic();
});
inputLabel.setText("Input").setFont(Font().setBold());
inputLayout.setSize({2, 1});
inputDriverLabel.setText("Driver:");
inputDriverOption.onChange([&] {
inputDriverUpdate.setEnabled(inputDriverOption.selected().text() != input->driver());
});
inputDriverUpdate.setText("Change").onActivate([&] { inputDriverChange(); });
//hide video format for simplicity, as it's not very useful just yet ...
videoLayout.setSize({2, 1});
videoLayout.remove(videoFormatLabel);
videoLayout.remove(videoPropertyLayout);
}
//
auto DriverSettings::videoDriverChanged() -> void {
videoDriverOption.reset();
for(auto& driver : video->availableDrivers()) {
ComboButtonItem item{&videoDriverOption};
item.setText(driver);
if(driver == video->driver()) item.setSelected();
}
videoDriverActive.setText({"Active driver: ", video->driver()});
videoDriverOption.doChange();
videoFormatChanged();
videoExclusiveToggle.setChecked(video->exclusive()).setEnabled(video->hasExclusive());
videoBlockingToggle.setChecked(video->blocking()).setEnabled(video->hasBlocking());
videoFlushToggle.setChecked(video->flush()).setEnabled(video->hasFlush());
layout.setGeometry(layout.geometry());
}
auto DriverSettings::videoDriverChange() -> void {
auto item = videoDriverOption.selected();
settings["Video/Driver"].setValue(item.text());
if(!emulator->loaded() || item.text() == "None" || MessageDialog(
"Warning: incompatible drivers may cause bsnes to crash.\n"
"It is highly recommended you unload your game first to be safe.\n"
"Do you wish to proceed with the video driver change now anyway?"
).setParent(*settingsWindow).question() == "Yes") {
program->save();
program->saveUndoState();
settings["Crashed"].setValue(true);
settings.save();
program->updateVideoDriver(*settingsWindow);
settings["Crashed"].setValue(false);
settings.save();
videoDriverChanged();
}
}
auto DriverSettings::videoFormatChanged() -> void {
videoFormatOption.reset();
for(auto& format : video->availableFormats()) {
ComboButtonItem item{&videoFormatOption};
item.setText(format);
if(format == video->format()) item.setSelected();
}
videoFormatOption.setEnabled(video->hasFormat());
layout.setGeometry(layout.geometry());
}
auto DriverSettings::videoFormatChange() -> void {
auto item = videoFormatOption.selected();
settings["Video/Format"].setValue(item.text());
video->setFormat(item.text());
}
//
auto DriverSettings::audioDriverChanged() -> void {
audioDriverOption.reset();
for(auto& driver : audio->availableDrivers()) {
ComboButtonItem item{&audioDriverOption};
item.setText(driver);
if(driver == audio->driver()) item.setSelected();
}
audioDriverActive.setText({"Active driver: ", audio->driver()});
audioDriverOption.doChange();
audioDeviceChanged();
audioFrequencyChanged();
audioLatencyChanged();
audioExclusiveToggle.setChecked(audio->exclusive()).setEnabled(audio->hasExclusive());
audioBlockingToggle.setChecked(audio->blocking()).setEnabled(audio->hasBlocking());
audioDynamicToggle.setChecked(audio->dynamic()).setEnabled(audio->hasDynamic());
layout.setGeometry(layout.geometry());
}
auto DriverSettings::audioDriverChange() -> void {
auto item = audioDriverOption.selected();
settings["Audio/Driver"].setValue(item.text());
if(!emulator->loaded() || item.text() == "None" || MessageDialog(
"Warning: incompatible drivers may cause bsnes to crash.\n"
"It is highly recommended you unload your game first to be safe.\n"
"Do you wish to proceed with the audio driver change now anyway?"
).setParent(*settingsWindow).question() == "Yes") {
program->save();
program->saveUndoState();
settings["Crashed"].setValue(true);
settings.save();
program->updateAudioDriver(*settingsWindow);
settings["Crashed"].setValue(false);
settings.save();
audioDriverChanged();
}
}
auto DriverSettings::audioDeviceChanged() -> void {
audioDeviceOption.reset();
for(auto& device : audio->availableDevices()) {
ComboButtonItem item{&audioDeviceOption};
item.setText(device);
if(device == audio->device()) item.setSelected();
}
audioDeviceOption.setEnabled(audio->hasDevice());
layout.setGeometry(layout.geometry());
}
auto DriverSettings::audioDeviceChange() -> void {
auto item = audioDeviceOption.selected();
settings["Audio/Device"].setValue(item.text());
program->updateAudioDevice();
audioFrequencyChanged();
audioLatencyChanged();
}
auto DriverSettings::audioFrequencyChanged() -> void {
audioFrequencyOption.reset();
for(auto& frequency : audio->availableFrequencies()) {
ComboButtonItem item{&audioFrequencyOption};
item.setText({(uint)frequency, "hz"});
if(frequency == audio->frequency()) item.setSelected();
}
audioFrequencyOption.setEnabled(audio->hasFrequency());
layout.setGeometry(layout.geometry());
}
auto DriverSettings::audioFrequencyChange() -> void {
auto item = audioFrequencyOption.selected();
settings["Audio/Frequency"].setValue(item.text());
program->updateAudioFrequency();
}
auto DriverSettings::audioLatencyChanged() -> void {
audioLatencyOption.reset();
for(auto& latency : audio->availableLatencies()) {
ComboButtonItem item{&audioLatencyOption};
item.setText(latency);
if(latency == audio->latency()) item.setSelected();
}
audioLatencyOption.setEnabled(audio->hasLatency());
layout.setGeometry(layout.geometry());
}
auto DriverSettings::audioLatencyChange() -> void {
auto item = audioLatencyOption.selected();
settings["Audio/Latency"].setValue(item.text());
program->updateAudioLatency();
}
//
auto DriverSettings::inputDriverChanged() -> void {
inputDriverOption.reset();
for(auto& driver : input->availableDrivers()) {
ComboButtonItem item{&inputDriverOption};
item.setText(driver);
if(driver == input->driver()) item.setSelected();
}
inputDriverActive.setText({"Active driver: ", input->driver()});
inputDriverOption.doChange();
layout.setGeometry(layout.geometry());
}
auto DriverSettings::inputDriverChange() -> void {
auto item = inputDriverOption.selected();
settings["Input/Driver"].setValue(item.text());
if(!emulator->loaded() || item.text() == "None" || MessageDialog(
"Warning: incompatible drivers may cause bsnes to crash.\n"
"It is highly recommended you unload your game first to be safe.\n"
"Do you wish to proceed with the input driver change now anyway?"
).setParent(*settingsWindow).question() == "Yes") {
program->save();
program->saveUndoState();
settings["Crashed"].setValue(true);
settings.save();
program->updateInputDriver(*settingsWindow);
settings["Crashed"].setValue(false);
settings.save();
inputDriverChanged();
}
}

View File

@ -54,9 +54,18 @@ auto HotkeySettings::assignMapping() -> void {
activeMapping = inputManager->hotkeys[item.offset()];
settingsWindow->layout.setEnabled(false);
settingsWindow->statusBar.setText({"Press a key or button to map [", activeMapping->name, "] ..."});
settingsWindow->setDismissable(false);
}
}
auto HotkeySettings::cancelMapping() -> void {
activeMapping.reset();
settingsWindow->statusBar.setText();
settingsWindow->layout.setEnabled();
settingsWindow->doSize();
settingsWindow->setDismissable(true);
}
auto HotkeySettings::inputEvent(shared_pointer<HID::Device> device, uint group, uint input, int16 oldValue, int16 newValue) -> void {
if(!activeMapping) return;
if(device->isMouse()) return;
@ -67,9 +76,7 @@ auto HotkeySettings::inputEvent(shared_pointer<HID::Device> device, uint group,
refreshMappings();
timer.onActivate([&] {
timer.setEnabled(false);
settingsWindow->statusBar.setText();
settingsWindow->layout.setEnabled();
settingsWindow->doSize();
cancelMapping();
}).setInterval(200).setEnabled();
}
}

View File

@ -3,23 +3,22 @@ InputSettings::InputSettings(TabFrame* parent) : TabFrameItem(parent) {
setText("Input");
layout.setPadding(5);
defocusLabel.setText("When focus is lost:");
pauseEmulation.setText("Pause emulation").onActivate([&] {
settings["Input/Defocus"].setValue("Pause");
});
blockInput.setText("Block input").onActivate([&] {
settings["Input/Defocus"].setValue("Block");
});
allowInput.setText("Allow input").onActivate([&] {
settings["Input/Defocus"].setValue("Allow");
});
if(settings["Input/Defocus"].text() == "Pause") pauseEmulation.setChecked();
if(settings["Input/Defocus"].text() == "Block") blockInput.setChecked();
if(settings["Input/Defocus"].text() == "Allow") allowInput.setChecked();
portLabel.setText("Port:");
portList.onChange([&] { reloadDevices(); });
deviceLabel.setText("Device:");
deviceList.onChange([&] { reloadMappings(); });
turboLabel.setText("Turbo rate:");
for(uint frequency : range(1, 9)) {
ComboButtonItem item{&turboList};
item.setText(frequency);
if(frequency == settings["Input/Turbo/Frequency"].natural()) item.setSelected();
}
turboList.onChange([&] {
uint frequency = turboList.selected().text().natural();
settings["Input/Turbo/Frequency"].setValue(frequency);
inputManager->turboCounter = 0;
inputManager->turboFrequency = frequency;
});
mappingList.setBatchable();
mappingList.onActivate([&] { if(assignButton.enabled()) assignButton.doActivate(); });
mappingList.onChange([&] { updateControls(); });
@ -120,9 +119,18 @@ auto InputSettings::assignMapping() -> void {
activeMapping = activeDevice().mappings[mapping.offset()];
settingsWindow->layout.setEnabled(false);
settingsWindow->statusBar.setText({"Press a key or button to map [", activeMapping->name, "] ..."});
settingsWindow->setDismissable(false);
}
}
auto InputSettings::cancelMapping() -> void {
activeMapping.reset();
settingsWindow->statusBar.setText();
settingsWindow->layout.setEnabled();
settingsWindow->doSize();
settingsWindow->setDismissable(true);
}
auto InputSettings::assignMouseInput(uint id) -> void {
if(auto mapping = mappingList.selected()) {
activeMapping = activeDevice().mappings[mapping.offset()];
@ -146,9 +154,7 @@ auto InputSettings::inputEvent(shared_pointer<HID::Device> device, uint group, u
refreshMappings();
timer.onActivate([&] {
timer.setEnabled(false);
settingsWindow->statusBar.setText();
settingsWindow->layout.setEnabled();
settingsWindow->doSize();
cancelMapping();
}).setInterval(200).setEnabled();
}
}

View File

@ -4,7 +4,8 @@
#include "input.cpp"
#include "hotkeys.cpp"
#include "paths.cpp"
#include "advanced.cpp"
#include "configuration.cpp"
#include "drivers.cpp"
Settings settings;
unique_pointer<SettingsWindow> settingsWindow;
@ -19,6 +20,8 @@ Settings::Settings() {
set("Video/Driver", Video::safestDriver());
set("Video/Exclusive", false);
set("Video/Blocking", false);
set("Video/Flush", false);
set("Video/Format", "Default");
set("Video/Shader", "Blur");
set("Video/Luminance", "100%");
set("Video/Saturation", "100%");
@ -26,9 +29,10 @@ Settings::Settings() {
set("Audio/Driver", Audio::safestDriver());
set("Audio/Exclusive", false);
set("Audio/Blocking", true);
set("Audio/Device", "");
set("Audio/Frequency", 48000.0);
set("Audio/Blocking", true);
set("Audio/Dynamic", false);
set("Audio/Frequency", "48000hz");
set("Audio/Latency", 0);
set("Audio/Mute", false);
set("Audio/Skew", "0");
@ -38,6 +42,7 @@ Settings::Settings() {
set("Input/Driver", Input::safestDriver());
set("Input/Frequency", 5);
set("Input/Defocus", "Pause");
set("Input/Turbo/Frequency", 4);
set("View/Multiplier", "2");
set("View/Output", "Scale");
@ -58,6 +63,7 @@ Settings::Settings() {
set("Path/Recent/SufamiTurboB", Path::user());
set("UserInterface/ShowStatusBar", true);
set("UserInterface/SuppressScreenSaver", true);
set("Emulator/WarnOnUnverifiedGames", false);
set("Emulator/AutoSaveMemory/Enable", true);
@ -93,6 +99,12 @@ SettingsWindow::SettingsWindow() {
input.mappingList.resizeColumns();
hotkeys.mappingList.resizeColumns();
});
onClose([&] {
if(input.activeMapping) input.cancelMapping();
if(hotkeys.activeMapping) hotkeys.cancelMapping();
setVisible(false);
});
}
auto SettingsWindow::setVisible(bool visible) -> SettingsWindow& {

View File

@ -21,27 +21,13 @@ public:
Label gammaLabel{&colorLayout, Size{0, 0}};
Label gammaValue{&colorLayout, Size{50, 0}};
HorizontalSlider gammaSlider{&colorLayout, Size{~0, 0}};
Label fullscreenLabel{&layout, Size{~0, 0}, 2};
CheckLabel exclusiveMode{&layout, Size{~0, 0}};
};
struct AudioSettings : TabFrameItem {
AudioSettings(TabFrame*);
auto updateDevice() -> void;
auto updateFrequency() -> void;
auto updateLatency() -> void;
public:
VerticalLayout layout{this};
Label driverLabel{&layout, Size{~0, 0}, 2};
HorizontalLayout driverLayout{&layout, Size{~0, 0}};
Label deviceLabel{&driverLayout, Size{0, 0}};
ComboButton deviceList{&driverLayout, Size{~0, 0}};
Label frequencyLabel{&driverLayout, Size{0, 0}};
ComboButton frequencyList{&driverLayout, Size{80, 0}};
Label latencyLabel{&driverLayout, Size{0, 0}};
ComboButton latencyList{&driverLayout, Size{80, 0}};
CheckLabel exclusiveMode{&layout, Size{~0, 0}};
Label effectsLabel{&layout, Size{~0, 0}, 2};
TableLayout effectsLayout{&layout, Size{~0, 0}};
Label skewLabel{&effectsLayout, Size{0, 0}};
@ -67,6 +53,7 @@ struct InputSettings : TabFrameItem {
auto reloadMappings() -> void;
auto refreshMappings() -> void;
auto assignMapping() -> void;
auto cancelMapping() -> void;
auto assignMouseInput(uint id) -> void;
auto inputEvent(shared_pointer<HID::Device> device, uint group, uint input, int16 oldValue, int16 newValue, bool allowMouseInput = false) -> void;
@ -75,17 +62,13 @@ public:
Timer timer;
VerticalLayout layout{this};
HorizontalLayout defocusLayout{&layout, Size{~0, 0}};
Label defocusLabel{&defocusLayout, Size{0, 0}};
RadioLabel pauseEmulation{&defocusLayout, Size{0, 0}};
RadioLabel blockInput{&defocusLayout, Size{0, 0}};
RadioLabel allowInput{&defocusLayout, Size{0, 0}};
Group defocusGroup{&pauseEmulation, &blockInput, &allowInput};
HorizontalLayout selectionLayout{&layout, Size{~0, 0}};
Label portLabel{&selectionLayout, Size{0, 0}};
ComboButton portList{&selectionLayout, Size{~0, 0}};
Label deviceLabel{&selectionLayout, Size{0, 0}};
ComboButton deviceList{&selectionLayout, Size{~0, 0}};
Label turboLabel{&selectionLayout, Size{0, 0}};
ComboButton turboList{&selectionLayout, Size{0, 0}};
TableView mappingList{&layout, Size{~0, ~0}};
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
Button assignMouse1{&controlLayout, Size{100, 0}};
@ -101,6 +84,7 @@ struct HotkeySettings : TabFrameItem {
auto reloadMappings() -> void;
auto refreshMappings() -> void;
auto assignMapping() -> void;
auto cancelMapping() -> void;
auto inputEvent(shared_pointer<HID::Device> device, uint group, uint input, int16 oldValue, int16 newValue) -> void;
public:
@ -152,29 +136,25 @@ public:
Button screenshotsReset{&layout, Size{80, 0}};
};
struct AdvancedSettings : TabFrameItem {
AdvancedSettings(TabFrame*);
auto updateVideoDriver() -> void;
auto updateAudioDriver() -> void;
auto updateInputDriver() -> void;
struct ConfigurationSettings : TabFrameItem {
ConfigurationSettings(TabFrame*);
auto updateConfiguration() -> void;
public:
VerticalLayout layout{this};
Label driversLabel{&layout, Size{~0, 0}, 2};
HorizontalLayout driverLayout{&layout, Size{~0, 0}};
Label videoDriverLabel{&driverLayout, Size{0, 0}};
ComboButton videoDriverOption{&driverLayout, Size{~0, 0}};
Label audioDriverLabel{&driverLayout, Size{0, 0}};
ComboButton audioDriverOption{&driverLayout, Size{~0, 0}};
Label inputDriverLabel{&driverLayout, Size{0, 0}};
ComboButton inputDriverOption{&driverLayout, Size{~0, 0}};
Label optionsLabel{&layout, Size{~0, 0}, 2};
HorizontalLayout inputFocusLayout{&layout, Size{~0, 0}};
Label inputFocusLabel{&inputFocusLayout, Size{0, 0}};
RadioLabel pauseEmulation{&inputFocusLayout, Size{0, 0}};
RadioLabel blockInput{&inputFocusLayout, Size{0, 0}};
RadioLabel allowInput{&inputFocusLayout, Size{0, 0}};
Group inputFocusGroup{&pauseEmulation, &blockInput, &allowInput};
CheckLabel warnOnUnverifiedGames{&layout, Size{~0, 0}};
CheckLabel autoSaveMemory{&layout, Size{~0, 0}};
HorizontalLayout autoStateLayout{&layout, Size{~0, 0}};
CheckLabel autoSaveStateOnUnload{&autoStateLayout, Size{0, 0}};
CheckLabel autoLoadStateOnLoad{&autoStateLayout, Size{0, 0}};
CheckLabel suppressScreenSaver{&layout, Size{~0, 0}};
Label hacksLabel{&layout, Size{~0, 0}, 2};
HorizontalLayout fastPPULayout{&layout, Size{~0, 0}};
CheckLabel fastPPUOption{&fastPPULayout, Size{0, 0}};
@ -188,6 +168,66 @@ public:
Label hacksNote{&layout, Size{~0, 0}};
};
struct DriverSettings : TabFrameItem {
DriverSettings(TabFrame*);
auto videoDriverChanged() -> void;
auto videoDriverChange() -> void;
auto videoFormatChanged() -> void;
auto videoFormatChange() -> void;
auto audioDriverChanged() -> void;
auto audioDriverChange() -> void;
auto audioDeviceChanged() -> void;
auto audioDeviceChange() -> void;
auto audioFrequencyChanged() -> void;
auto audioFrequencyChange() -> void;
auto audioLatencyChanged() -> void;
auto audioLatencyChange() -> void;
auto inputDriverChanged() -> void;
auto inputDriverChange() -> void;
public:
VerticalLayout layout{this};
Label videoLabel{&layout, Size{~0, 0}, 2};
TableLayout videoLayout{&layout, Size{~0, 0}};
Label videoDriverLabel{&videoLayout, Size{0, 0}};
HorizontalLayout videoDriverLayout{&videoLayout, Size{~0, 0}};
ComboButton videoDriverOption{&videoDriverLayout, Size{0, 0}};
Button videoDriverUpdate{&videoDriverLayout, Size{0, 0}};
Label videoDriverActive{&videoDriverLayout, Size{0, 0}};
Label videoFormatLabel{&videoLayout, Size{0, 0}};
HorizontalLayout videoPropertyLayout{&videoLayout, Size{~0, 0}};
ComboButton videoFormatOption{&videoPropertyLayout, Size{0, 0}};
HorizontalLayout videoToggleLayout{&layout, Size{~0, 0}};
CheckLabel videoExclusiveToggle{&videoToggleLayout, Size{0, 0}};
CheckLabel videoBlockingToggle{&videoToggleLayout, Size{0, 0}};
CheckLabel videoFlushToggle{&videoToggleLayout, Size{0, 0}};
Label audioLabel{&layout, Size{~0, 0}, 2};
TableLayout audioLayout{&layout, Size{~0, 0}};
Label audioDriverLabel{&audioLayout, Size{0, 0}};
HorizontalLayout audioDriverLayout{&audioLayout, Size{~0, 0}};
ComboButton audioDriverOption{&audioDriverLayout, Size{0, 0}};
Button audioDriverUpdate{&audioDriverLayout, Size{0, 0}};
Label audioDriverActive{&audioDriverLayout, Size{0, 0}};
Label audioDeviceLabel{&audioLayout, Size{0, 0}};
HorizontalLayout audioPropertyLayout{&audioLayout, Size{~0, 0}};
ComboButton audioDeviceOption{&audioPropertyLayout, Size{0, 0}};
Label audioFrequencyLabel{&audioPropertyLayout, Size{0, 0}};
ComboButton audioFrequencyOption{&audioPropertyLayout, Size{0, 0}};
Label audioLatencyLabel{&audioPropertyLayout, Size{0, 0}};
ComboButton audioLatencyOption{&audioPropertyLayout, Size{0, 0}};
HorizontalLayout audioToggleLayout{&layout, Size{~0, 0}};
CheckLabel audioExclusiveToggle{&audioToggleLayout, Size{0, 0}};
CheckLabel audioBlockingToggle{&audioToggleLayout, Size{0, 0}};
CheckLabel audioDynamicToggle{&audioToggleLayout, Size{0, 0}};
Label inputLabel{&layout, Size{~0, 0}, 2};
TableLayout inputLayout{&layout, Size{~0, 0}};
Label inputDriverLabel{&inputLayout, Size{0, 0}};
HorizontalLayout inputDriverLayout{&inputLayout, Size{~0, 0}};
ComboButton inputDriverOption{&inputDriverLayout, Size{0, 0}};
Button inputDriverUpdate{&inputDriverLayout, Size{0, 0}};
Label inputDriverActive{&inputDriverLayout, Size{0, 0}};
};
struct SettingsWindow : Window {
SettingsWindow();
auto setVisible(bool visible = true) -> SettingsWindow&;
@ -201,7 +241,8 @@ public:
InputSettings input{&panel};
HotkeySettings hotkeys{&panel};
PathSettings paths{&panel};
AdvancedSettings advanced{&panel};
ConfigurationSettings configuration{&panel};
DriverSettings drivers{&panel};
StatusBar statusBar{this};
};

View File

@ -31,9 +31,4 @@ VideoSettings::VideoSettings(TabFrame* parent) : TabFrameItem(parent) {
gammaValue.setText(value);
program->updateVideoPalette();
}).doChange();
fullscreenLabel.setFont(Font().setBold()).setText("Fullscreen");
exclusiveMode.setText("Exclusive mode").setChecked(settings["Video/Exclusive"].boolean()).onToggle([&] {
settings["Video/Exclusive"].setValue(exclusiveMode.checked());
});
}

View File

@ -81,12 +81,15 @@ StateManager::StateManager(TabFrame* parent) : TabFrameItem(parent) {
});
loadButton.setText("Load").onActivate([&] {
if(auto item = stateList.selected()) {
program->loadState({"managed/", item.cell(0).text()});
string filename = {"managed/", item.cell(0).text()};
program->loadState(filename);
}
});
saveButton.setText("Save").onActivate([&] {
if(auto item = stateList.selected()) {
program->saveState({"managed/", item.cell(0).text()});
string filename = {"managed/", item.cell(0).text()};
program->saveState(filename);
item.cell(1).setText(chrono::local::datetime(program->stateTimestamp(filename)));
}
});
addButton.setText("Add").onActivate([&] {
@ -104,12 +107,14 @@ StateManager::StateManager(TabFrame* parent) : TabFrameItem(parent) {
auto StateManager::loadStates() -> void {
stateList.reset();
stateList.append(TableViewHeader().setVisible(false)
.append(TableViewColumn().setExpandable())
stateList.append(TableViewHeader()
.append(TableViewColumn().setText("Name").setExpandable())
.append(TableViewColumn().setText("Date").setForegroundColor({160, 160, 160}))
);
for(auto filename : program->managedStates()) {
for(auto& filename : program->availableStates("managed/")) {
stateList.append(TableViewItem()
.append(TableViewCell().setText(filename.trimRight(".bst", 1L)))
.append(TableViewCell().setText(string{filename}.trimLeft("managed/", 1L)))
.append(TableViewCell().setText(chrono::local::datetime(program->stateTimestamp(filename))))
);
}
stateList.resizeColumns().doChange();
@ -118,7 +123,7 @@ auto StateManager::loadStates() -> void {
auto StateManager::createState(string name) -> void {
program->saveState({"managed/", name});
loadStates();
for(auto item : stateList.items()) {
for(auto& item : stateList.items()) {
if(item.cell(0).text() == name) item.setSelected();
}
stateList.doChange();
@ -131,7 +136,7 @@ auto StateManager::modifyState(string name) -> void {
if(from != to) {
program->renameState(from, to);
loadStates();
for(auto item : stateList.items()) {
for(auto& item : stateList.items()) {
if(item.cell(0).text() == name) item.setSelected();
}
stateList.doChange();
@ -143,7 +148,7 @@ auto StateManager::removeStates() -> void {
if(auto batched = stateList.batched()) {
if(MessageDialog("Are you sure you want to permanently remove the selected state(s)?")
.setParent(*toolsWindow).question() == "Yes") {
for(auto item : batched) {
for(auto& item : batched) {
program->removeState({"managed/", item.cell(0).text()});
}
loadStates();

View File

@ -1,8 +1,3 @@
ifeq ($(platform),)
hiro.flags = $(flags.cpp) -DHIRO_REFERENCE
hiro.options =
endif
ifeq ($(platform),windows)
ifeq ($(hiro),)
hiro := windows

View File

@ -33,8 +33,8 @@ auto DropPathsOperation(id<NSDraggingInfo> sender) -> NSDragOperation {
return NSDragOperationNone;
}
auto DropPaths(id<NSDraggingInfo> sender) -> string_vector {
string_vector paths;
auto DropPaths(id<NSDraggingInfo> sender) -> vector<string> {
vector<string> paths;
NSPasteboard* pboard = [sender draggingPasteboard];
if([[pboard types] containsObject:NSFilenamesPboardType]) {
NSArray* files = [pboard propertyListForType:NSFilenamesPboardType];

View File

@ -241,6 +241,11 @@ auto pWindow::frameMargin() const -> Geometry {
}
}
auto pWindow::monitor() const -> uint {
//TODO
return 0;
}
auto pWindow::remove(sMenuBar menuBar) -> void {
}

View File

@ -35,6 +35,7 @@ struct pWindow : pObject {
auto append(sStatusBar statusBar) -> void;
auto focused() const -> bool override;
auto frameMargin() const -> Geometry;
auto monitor() const -> uint;
auto remove(sMenuBar menuBar) -> void;
auto remove(sSizable sizable) -> void;
auto remove(sStatusBar statusBar) -> void;

View File

@ -8,8 +8,6 @@
#include "../gtk/header.hpp"
#elif defined(HIRO_COCOA)
#include "../cocoa/header.hpp"
#elif defined(HIRO_REFERENCE)
#include "../reference/header.hpp"
#endif
#include "core.hpp"
@ -23,8 +21,6 @@ using namespace nall;
#include "../gtk/platform.cpp"
#elif defined(HIRO_COCOA)
#include "../cocoa/platform.cpp"
#elif defined(HIRO_REFERENCE)
#include "../reference/platform.cpp"
#endif
#define signal(function, ...) \

View File

@ -371,18 +371,7 @@ struct Hotkey {
#include "application.hpp"
#include "desktop.hpp"
#if defined(Hiro_Monitor)
struct Monitor {
Monitor() = delete;
static auto count() -> uint;
static auto dpi(maybe<uint> monitor = nothing) -> Position;
static auto geometry(maybe<uint> monitor = nothing) -> Geometry;
static auto primary() -> uint;
static auto workspace(maybe<uint> monitor = nothing) -> Geometry;
};
#endif
#include "monitor.hpp"
#if defined(Hiro_Keyboard)
struct Keyboard {
@ -504,71 +493,7 @@ private:
} \
virtual auto allocate() -> pObject*; \
#if defined(Hiro_Object)
struct mObject {
Declare(Object)
mObject();
virtual ~mObject();
mObject(const mObject&) = delete;
mObject& operator=(const mObject&) = delete;
explicit operator bool() const;
auto abstract() const -> bool;
auto adjustOffset(int displacement) -> type&;
auto enabled(bool recursive = false) const -> bool;
virtual auto focused() const -> bool;
auto font(bool recursive = false) const -> Font;
virtual auto group() const -> Group;
auto offset() const -> int;
auto parent() const -> mObject*;
auto parentComboButton(bool recursive = false) const -> mComboButton*;
auto parentComboEdit(bool recursive = false) const -> mComboEdit*;
auto parentFrame(bool recursive = false) const -> mFrame*;
auto parentIconView(bool recursive = false) const -> mIconView*;
auto parentMenu(bool recursive = false) const -> mMenu*;
auto parentMenuBar(bool recursive = false) const -> mMenuBar*;
auto parentPopupMenu(bool recursive = false) const -> mPopupMenu*;
auto parentSizable(bool recursive = false) const -> mSizable*;
auto parentTabFrame(bool recursive = false) const -> mTabFrame*;
auto parentTabFrameItem(bool recursive = false) const -> mTabFrameItem*;
auto parentTableView(bool recursive = false) const -> mTableView*;
auto parentTableViewHeader(bool recursive = false) const -> mTableViewHeader*;
auto parentTableViewItem(bool recursive = false) const -> mTableViewItem*;
auto parentTreeView(bool recursive = false) const -> mTreeView*;
auto parentTreeViewItem(bool recursive = false) const -> mTreeViewItem*;
auto parentWidget(bool recursive = false) const -> mWidget*;
auto parentWindow(bool recursive = false) const -> mWindow*;
auto property(const string& name) const -> string;
virtual auto remove() -> type&;
virtual auto reset() -> type&;
virtual auto setEnabled(bool enabled = true) -> type&;
virtual auto setFocused() -> type&;
virtual auto setFont(const Font& font = {}) -> type&;
virtual auto setGroup(sGroup group = {}) -> type&;
virtual auto setParent(mObject* parent = nullptr, int offset = -1) -> type&;
virtual auto setProperty(const string& name, const string& value = "") -> type&;
virtual auto setVisible(bool visible = true) -> type&;
auto visible(bool recursive = false) const -> bool;
//private:
struct State {
bool enabled = true;
Font font;
int offset = -1;
mObject* parent = nullptr;
set<Property> properties;
bool visible = true;
} state;
wObject instance;
pObject* delegate = nullptr;
virtual auto construct() -> void;
virtual auto destruct() -> void;
};
#endif
#include "object.hpp"
#if defined(Hiro_Group)
struct mGroup : mObject {
@ -605,97 +530,7 @@ struct mTimer : mObject {
};
#endif
#if defined(Hiro_Window)
struct mWindow : mObject {
Declare(Window)
using mObject::remove;
mWindow();
auto append(sMenuBar menuBar) -> type&;
auto append(sSizable sizable) -> type&;
auto append(sStatusBar statusBar) -> type&;
auto backgroundColor() const -> Color;
auto dismissable() const -> bool;
auto doClose() const -> void;
auto doDrop(vector<string>) const -> void;
auto doKeyPress(int) const -> void;
auto doKeyRelease(int) const -> void;
auto doMove() const -> void;
auto doSize() const -> void;
auto droppable() const -> bool;
auto frameGeometry() const -> Geometry;
auto fullScreen() const -> bool;
auto geometry() const -> Geometry;
auto maximized() const -> bool;
auto maximumSize() const -> Size;
auto menuBar() const -> MenuBar;
auto minimized() const -> bool;
auto minimumSize() const -> Size;
auto modal() const -> bool;
auto onClose(const function<void ()>& callback = {}) -> type&;
auto onDrop(const function<void (vector<string>)>& callback = {}) -> type&;
auto onKeyPress(const function<void (int)>& callback = {}) -> type&;
auto onKeyRelease(const function<void (int)>& callback = {}) -> type&;
auto onMove(const function<void ()>& callback = {}) -> type&;
auto onSize(const function<void ()>& callback = {}) -> type&;
auto remove(sMenuBar menuBar) -> type&;
auto remove(sSizable sizable) -> type&;
auto remove(sStatusBar statusBar) -> type&;
auto reset() -> type& override;
auto resizable() const -> bool;
auto setAlignment(Alignment alignment) -> type&;
auto setBackgroundColor(Color color = {}) -> type&;
auto setCentered(sWindow parent = {}) -> type&;
auto setDismissable(bool dismissable = true) -> type&;
auto setDroppable(bool droppable = true) -> type&;
auto setFrameGeometry(Geometry geometry) -> type&;
auto setFramePosition(Position position) -> type&;
auto setFrameSize(Size size) -> type&;
auto setFullScreen(bool fullScreen = true) -> type&;
auto setGeometry(Geometry geometry) -> type&;
auto setMaximized(bool maximized = true) -> type&;
auto setMaximumSize(Size size = {}) -> type&;
auto setMinimized(bool minimized = true) -> type&;
auto setMinimumSize(Size size = {}) -> type&;
auto setModal(bool modal = true) -> type&;
auto setPosition(Position position) -> type&;
auto setResizable(bool resizable = true) -> type&;
auto setSize(Size size) -> type&;
auto setTitle(const string& title = "") -> type&;
auto setVisible(bool visible = true) -> type&;
auto sizable() const -> Sizable;
auto statusBar() const -> StatusBar;
auto title() const -> string;
//private:
struct State {
Color backgroundColor;
bool dismissable = false;
bool droppable = false;
bool fullScreen = false;
Geometry geometry = {128, 128, 256, 256};
bool maximized = false;
Size maximumSize;
bool minimized = false;
Size minimumSize;
sMenuBar menuBar;
bool modal = false;
function<void ()> onClose;
function<void (vector<string>)> onDrop;
function<void (int)> onKeyPress;
function<void (int)> onKeyRelease;
function<void ()> onMove;
function<void ()> onSize;
bool resizable = true;
sSizable sizable;
sStatusBar statusBar;
string title;
} state;
auto destruct() -> void;
};
#endif
#include "window.hpp"
#if defined(Hiro_StatusBar)
struct mStatusBar : mObject {

11
hiro/core/monitor.hpp Normal file
View File

@ -0,0 +1,11 @@
#if defined(Hiro_Monitor)
struct Monitor {
Monitor() = delete;
static auto count() -> uint;
static auto dpi(maybe<uint> monitor = nothing) -> Position;
static auto geometry(maybe<uint> monitor = nothing) -> Geometry;
static auto primary() -> uint;
static auto workspace(maybe<uint> monitor = nothing) -> Geometry;
};
#endif

View File

@ -68,8 +68,7 @@ auto mObject::enabled(bool recursive) const -> bool {
}
auto mObject::focused() const -> bool {
if(signal(focused)) return true;
return false;
return signal(focused);
}
auto mObject::font(bool recursive) const -> Font {

65
hiro/core/object.hpp Normal file
View File

@ -0,0 +1,65 @@
#if defined(Hiro_Object)
struct mObject {
Declare(Object)
mObject();
virtual ~mObject();
mObject(const mObject&) = delete;
mObject& operator=(const mObject&) = delete;
explicit operator bool() const;
auto abstract() const -> bool;
auto adjustOffset(int displacement) -> type&;
auto enabled(bool recursive = false) const -> bool;
virtual auto focused() const -> bool;
auto font(bool recursive = false) const -> Font;
virtual auto group() const -> Group;
auto offset() const -> int;
auto parent() const -> mObject*;
auto parentComboButton(bool recursive = false) const -> mComboButton*;
auto parentComboEdit(bool recursive = false) const -> mComboEdit*;
auto parentFrame(bool recursive = false) const -> mFrame*;
auto parentIconView(bool recursive = false) const -> mIconView*;
auto parentMenu(bool recursive = false) const -> mMenu*;
auto parentMenuBar(bool recursive = false) const -> mMenuBar*;
auto parentPopupMenu(bool recursive = false) const -> mPopupMenu*;
auto parentSizable(bool recursive = false) const -> mSizable*;
auto parentTabFrame(bool recursive = false) const -> mTabFrame*;
auto parentTabFrameItem(bool recursive = false) const -> mTabFrameItem*;
auto parentTableView(bool recursive = false) const -> mTableView*;
auto parentTableViewHeader(bool recursive = false) const -> mTableViewHeader*;
auto parentTableViewItem(bool recursive = false) const -> mTableViewItem*;
auto parentTreeView(bool recursive = false) const -> mTreeView*;
auto parentTreeViewItem(bool recursive = false) const -> mTreeViewItem*;
auto parentWidget(bool recursive = false) const -> mWidget*;
auto parentWindow(bool recursive = false) const -> mWindow*;
auto property(const string& name) const -> string;
virtual auto remove() -> type&;
virtual auto reset() -> type&;
virtual auto setEnabled(bool enabled = true) -> type&;
virtual auto setFocused() -> type&;
virtual auto setFont(const Font& font = {}) -> type&;
virtual auto setGroup(sGroup group = {}) -> type&;
virtual auto setParent(mObject* parent = nullptr, int offset = -1) -> type&;
virtual auto setProperty(const string& name, const string& value = "") -> type&;
virtual auto setVisible(bool visible = true) -> type&;
auto visible(bool recursive = false) const -> bool;
//private:
struct State {
bool enabled = true;
Font font;
int offset = -1;
mObject* parent = nullptr;
set<Property> properties;
bool visible = true;
} state;
wObject instance;
pObject* delegate = nullptr;
virtual auto construct() -> void;
virtual auto destruct() -> void;
};
#endif

View File

@ -944,6 +944,7 @@ struct Window : sWindow {
auto minimized() const { return self().minimized(); }
auto minimumSize() const { return self().minimumSize(); }
auto modal() const { return self().modal(); }
auto monitor() const { return self().monitor(); }
auto onClose(const function<void ()>& callback = {}) { return self().onClose(callback), *this; }
auto onDrop(const function<void (vector<string>)>& callback = {}) { return self().onDrop(callback), *this; }
auto onKeyPress(const function<void (signed)>& callback = {}) { return self().onKeyPress(callback), *this; }

View File

@ -117,6 +117,10 @@ auto mWindow::modal() const -> bool {
return state.modal;
}
auto mWindow::monitor() const -> uint {
return signal(monitor);
}
auto mWindow::onClose(const function<void ()>& callback) -> type& {
state.onClose = callback;
return *this;
@ -183,8 +187,8 @@ auto mWindow::setAlignment(Alignment alignment) -> type& {
if(!alignment) alignment = {0.0, 0.0};
auto workspace = Desktop::workspace();
auto geometry = frameGeometry();
signed left = alignment.horizontal() * (workspace.width() - geometry.width());
signed top = alignment.vertical() * (workspace.height() - geometry.height());
int left = alignment.horizontal() * (workspace.width() - geometry.width());
int top = alignment.vertical() * (workspace.height() - geometry.height());
setFramePosition({left, top});
return *this;
}
@ -196,12 +200,17 @@ auto mWindow::setBackgroundColor(Color color) -> type& {
}
auto mWindow::setCentered(sWindow parent) -> type& {
Geometry workspace = parent ? parent->frameGeometry() : Desktop::workspace();
Geometry workspace = Desktop::workspace();
Geometry parentGeometry = parent ? parent->frameGeometry() : workspace;
Geometry geometry = frameGeometry();
signed x = workspace.x();
signed y = workspace.y();
if(workspace.width() > geometry.width()) x += (workspace.width() - geometry.width()) / 2;
if(workspace.height() > geometry.height()) y += (workspace.height() - geometry.height()) / 2;
//center the window to its parent window ...
int x = parentGeometry.x() + (parentGeometry.width() - geometry.width()) / 2;
int y = parentGeometry.y() + (parentGeometry.height() - geometry.height()) / 2;
//try and keep the window onscreen ...
if(x + geometry.width() > workspace.width()) x = workspace.width() - geometry.width();
if(y + geometry.height() > workspace.height()) y = workspace.height() - geometry.height();
if(x < workspace.x()) x = workspace.x();
if(y < workspace.y()) y = workspace.y();
return setFrameGeometry({x, y, geometry.width(), geometry.height()});
}

92
hiro/core/window.hpp Normal file
View File

@ -0,0 +1,92 @@
#if defined(Hiro_Window)
struct mWindow : mObject {
Declare(Window)
using mObject::remove;
mWindow();
auto append(sMenuBar menuBar) -> type&;
auto append(sSizable sizable) -> type&;
auto append(sStatusBar statusBar) -> type&;
auto backgroundColor() const -> Color;
auto dismissable() const -> bool;
auto doClose() const -> void;
auto doDrop(vector<string>) const -> void;
auto doKeyPress(int) const -> void;
auto doKeyRelease(int) const -> void;
auto doMove() const -> void;
auto doSize() const -> void;
auto droppable() const -> bool;
auto frameGeometry() const -> Geometry;
auto fullScreen() const -> bool;
auto geometry() const -> Geometry;
auto maximized() const -> bool;
auto maximumSize() const -> Size;
auto menuBar() const -> MenuBar;
auto minimized() const -> bool;
auto minimumSize() const -> Size;
auto modal() const -> bool;
auto monitor() const -> uint;
auto onClose(const function<void ()>& callback = {}) -> type&;
auto onDrop(const function<void (vector<string>)>& callback = {}) -> type&;
auto onKeyPress(const function<void (int)>& callback = {}) -> type&;
auto onKeyRelease(const function<void (int)>& callback = {}) -> type&;
auto onMove(const function<void ()>& callback = {}) -> type&;
auto onSize(const function<void ()>& callback = {}) -> type&;
auto remove(sMenuBar menuBar) -> type&;
auto remove(sSizable sizable) -> type&;
auto remove(sStatusBar statusBar) -> type&;
auto reset() -> type& override;
auto resizable() const -> bool;
auto setAlignment(Alignment alignment) -> type&;
auto setBackgroundColor(Color color = {}) -> type&;
auto setCentered(sWindow parent = {}) -> type&;
auto setDismissable(bool dismissable = true) -> type&;
auto setDroppable(bool droppable = true) -> type&;
auto setFrameGeometry(Geometry geometry) -> type&;
auto setFramePosition(Position position) -> type&;
auto setFrameSize(Size size) -> type&;
auto setFullScreen(bool fullScreen = true) -> type&;
auto setGeometry(Geometry geometry) -> type&;
auto setMaximized(bool maximized = true) -> type&;
auto setMaximumSize(Size size = {}) -> type&;
auto setMinimized(bool minimized = true) -> type&;
auto setMinimumSize(Size size = {}) -> type&;
auto setModal(bool modal = true) -> type&;
auto setPosition(Position position) -> type&;
auto setResizable(bool resizable = true) -> type&;
auto setSize(Size size) -> type&;
auto setTitle(const string& title = "") -> type&;
auto setVisible(bool visible = true) -> type&;
auto sizable() const -> Sizable;
auto statusBar() const -> StatusBar;
auto title() const -> string;
//private:
struct State {
Color backgroundColor;
bool dismissable = false;
bool droppable = false;
bool fullScreen = false;
Geometry geometry = {128, 128, 256, 256};
bool maximized = false;
Size maximumSize;
bool minimized = false;
Size minimumSize;
sMenuBar menuBar;
bool modal = false;
function<void ()> onClose;
function<void (vector<string>)> onDrop;
function<void (int)> onKeyPress;
function<void (int)> onKeyRelease;
function<void ()> onMove;
function<void ()> onSize;
bool resizable = true;
sSizable sizable;
sStatusBar statusBar;
string title;
} state;
auto destruct() -> void;
};
#endif

View File

@ -38,6 +38,13 @@ auto mFixedLayout::minimumSize() const -> Size {
return {width, height};
}
auto mFixedLayout::remove(sSizable sizable) -> type& {
for(auto& cell : state.cells) {
if(cell->state.sizable == sizable) return remove(cell);
}
return *this;
}
auto mFixedLayout::remove(sFixedLayoutCell cell) -> type& {
if(cell->parent() != this) return *this;
auto offset = cell->offset();

View File

@ -18,6 +18,7 @@ struct mFixedLayout : mSizable {
auto cell(sSizable sizable) const -> FixedLayoutCell;
auto cellCount() const -> uint;
auto minimumSize() const -> Size override;
auto remove(sSizable sizable) -> type&;
auto remove(sFixedLayoutCell cell) -> type&;
auto reset() -> type& override;
auto setEnabled(bool enabled) -> type& override;

View File

@ -66,6 +66,13 @@ auto mHorizontalLayout::padding() const -> Geometry {
return state.padding;
}
auto mHorizontalLayout::remove(sSizable sizable) -> type& {
for(auto& cell : state.cells) {
if(cell->state.sizable == sizable) return remove(cell);
}
return *this;
}
auto mHorizontalLayout::remove(sHorizontalLayoutCell cell) -> type& {
if(cell->parent() != this) return *this;
auto offset = cell->offset();

View File

@ -20,6 +20,7 @@ struct mHorizontalLayout : mSizable {
auto cellCount() const -> uint;
auto minimumSize() const -> Size override;
auto padding() const -> Geometry;
auto remove(sSizable sizable) -> type&;
auto remove(sHorizontalLayoutCell cell) -> type&;
auto reset() -> type& override;
auto setAlignment(maybe<float> alignment) -> type&;

View File

@ -15,6 +15,7 @@ struct FixedLayout : sFixedLayout {
auto cell(uint position) const { return self().cell(position); }
auto cell(sSizable sizable) const { return self().cell(sizable); }
auto cellCount() const { return self().cellCount(); }
auto remove(sSizable sizable) { return self().remove(sizable), *this; }
auto remove(sFixedLayoutCell cell) { return self().remove(cell), *this; }
auto reset() { return self().reset(), *this; }
};
@ -42,6 +43,7 @@ struct HorizontalLayout : sHorizontalLayout {
auto cell(uint position) const { return self().cell(position); }
auto cell(sSizable sizable) const { return self().cell(sizable); }
auto cellCount() const { return self().cellCount(); }
auto remove(sSizable sizable) { return self().remove(sizable), *this; }
auto remove(sHorizontalLayoutCell cell) { return self().remove(cell), *this; }
auto reset() { return self().reset(), *this; }
auto setAlignment(maybe<float> alignment = {}) { return self().setAlignment(alignment), *this; }
@ -125,6 +127,7 @@ struct TableLayout : sTableLayout {
auto column(uint position) const { return self().column(position); }
auto columnCount() const { return self().columnCount(); }
auto padding() const { return self().padding(); }
auto remove(sSizable sizable) { return self().remove(sizable), *this; }
auto remove(sTableLayoutCell cell) { return self().remove(cell), *this; }
auto reset() { return self().reset(), *this; }
auto row(uint position) const { return self().row(position); }

View File

@ -93,6 +93,13 @@ auto mTableLayout::padding() const -> Geometry {
return state.padding;
}
auto mTableLayout::remove(sSizable sizable) -> type& {
for(auto& cell : state.cells) {
if(cell->state.sizable == sizable) return remove(cell);
}
return *this;
}
auto mTableLayout::remove(sTableLayoutCell cell) -> type& {
if(cell->parent() != this) return *this;
auto offset = cell->offset();

View File

@ -29,6 +29,7 @@ struct mTableLayout : mSizable {
auto columnCount() const -> uint;
auto minimumSize() const -> Size override;
auto padding() const -> Geometry;
auto remove(sSizable sizable) -> type&;
auto remove(sTableLayoutCell cell) -> type&;
auto reset() -> type&;
auto row(uint position) const -> TableLayoutRow;

View File

@ -68,10 +68,7 @@ auto mVerticalLayout::padding() const -> Geometry {
auto mVerticalLayout::remove(sSizable sizable) -> type& {
for(auto& cell : state.cells) {
if(cell->state.sizable == sizable) {
remove(cell);
break;
}
if(cell->state.sizable == sizable) return remove(cell);
}
return *this;
}

View File

@ -269,6 +269,12 @@ auto pWindow::handle() const -> uintptr {
return (uintptr)nullptr;
}
auto pWindow::monitor() const -> uint {
if(!gtk_widget_get_realized(widget)) return 0;
auto window = gtk_widget_get_window(widget);
return gdk_screen_get_monitor_at_window(gdk_screen_get_default(), window);
}
auto pWindow::remove(sMenuBar menuBar) -> void {
_setMenuVisible(false);
}
@ -558,7 +564,9 @@ auto pWindow::_synchronizeGeometry() -> void {
}
lastSize = allocation;
gtk_widget_get_allocation(widget, &allocation);
auto gdkWindow = gtk_widget_get_window(widget);
gdk_window_get_origin(gdkWindow, &allocation.x, &allocation.y);
allocation.y += _menuHeight();
if(allocation.x != lastMove.x || allocation.y != lastMove.y) {
state().geometry.setPosition({allocation.x, allocation.y});
self().doMove();

View File

@ -11,6 +11,7 @@ struct pWindow : pObject {
auto focused() const -> bool override;
auto frameMargin() const -> Geometry;
auto handle() const -> uintptr;
auto monitor() const -> uint;
auto remove(sMenuBar menuBar) -> void;
auto remove(sSizable sizable) -> void;
auto remove(sStatusBar statusBar) -> void;

View File

@ -28,11 +28,11 @@ static auto CreateIcon(const image& icon, bool scale = false) -> QIcon {
return QIcon(QPixmap::fromImage(qtImage));
}
static auto DropPaths(QDropEvent* event) -> string_vector {
static auto DropPaths(QDropEvent* event) -> vector<string> {
QList<QUrl> urls = event->mimeData()->urls();
if(urls.size() == 0) return {};
string_vector paths;
vector<string> paths;
for(auto n : range(urls.size())) {
string path{urls[n].path().toUtf8().constData()};
if(!path) continue;

View File

@ -78,6 +78,11 @@ auto pWindow::frameMargin() const -> Geometry {
};
}
auto pWindow::monitor() const -> uint {
//TODO
return 0;
}
auto pWindow::remove(sMenuBar menuBar) -> void {
//QMenuBar::removeMenu() does not exist
//qtMenu->clear();

View File

@ -10,6 +10,7 @@ struct pWindow : pObject {
auto append(sStatusBar statusBar) -> void;
auto focused() const -> bool override;
auto frameMargin() const -> Geometry;
auto monitor() const -> uint;
auto remove(sMenuBar menuBar) -> void;
auto remove(sSizable sizable) -> void;
auto remove(sStatusBar statusBar) -> void;

View File

@ -1,15 +0,0 @@
namespace phoenix {
void pAction::setEnabled(bool enabled) {
}
void pAction::setVisible(bool visible) {
}
void pAction::constructor() {
}
void pAction::destructor() {
}
}

View File

@ -1,14 +0,0 @@
namespace phoenix {
struct pAction : public pObject {
Action& action;
void setEnabled(bool enabled);
void setVisible(bool visible);
pAction(Action& action) : pObject(action), action(action) {}
void constructor();
void destructor();
};
}

View File

@ -1,15 +0,0 @@
namespace phoenix {
void pCheckItem::setChecked(bool checked) {
}
void pCheckItem::setText(string text) {
}
void pCheckItem::constructor() {
}
void pCheckItem::destructor() {
}
}

View File

@ -1,14 +0,0 @@
namespace phoenix {
struct pCheckItem : public pAction {
CheckItem& checkItem;
void setChecked(bool checked);
void setText(string text);
pCheckItem(CheckItem& checkItem) : pAction(checkItem), checkItem(checkItem) {}
void constructor();
void destructor();
};
}

View File

@ -1,15 +0,0 @@
namespace phoenix {
void pItem::setImage(const image& image) {
}
void pItem::setText(string text) {
}
void pItem::constructor() {
}
void pItem::destructor() {
}
}

View File

@ -1,14 +0,0 @@
namespace phoenix {
struct pItem : public pAction {
Item& item;
void setImage(const image& image);
void setText(string text);
pItem(Item& item) : pAction(item), item(item) {}
void constructor();
void destructor();
};
}

View File

@ -1,21 +0,0 @@
namespace phoenix {
void pMenu::append(Action& action) {
}
void pMenu::remove(Action& action) {
}
void pMenu::setImage(const image& image) {
}
void pMenu::setText(string text) {
}
void pMenu::constructor() {
}
void pMenu::destructor() {
}
}

View File

@ -1,16 +0,0 @@
namespace phoenix {
struct pMenu : public pAction {
Menu& menu;
void append(Action& action);
void remove(Action& action);
void setImage(const image& image);
void setText(string text);
pMenu(Menu& menu) : pAction(menu), menu(menu) {}
void constructor();
void destructor();
};
}

View File

@ -1,18 +0,0 @@
namespace phoenix {
void pRadioItem::setChecked() {
}
void pRadioItem::setGroup(const group<RadioItem>& group) {
}
void pRadioItem::setText(string text) {
}
void pRadioItem::constructor() {
}
void pRadioItem::destructor() {
}
}

View File

@ -1,15 +0,0 @@
namespace phoenix {
struct pRadioItem : public pAction {
RadioItem& radioItem;
void setChecked();
void setGroup(const group<RadioItem>& group);
void setText(string text);
pRadioItem(RadioItem& radioItem) : pAction(radioItem), radioItem(radioItem) {}
void constructor();
void destructor();
};
}

View File

@ -1,9 +0,0 @@
namespace phoenix {
void pSeparator::constructor() {
}
void pSeparator::destructor() {
}
}

View File

@ -1,11 +0,0 @@
namespace phoenix {
struct pSeparator : public pAction {
Separator& separator;
pSeparator(Separator& separator) : pAction(separator), separator(separator) {}
void constructor();
void destructor();
};
}

View File

@ -1,19 +0,0 @@
namespace phoenix {
void pApplication::run() {
}
bool pApplication::pendingEvents() {
return false;
}
void pApplication::processEvents() {
}
void pApplication::quit() {
}
void pApplication::initialize() {
}
}

View File

@ -1,12 +0,0 @@
namespace phoenix {
struct pApplication {
static void run();
static bool pendingEvents();
static void processEvents();
static void quit();
static void initialize();
};
}

View File

@ -1,15 +0,0 @@
namespace phoenix {
string pBrowserWindow::directory(BrowserWindow::State& state) {
return "";
}
string pBrowserWindow::open(BrowserWindow::State& state) {
return "";
}
string pBrowserWindow::save(BrowserWindow::State& state) {
return "";
}
}

View File

@ -1,9 +0,0 @@
namespace phoenix {
struct pBrowserWindow {
static string directory(BrowserWindow::State& state);
static string open(BrowserWindow::State& state);
static string save(BrowserWindow::State& state);
};
}

View File

@ -1,11 +0,0 @@
namespace phoenix {
Size pDesktop::size() {
return {0, 0};
}
Geometry pDesktop::workspace() {
return {0, 0, 0, 0};
}
}

View File

@ -1,8 +0,0 @@
namespace phoenix {
struct pDesktop {
static Size size();
static Geometry workspace();
};
}

View File

@ -1,19 +0,0 @@
namespace phoenix {
string pFont::serif(unsigned size, string style) {
return "";
}
string pFont::sans(unsigned size, string style) {
return "";
}
string pFont::monospace(unsigned size, string style) {
return "";
}
Size pFont::size(string font, string text) {
return {0, 0};
}
}

View File

@ -1,10 +0,0 @@
namespace phoenix {
struct pFont {
static string serif(unsigned size, string style);
static string sans(unsigned size, string style);
static string monospace(unsigned size, string style);
static Size size(string font, string text);
};
}

View File

@ -1,14 +0,0 @@
namespace phoenix {
bool pKeyboard::pressed(Keyboard::Scancode scancode) {
return false;
}
vector<bool> pKeyboard::state() {
vector<bool> output;
output.resize((unsigned)Keyboard::Scancode::Limit);
for(auto& n : output) n = false;
return output;
}
}

View File

@ -1,8 +0,0 @@
namespace phoenix {
struct pKeyboard {
static bool pressed(Keyboard::Scancode scancode);
static vector<bool> state();
};
}

View File

@ -1,19 +0,0 @@
namespace phoenix {
MessageWindow::Response pMessageWindow::error(MessageWindow::State& state) {
return MessageWindow::Response::Ok;
}
MessageWindow::Response pMessageWindow::information(MessageWindow::State& state) {
return MessageWindow::Response::Ok;
}
MessageWindow::Response pMessageWindow::question(MessageWindow::State& state) {
return MessageWindow::Response::Ok;
}
MessageWindow::Response pMessageWindow::warning(MessageWindow::State& state) {
return MessageWindow::Response::Ok;
}
}

View File

@ -1,10 +0,0 @@
namespace phoenix {
struct pMessageWindow {
static MessageWindow::Response error(MessageWindow::State& state);
static MessageWindow::Response information(MessageWindow::State& state);
static MessageWindow::Response question(MessageWindow::State& state);
static MessageWindow::Response warning(MessageWindow::State& state);
};
}

View File

@ -1,15 +0,0 @@
namespace phoenix {
unsigned pMonitor::count() {
return 1;
}
Geometry pMonitor::geometry(unsigned monitor) {
return {0, 0, 0, 0};
}
unsigned pMonitor::primary() {
return 0;
}
}

View File

@ -1,9 +0,0 @@
namespace phoenix {
struct pMonitor {
static unsigned count();
static Geometry geometry(unsigned monitor);
static unsigned primary();
};
}

View File

@ -1,11 +0,0 @@
namespace phoenix {
Position pMouse::position() {
return {0, 0};
}
bool pMouse::pressed(Mouse::Button button) {
return false;
}
}

View File

@ -1,8 +0,0 @@
namespace phoenix {
struct pMouse {
static Position position();
static bool pressed(Mouse::Button button);
};
}

View File

@ -1,9 +0,0 @@
namespace phoenix {
void pObject::constructor() {
}
void pObject::destructor() {
}
}

View File

@ -1,14 +0,0 @@
namespace phoenix {
struct pObject {
Object& object;
bool locked;
pObject(Object& object) : object(object), locked(locked) {}
virtual ~pObject() {}
void constructor();
void destructor();
};
}

View File

@ -1,45 +0,0 @@
#include "platform.hpp"
#include "font.cpp"
#include "desktop.cpp"
#include "monitor.cpp"
#include "keyboard.cpp"
#include "mouse.cpp"
#include "browser-window.cpp"
#include "message-window.cpp"
#include "object.cpp"
#include "timer.cpp"
#include "window.cpp"
#include "popup-menu.cpp"
#include "action/action.cpp"
#include "action/menu.cpp"
#include "action/separator.cpp"
#include "action/item.cpp"
#include "action/check-item.cpp"
#include "action/radio-item.cpp"
#include "widget/widget.cpp"
#include "widget/button.cpp"
#include "widget/canvas.cpp"
#include "widget/check-button.cpp"
#include "widget/check-label.cpp"
#include "widget/combo-button.cpp"
#include "widget/console.cpp"
#include "widget/frame.cpp"
#include "widget/hex-edit.cpp"
#include "widget/horizontal-scroller.cpp"
#include "widget/horizontal-slider.cpp"
#include "widget/label.cpp"
#include "widget/line-edit.cpp"
#include "widget/list-view.cpp"
#include "widget/progress-bar.cpp"
#include "widget/radio-button.cpp"
#include "widget/radio-label.cpp"
#include "widget/tab-frame.cpp"
#include "widget/text-edit.cpp"
#include "widget/vertical-scroller.cpp"
#include "widget/vertical-slider.cpp"
#include "widget/viewport.cpp"
#include "application.cpp"

View File

@ -1,53 +0,0 @@
namespace phoenix {
struct pFont;
struct pWindow;
struct pMenu;
struct pLayout;
struct pWidget;
}
#include "font.hpp"
#include "desktop.hpp"
#include "monitor.hpp"
#include "keyboard.hpp"
#include "mouse.hpp"
#include "browser-window.hpp"
#include "message-window.hpp"
#include "object.hpp"
#include "timer.hpp"
#include "window.hpp"
#include "popup-menu.hpp"
#include "action/action.hpp"
#include "action/menu.hpp"
#include "action/separator.hpp"
#include "action/item.hpp"
#include "action/check-item.hpp"
#include "action/radio-item.hpp"
#include "widget/sizable.hpp"
#include "widget/layout.hpp"
#include "widget/widget.hpp"
#include "widget/button.hpp"
#include "widget/canvas.hpp"
#include "widget/check-button.hpp"
#include "widget/check-label.hpp"
#include "widget/combo-button.hpp"
#include "widget/console.hpp"
#include "widget/frame.hpp"
#include "widget/hex-edit.hpp"
#include "widget/horizontal-scroller.hpp"
#include "widget/horizontal-slider.hpp"
#include "widget/label.hpp"
#include "widget/line-edit.hpp"
#include "widget/list-view.hpp"
#include "widget/progress-bar.hpp"
#include "widget/radio-button.hpp"
#include "widget/radio-label.hpp"
#include "widget/tab-frame.hpp"
#include "widget/text-edit.hpp"
#include "widget/vertical-scroller.hpp"
#include "widget/vertical-slider.hpp"
#include "widget/viewport.hpp"
#include "application.hpp"

View File

@ -1,18 +0,0 @@
namespace phoenix {
void pPopupMenu::append(Action& action) {
}
void pPopupMenu::remove(Action& action) {
}
void pPopupMenu::setVisible() {
}
void pPopupMenu::constructor() {
}
void pPopupMenu::destructor() {
}
}

View File

@ -1,15 +0,0 @@
namespace phoenix {
struct pPopupMenu : public pObject {
PopupMenu& popupMenu;
void append(Action& action);
void remove(Action& action);
void setVisible();
pPopupMenu(PopupMenu& popupMenu) : pObject(popupMenu), popupMenu(popupMenu) {}
void constructor();
void destructor();
};
}

View File

@ -1,15 +0,0 @@
namespace phoenix {
void pTimer::setEnabled(bool enabled) {
}
void pTimer::setInterval(unsigned interval) {
}
void pTimer::constructor() {
}
void pTimer::destructor() {
}
}

View File

@ -1,14 +0,0 @@
namespace phoenix {
struct pTimer : public pObject {
Timer& timer;
void setEnabled(bool enabled);
void setInterval(unsigned interval);
pTimer(Timer& timer) : pObject(timer), timer(timer) {}
void constructor();
void destructor();
};
}

View File

@ -1,18 +0,0 @@
namespace phoenix {
void pButton::setBordered(bool bordered) {
}
void pButton::setImage(const image& image, Orientation orientation) {
}
void pButton::setText(string text) {
}
void pButton::constructor() {
}
void pButton::destructor() {
}
}

View File

@ -1,15 +0,0 @@
namespace phoenix {
struct pButton : public pWidget {
Button& button;
void setBordered(bool bordered);
void setImage(const image& image, Orientation orientation);
void setText(string text);
pButton(Button& button) : pWidget(button), button(button) {}
void constructor();
void destructor();
};
}

View File

@ -1,18 +0,0 @@
namespace phoenix {
void pCanvas::setDroppable(bool droppable) {
}
void pCanvas::setMode(Canvas::Mode mode) {
}
void pCanvas::setSize(Size size) {
}
void pCanvas::constructor() {
}
void pCanvas::destructor() {
}
}

View File

@ -1,15 +0,0 @@
namespace phoenix {
struct pCanvas : public pWidget {
Canvas& canvas;
void setDroppable(bool droppable);
void setMode(Canvas::Mode mode);
void setSize(Size size);
pCanvas(Canvas& canvas) : pWidget(canvas), canvas(canvas) {}
void constructor();
void destructor();
};
}

View File

@ -1,18 +0,0 @@
namespace phoenix {
void pCheckButton::setChecked(bool checked) {
}
void pCheckButton::setImage(const image& image, Orientation orientation) {
}
void pCheckButton::setText(string text) {
}
void pCheckButton::constructor() {
}
void pCheckButton::destructor() {
}
}

View File

@ -1,15 +0,0 @@
namespace phoenix {
struct pCheckButton : public pWidget {
CheckButton& checkButton;
void setChecked(bool checked);
void setImage(const image& image, Orientation orientation);
void setText(string text);
pCheckButton(CheckButton& checkButton) : pWidget(checkButton), checkButton(checkButton) {}
void constructor();
void destructor();
};
}

View File

@ -1,15 +0,0 @@
namespace phoenix {
void pCheckLabel::setChecked(bool checked) {
}
void pCheckLabel::setText(string text) {
}
void pCheckLabel::constructor() {
}
void pCheckLabel::destructor() {
}
}

View File

@ -1,14 +0,0 @@
namespace phoenix {
struct pCheckLabel : public pWidget {
CheckLabel& checkLabel;
void setChecked(bool checked);
void setText(string text);
pCheckLabel(CheckLabel& checkLabel) : pWidget(checkLabel), checkLabel(checkLabel) {}
void constructor();
void destructor();
};
}

View File

@ -1,24 +0,0 @@
namespace phoenix {
void pComboButton::append() {
}
void pComboButton::remove(unsigned selection) {
}
void pComboButton::reset() {
}
void pComboButton::setSelected(unsigned selection) {
}
void pComboButton::setText(unsigned selection, string text) {
}
void pComboButton::constructor() {
}
void pComboButton::destructor() {
}
}

View File

@ -1,17 +0,0 @@
namespace phoenix {
struct pComboButton : public pWidget {
ComboButton& comboButton;
void append();
void remove(unsigned selection);
void reset();
void setSelected(unsigned selection);
void setText(unsigned selection, string text);
pComboButton(ComboButton& comboButton) : pWidget(comboButton), comboButton(comboButton) {}
void constructor();
void destructor();
};
}

View File

@ -1,24 +0,0 @@
namespace phoenix {
void pConsole::print(string text) {
}
void pConsole::reset() {
}
void pConsole::setBackgroundColor(Color color) {
}
void pConsole::setForegroundColor(Color color) {
}
void pConsole::setPrompt(string prompt) {
}
void pConsole::constructor() {
}
void pConsole::destructor() {
}
}

View File

@ -1,17 +0,0 @@
namespace phoenix {
struct pConsole : public pWidget {
Console& console;
void print(string text);
void reset();
void setBackgroundColor(Color color);
void setForegroundColor(Color color);
void setPrompt(string prompt);
pConsole(Console& console) : pWidget(console), console(console) {}
void constructor();
void destructor();
};
}

Some files were not shown because too many files have changed in this diff Show More