mirror of https://github.com/bsnes-emu/bsnes.git
Update to v106r39 release.
byuu says: Changelog: - ruby/video: implement onUpdate() callback to signal when redraws are necessary - ruby/video/GLX,GLX2,XVideo,XShm: implement onUpdate() support - bsnes: implement Video::onUpdate() support to redraw Viewport icon as needed - bsnes: save RAM before ruby driver changes - sfc/sa1: clip signed multiplication to 32-bit [Jonas Quinn] - sfc/sa1: handle negative dividends in division [Jonas Quinn] - hiro/gtk3: a few improvements - bsnes: added empty stub video and audio settings panels - bsnes: restructured advanced settings panel - bsnes: experiment: input/hotkeys name column bolded and colored for increased visual distinction - bsnes: added save button to state manager
This commit is contained in:
parent
15b67922b3
commit
91bb781b73
|
@ -13,7 +13,7 @@ using namespace nall;
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
static const string Name = "higan";
|
||||||
static const string Version = "106.38";
|
static const string Version = "106.39";
|
||||||
static const string Author = "byuu";
|
static const string Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "https://byuu.org/";
|
static const string Website = "https://byuu.org/";
|
||||||
|
|
|
@ -415,19 +415,19 @@ auto SA1::writeIO(uint24 addr, uint8 data) -> void {
|
||||||
|
|
||||||
//(MAL) multiplicand / dividend low
|
//(MAL) multiplicand / dividend low
|
||||||
case 0x2251: {
|
case 0x2251: {
|
||||||
mmio.ma = (mmio.ma & 0xff00) | data;
|
mmio.ma.byte(0) = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//(MAH) multiplicand / dividend high
|
//(MAH) multiplicand / dividend high
|
||||||
case 0x2252: {
|
case 0x2252: {
|
||||||
mmio.ma = (data << 8) | (mmio.ma & 0x00ff);
|
mmio.ma.byte(1) = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//(MBL) multiplier / divisor low
|
//(MBL) multiplier / divisor low
|
||||||
case 0x2253: {
|
case 0x2253: {
|
||||||
mmio.mb = (mmio.mb & 0xff00) | data;
|
mmio.mb.byte(0) = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,21 +435,23 @@ auto SA1::writeIO(uint24 addr, uint8 data) -> void {
|
||||||
//multiplication / cumulative sum only resets MB
|
//multiplication / cumulative sum only resets MB
|
||||||
//division resets both MA and MB
|
//division resets both MA and MB
|
||||||
case 0x2254: {
|
case 0x2254: {
|
||||||
mmio.mb = (data << 8) | (mmio.mb & 0x00ff);
|
mmio.mb.byte(1) = data;
|
||||||
|
|
||||||
if(mmio.acm == 0) {
|
if(mmio.acm == 0) {
|
||||||
if(mmio.md == 0) {
|
if(mmio.md == 0) {
|
||||||
//signed multiplication
|
//signed multiplication
|
||||||
mmio.mr = (int16)mmio.ma * (int16)mmio.mb;
|
mmio.mr = (uint32)((int16)mmio.ma * (int16)mmio.mb);
|
||||||
mmio.mb = 0;
|
mmio.mb = 0;
|
||||||
} else {
|
} else {
|
||||||
//unsigned division
|
//unsigned division
|
||||||
if(mmio.mb == 0) {
|
if(mmio.mb == 0) {
|
||||||
mmio.mr = 0;
|
mmio.mr = 0;
|
||||||
} else {
|
} else {
|
||||||
int16 quotient = (int16)mmio.ma / (uint16)mmio.mb;
|
int16 dividend = mmio.ma;
|
||||||
uint16 remainder = (int16)mmio.ma % (uint16)mmio.mb;
|
uint16 divisor = mmio.mb;
|
||||||
mmio.mr = (remainder << 16) | quotient;
|
uint16 remainder = dividend >= 0 ? dividend % divisor : (dividend % divisor + divisor) % divisor;
|
||||||
|
uint16 quotient = (dividend - remainder) / divisor;
|
||||||
|
mmio.mr = remainder << 16 | quotient;
|
||||||
}
|
}
|
||||||
mmio.ma = 0;
|
mmio.ma = 0;
|
||||||
mmio.mb = 0;
|
mmio.mb = 0;
|
||||||
|
@ -457,8 +459,8 @@ auto SA1::writeIO(uint24 addr, uint8 data) -> void {
|
||||||
} else {
|
} else {
|
||||||
//sigma (accumulative multiplication)
|
//sigma (accumulative multiplication)
|
||||||
mmio.mr += (int16)mmio.ma * (int16)mmio.mb;
|
mmio.mr += (int16)mmio.ma * (int16)mmio.mb;
|
||||||
mmio.overflow = (mmio.mr >= (1ULL << 40));
|
mmio.overflow = mmio.mr >> 40;
|
||||||
mmio.mr &= (1ULL << 40) - 1;
|
mmio.mr = (uint40)mmio.mr;
|
||||||
mmio.mb = 0;
|
mmio.mb = 0;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -99,10 +99,12 @@ Presentation::Presentation() {
|
||||||
statusBar.setVisible(showStatusBar.checked());
|
statusBar.setVisible(showStatusBar.checked());
|
||||||
if(visible()) resizeWindow();
|
if(visible()) resizeWindow();
|
||||||
});
|
});
|
||||||
inputSettings.setText("Input ...").onActivate([&] { settingsWindow->show(0); });
|
videoSettings.setText("Video ...").onActivate([&] { settingsWindow->show(0); });
|
||||||
hotkeySettings.setText("Hotkeys ...").onActivate([&] { settingsWindow->show(1); });
|
audioSettings.setText("Audio ...").onActivate([&] { settingsWindow->show(1); });
|
||||||
pathSettings.setText("Paths ...").onActivate([&] { settingsWindow->show(2); });
|
inputSettings.setText("Input ...").onActivate([&] { settingsWindow->show(2); });
|
||||||
advancedSettings.setText("Advanced ...").onActivate([&] { settingsWindow->show(3); });
|
hotkeySettings.setText("Hotkeys ...").onActivate([&] { settingsWindow->show(3); });
|
||||||
|
pathSettings.setText("Paths ...").onActivate([&] { settingsWindow->show(4); });
|
||||||
|
advancedSettings.setText("Advanced ...").onActivate([&] { settingsWindow->show(5); });
|
||||||
|
|
||||||
toolsMenu.setText("Tools").setVisible(false);
|
toolsMenu.setText("Tools").setVisible(false);
|
||||||
saveState.setText("Save State");
|
saveState.setText("Save State");
|
||||||
|
@ -171,7 +173,7 @@ Presentation::Presentation() {
|
||||||
#if defined(PLATFORM_MACOS)
|
#if defined(PLATFORM_MACOS)
|
||||||
Application::Cocoa::onAbout([&] { about.doActivate(); });
|
Application::Cocoa::onAbout([&] { about.doActivate(); });
|
||||||
Application::Cocoa::onActivate([&] { setFocused(); });
|
Application::Cocoa::onActivate([&] { setFocused(); });
|
||||||
Application::Cocoa::onPreferences([&] { settingsWindow->show(0); });
|
Application::Cocoa::onPreferences([&] { settingsWindow->show(2); });
|
||||||
Application::Cocoa::onQuit([&] { doClose(); });
|
Application::Cocoa::onQuit([&] { doClose(); });
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,8 @@ struct Presentation : Window {
|
||||||
MenuCheckItem muteAudio{&settingsMenu};
|
MenuCheckItem muteAudio{&settingsMenu};
|
||||||
MenuCheckItem showStatusBar{&settingsMenu};
|
MenuCheckItem showStatusBar{&settingsMenu};
|
||||||
MenuSeparator settingsSeparator{&settingsMenu};
|
MenuSeparator settingsSeparator{&settingsMenu};
|
||||||
|
MenuItem videoSettings{&settingsMenu};
|
||||||
|
MenuItem audioSettings{&settingsMenu};
|
||||||
MenuItem inputSettings{&settingsMenu};
|
MenuItem inputSettings{&settingsMenu};
|
||||||
MenuItem hotkeySettings{&settingsMenu};
|
MenuItem hotkeySettings{&settingsMenu};
|
||||||
MenuItem pathSettings{&settingsMenu};
|
MenuItem pathSettings{&settingsMenu};
|
||||||
|
|
|
@ -77,6 +77,7 @@ Program::Program(string_vector arguments) {
|
||||||
|
|
||||||
auto Program::main() -> void {
|
auto Program::main() -> void {
|
||||||
updateMessage();
|
updateMessage();
|
||||||
|
video->poll();
|
||||||
inputManager->poll();
|
inputManager->poll();
|
||||||
inputManager->pollHotkeys();
|
inputManager->pollHotkeys();
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,10 @@ auto Program::initializeVideoDriver() -> void {
|
||||||
presentation->clearViewport();
|
presentation->clearViewport();
|
||||||
updateVideoShader();
|
updateVideoShader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
video->onUpdate([&](uint width, uint height) {
|
||||||
|
if(!emulator->loaded()) presentation->clearViewport();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Program::initializeAudioDriver() -> void {
|
auto Program::initializeAudioDriver() -> void {
|
||||||
|
|
|
@ -3,8 +3,9 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
setText("Advanced");
|
setText("Advanced");
|
||||||
|
|
||||||
layout.setMargin(5);
|
layout.setMargin(5);
|
||||||
|
driversLabel.setText("Drivers").setFont(Font().setBold());
|
||||||
|
|
||||||
videoDriverLabel.setText("Video Driver:");
|
videoDriverLabel.setText("Video:");
|
||||||
for(auto& driver : Video::availableDrivers()) {
|
for(auto& driver : Video::availableDrivers()) {
|
||||||
ComboButtonItem item;
|
ComboButtonItem item;
|
||||||
item.setText(driver);
|
item.setText(driver);
|
||||||
|
@ -12,10 +13,6 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
if(video->driver() == driver) item.setSelected();
|
if(video->driver() == driver) item.setSelected();
|
||||||
}
|
}
|
||||||
videoDriverOption.onChange([&] {
|
videoDriverOption.onChange([&] {
|
||||||
auto item = videoDriverOption.selected();
|
|
||||||
videoDriverChange.setEnabled(video->driver() != item.text());
|
|
||||||
});
|
|
||||||
videoDriverChange.setText("Change").setEnabled(false).onActivate([&] {
|
|
||||||
auto item = videoDriverOption.selected();
|
auto item = videoDriverOption.selected();
|
||||||
settings["Video/Driver"].setValue(item.text());
|
settings["Video/Driver"].setValue(item.text());
|
||||||
if(!emulator->loaded() || item.text() == "None" || MessageDialog(
|
if(!emulator->loaded() || item.text() == "None" || MessageDialog(
|
||||||
|
@ -23,6 +20,7 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
"It is highly recommended you unload your game first to avoid data loss.\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?"
|
"Do you wish to proceed with the video driver change now anyway?"
|
||||||
).setParent(*settingsWindow).question() == "Yes") {
|
).setParent(*settingsWindow).question() == "Yes") {
|
||||||
|
program->save();
|
||||||
program->saveRecoveryState();
|
program->saveRecoveryState();
|
||||||
settings["Crashed"].setValue(true);
|
settings["Crashed"].setValue(true);
|
||||||
settings.save();
|
settings.save();
|
||||||
|
@ -39,11 +37,10 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
}
|
}
|
||||||
settings["Crashed"].setValue(false);
|
settings["Crashed"].setValue(false);
|
||||||
settings.save();
|
settings.save();
|
||||||
videoDriverChange.setEnabled(false);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
audioDriverLabel.setText("Audio Driver:");
|
audioDriverLabel.setText("Audio:");
|
||||||
for(auto& driver : Audio::availableDrivers()) {
|
for(auto& driver : Audio::availableDrivers()) {
|
||||||
ComboButtonItem item;
|
ComboButtonItem item;
|
||||||
item.setText(driver);
|
item.setText(driver);
|
||||||
|
@ -51,10 +48,6 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
if(audio->driver() == driver) item.setSelected();
|
if(audio->driver() == driver) item.setSelected();
|
||||||
}
|
}
|
||||||
audioDriverOption.onChange([&] {
|
audioDriverOption.onChange([&] {
|
||||||
auto item = audioDriverOption.selected();
|
|
||||||
audioDriverChange.setEnabled(audio->driver() != item.text());
|
|
||||||
});
|
|
||||||
audioDriverChange.setText("Change").setEnabled(false).onActivate([&] {
|
|
||||||
auto item = audioDriverOption.selected();
|
auto item = audioDriverOption.selected();
|
||||||
settings["Audio/Driver"].setValue(item.text());
|
settings["Audio/Driver"].setValue(item.text());
|
||||||
if(!emulator->loaded() || item.text() == "None" || MessageDialog(
|
if(!emulator->loaded() || item.text() == "None" || MessageDialog(
|
||||||
|
@ -62,6 +55,7 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
"It is highly recommended you unload your game first to avoid data loss.\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?"
|
"Do you wish to proceed with the audio driver change now anyway?"
|
||||||
).setParent(*settingsWindow).question() == "Yes") {
|
).setParent(*settingsWindow).question() == "Yes") {
|
||||||
|
program->save();
|
||||||
program->saveRecoveryState();
|
program->saveRecoveryState();
|
||||||
settings["Crashed"].setValue(true);
|
settings["Crashed"].setValue(true);
|
||||||
settings.save();
|
settings.save();
|
||||||
|
@ -78,11 +72,10 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
}
|
}
|
||||||
settings["Crashed"].setValue(false);
|
settings["Crashed"].setValue(false);
|
||||||
settings.save();
|
settings.save();
|
||||||
audioDriverChange.setEnabled(false);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
inputDriverLabel.setText("Input Driver:");
|
inputDriverLabel.setText("Input:");
|
||||||
for(auto& driver : Input::availableDrivers()) {
|
for(auto& driver : Input::availableDrivers()) {
|
||||||
ComboButtonItem item;
|
ComboButtonItem item;
|
||||||
item.setText(driver);
|
item.setText(driver);
|
||||||
|
@ -90,10 +83,6 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
if(input->driver() == driver) item.setSelected();
|
if(input->driver() == driver) item.setSelected();
|
||||||
}
|
}
|
||||||
inputDriverOption.onChange([&] {
|
inputDriverOption.onChange([&] {
|
||||||
auto item = inputDriverOption.selected();
|
|
||||||
inputDriverChange.setEnabled(input->driver() != item.text());
|
|
||||||
});
|
|
||||||
inputDriverChange.setText("Change").setEnabled(false).onActivate([&] {
|
|
||||||
auto item = inputDriverOption.selected();
|
auto item = inputDriverOption.selected();
|
||||||
settings["Input/Driver"].setValue(item.text());
|
settings["Input/Driver"].setValue(item.text());
|
||||||
if(!emulator->loaded() || item.text() == "None" || MessageDialog(
|
if(!emulator->loaded() || item.text() == "None" || MessageDialog(
|
||||||
|
@ -101,6 +90,7 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
"It is highly recommended you unload your game first to avoid data loss.\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?"
|
"Do you wish to proceed with the input driver change now anyway?"
|
||||||
).setParent(*settingsWindow).question() == "Yes") {
|
).setParent(*settingsWindow).question() == "Yes") {
|
||||||
|
program->save();
|
||||||
program->saveRecoveryState();
|
program->saveRecoveryState();
|
||||||
settings["Crashed"].setValue(true);
|
settings["Crashed"].setValue(true);
|
||||||
settings.save();
|
settings.save();
|
||||||
|
@ -117,7 +107,6 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
}
|
}
|
||||||
settings["Crashed"].setValue(false);
|
settings["Crashed"].setValue(false);
|
||||||
settings.save();
|
settings.save();
|
||||||
inputDriverChange.setEnabled(false);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
|
setIcon(Icon::Device::Speaker);
|
||||||
|
setText("Audio");
|
||||||
|
}
|
|
@ -34,7 +34,7 @@ auto HotkeySettings::reloadMappings() -> void {
|
||||||
);
|
);
|
||||||
for(auto& hotkey : inputManager->hotkeys) {
|
for(auto& hotkey : inputManager->hotkeys) {
|
||||||
mappingList.append(TableViewItem()
|
mappingList.append(TableViewItem()
|
||||||
.append(TableViewCell().setText(hotkey.name))
|
.append(TableViewCell().setText(hotkey.name).setFont(Font().setBold()).setBackgroundColor({240, 240, 255}))
|
||||||
.append(TableViewCell())
|
.append(TableViewCell())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ auto InputSettings::reloadMappings() -> void {
|
||||||
);
|
);
|
||||||
for(auto& mapping : activeDevice().mappings) {
|
for(auto& mapping : activeDevice().mappings) {
|
||||||
mappingList.append(TableViewItem()
|
mappingList.append(TableViewItem()
|
||||||
.append(TableViewCell().setText(mapping.name))
|
.append(TableViewCell().setText(mapping.name).setFont(Font().setBold()).setBackgroundColor({240, 240, 255}))
|
||||||
.append(TableViewCell())
|
.append(TableViewCell())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,7 @@ auto InputSettings::refreshMappings() -> void {
|
||||||
for(auto& mapping : activeDevice().mappings) {
|
for(auto& mapping : activeDevice().mappings) {
|
||||||
mappingList.item(index++).cell(1).setText(mapping.displayName());
|
mappingList.item(index++).cell(1).setText(mapping.displayName());
|
||||||
}
|
}
|
||||||
|
Application::processEvents();
|
||||||
mappingList.resizeColumns();
|
mappingList.resizeColumns();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "../bsnes.hpp"
|
#include "../bsnes.hpp"
|
||||||
|
#include "video.cpp"
|
||||||
|
#include "audio.cpp"
|
||||||
#include "input.cpp"
|
#include "input.cpp"
|
||||||
#include "hotkeys.cpp"
|
#include "hotkeys.cpp"
|
||||||
#include "paths.cpp"
|
#include "paths.cpp"
|
||||||
|
|
|
@ -3,6 +3,14 @@ struct Settings : Markup::Node {
|
||||||
auto save() -> void;
|
auto save() -> void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct VideoSettings : TabFrameItem {
|
||||||
|
VideoSettings(TabFrame*);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AudioSettings : TabFrameItem {
|
||||||
|
AudioSettings(TabFrame*);
|
||||||
|
};
|
||||||
|
|
||||||
struct InputSettings : TabFrameItem {
|
struct InputSettings : TabFrameItem {
|
||||||
InputSettings(TabFrame*);
|
InputSettings(TabFrame*);
|
||||||
auto updateControls() -> void;
|
auto updateControls() -> void;
|
||||||
|
@ -99,18 +107,14 @@ struct AdvancedSettings : TabFrameItem {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VerticalLayout layout{this};
|
VerticalLayout layout{this};
|
||||||
HorizontalLayout videoDriverLayout{&layout, Size{~0, 0}};
|
Label driversLabel{&layout, Size{~0, 0}, 2};
|
||||||
Label videoDriverLabel{&videoDriverLayout, Size{75, 0}};
|
HorizontalLayout driverLayout{&layout, Size{~0, 0}};
|
||||||
ComboButton videoDriverOption{&videoDriverLayout, Size{~0, 0}};
|
Label videoDriverLabel{&driverLayout, Size{0, 0}};
|
||||||
Button videoDriverChange{&videoDriverLayout, Size{80, 0}};
|
ComboButton videoDriverOption{&driverLayout, Size{~0, 0}};
|
||||||
HorizontalLayout audioDriverLayout{&layout, Size{~0, 0}};
|
Label audioDriverLabel{&driverLayout, Size{0, 0}};
|
||||||
Label audioDriverLabel{&audioDriverLayout, Size{75, 0}};
|
ComboButton audioDriverOption{&driverLayout, Size{~0, 0}};
|
||||||
ComboButton audioDriverOption{&audioDriverLayout, Size{~0, 0}};
|
Label inputDriverLabel{&driverLayout, Size{0, 0}};
|
||||||
Button audioDriverChange{&audioDriverLayout, Size{80, 0}};
|
ComboButton inputDriverOption{&driverLayout, Size{~0, 0}};
|
||||||
HorizontalLayout inputDriverLayout{&layout, Size{~0, 0}};
|
|
||||||
Label inputDriverLabel{&inputDriverLayout, Size{75, 0}};
|
|
||||||
ComboButton inputDriverOption{&inputDriverLayout, Size{~0, 0}};
|
|
||||||
Button inputDriverChange{&inputDriverLayout, Size{80, 0}};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SettingsWindow : Window {
|
struct SettingsWindow : Window {
|
||||||
|
@ -121,6 +125,8 @@ struct SettingsWindow : Window {
|
||||||
public:
|
public:
|
||||||
VerticalLayout layout{this};
|
VerticalLayout layout{this};
|
||||||
TabFrame panel{&layout, Size{~0, ~0}};
|
TabFrame panel{&layout, Size{~0, ~0}};
|
||||||
|
VideoSettings video{&panel};
|
||||||
|
AudioSettings audio{&panel};
|
||||||
InputSettings input{&panel};
|
InputSettings input{&panel};
|
||||||
HotkeySettings hotkeys{&panel};
|
HotkeySettings hotkeys{&panel};
|
||||||
PathSettings paths{&panel};
|
PathSettings paths{&panel};
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
VideoSettings::VideoSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
|
setIcon(Icon::Device::Display);
|
||||||
|
setText("Video");
|
||||||
|
}
|
|
@ -75,6 +75,7 @@ StateManager::StateManager(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
stateList.onChange([&] {
|
stateList.onChange([&] {
|
||||||
auto batched = stateList.batched();
|
auto batched = stateList.batched();
|
||||||
loadButton.setEnabled(batched.size() == 1);
|
loadButton.setEnabled(batched.size() == 1);
|
||||||
|
saveButton.setEnabled(batched.size() == 1);
|
||||||
editButton.setEnabled(batched.size() == 1);
|
editButton.setEnabled(batched.size() == 1);
|
||||||
removeButton.setEnabled(batched.size() >= 1);
|
removeButton.setEnabled(batched.size() >= 1);
|
||||||
});
|
});
|
||||||
|
@ -84,6 +85,11 @@ StateManager::StateManager(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
saveButton.setText("Save").onActivate([&] {
|
saveButton.setText("Save").onActivate([&] {
|
||||||
|
if(auto item = stateList.selected()) {
|
||||||
|
program->saveState(item.cell(0).text());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
addButton.setText("Add").onActivate([&] {
|
||||||
stateWindow->show();
|
stateWindow->show();
|
||||||
});
|
});
|
||||||
editButton.setText("Edit").onActivate([&] {
|
editButton.setText("Edit").onActivate([&] {
|
||||||
|
|
|
@ -100,8 +100,9 @@ public:
|
||||||
TableView stateList{&layout, Size{~0, ~0}};
|
TableView stateList{&layout, Size{~0, ~0}};
|
||||||
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
||||||
Button loadButton{&controlLayout, Size{80, 0}};
|
Button loadButton{&controlLayout, Size{80, 0}};
|
||||||
Widget spacer{&controlLayout, Size{~0, 0}};
|
|
||||||
Button saveButton{&controlLayout, Size{80, 0}};
|
Button saveButton{&controlLayout, Size{80, 0}};
|
||||||
|
Widget spacer{&controlLayout, Size{~0, 0}};
|
||||||
|
Button addButton{&controlLayout, Size{80, 0}};
|
||||||
Button editButton{&controlLayout, Size{80, 0}};
|
Button editButton{&controlLayout, Size{80, 0}};
|
||||||
Button removeButton{&controlLayout, Size{80, 0}};
|
Button removeButton{&controlLayout, Size{80, 0}};
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,6 +10,11 @@ GtkSelectionData* data, unsigned type, unsigned timestamp, pCanvas* p) -> void {
|
||||||
p->self().doDrop(paths);
|
p->self().doDrop(paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static auto Canvas_draw(GtkWidget* widget, cairo_t* context, pCanvas* p) -> signed {
|
||||||
|
p->_onDraw(context);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static auto Canvas_expose(GtkWidget* widget, GdkEventExpose* event, pCanvas* p) -> signed {
|
static auto Canvas_expose(GtkWidget* widget, GdkEventExpose* event, pCanvas* p) -> signed {
|
||||||
p->_onExpose(event);
|
p->_onExpose(event);
|
||||||
return true;
|
return true;
|
||||||
|
@ -59,7 +64,11 @@ auto pCanvas::construct() -> void {
|
||||||
g_signal_connect(G_OBJECT(gtkWidget), "button-press-event", G_CALLBACK(Canvas_mousePress), (gpointer)this);
|
g_signal_connect(G_OBJECT(gtkWidget), "button-press-event", G_CALLBACK(Canvas_mousePress), (gpointer)this);
|
||||||
g_signal_connect(G_OBJECT(gtkWidget), "button-release-event", G_CALLBACK(Canvas_mouseRelease), (gpointer)this);
|
g_signal_connect(G_OBJECT(gtkWidget), "button-release-event", G_CALLBACK(Canvas_mouseRelease), (gpointer)this);
|
||||||
g_signal_connect(G_OBJECT(gtkWidget), "drag-data-received", G_CALLBACK(Canvas_drop), (gpointer)this);
|
g_signal_connect(G_OBJECT(gtkWidget), "drag-data-received", G_CALLBACK(Canvas_drop), (gpointer)this);
|
||||||
|
#if HIRO_GTK==2
|
||||||
g_signal_connect(G_OBJECT(gtkWidget), "expose-event", G_CALLBACK(Canvas_expose), (gpointer)this);
|
g_signal_connect(G_OBJECT(gtkWidget), "expose-event", G_CALLBACK(Canvas_expose), (gpointer)this);
|
||||||
|
#elif HIRO_GTK==3
|
||||||
|
g_signal_connect(G_OBJECT(gtkWidget), "draw", G_CALLBACK(Canvas_draw), (gpointer)this);
|
||||||
|
#endif
|
||||||
g_signal_connect(G_OBJECT(gtkWidget), "leave-notify-event", G_CALLBACK(Canvas_mouseLeave), (gpointer)this);
|
g_signal_connect(G_OBJECT(gtkWidget), "leave-notify-event", G_CALLBACK(Canvas_mouseLeave), (gpointer)this);
|
||||||
g_signal_connect(G_OBJECT(gtkWidget), "motion-notify-event", G_CALLBACK(Canvas_mouseMove), (gpointer)this);
|
g_signal_connect(G_OBJECT(gtkWidget), "motion-notify-event", G_CALLBACK(Canvas_mouseMove), (gpointer)this);
|
||||||
|
|
||||||
|
@ -106,7 +115,36 @@ auto pCanvas::update() -> void {
|
||||||
_redraw();
|
_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto pCanvas::_onDraw(cairo_t* context) -> void {
|
||||||
|
#if HIRO_GTK==3
|
||||||
|
int sx = 0, sy = 0, dx = 0, dy = 0;
|
||||||
|
int width = surfaceWidth, height = surfaceHeight;
|
||||||
|
auto geometry = pSizable::state().geometry;
|
||||||
|
|
||||||
|
if(width <= geometry.width()) {
|
||||||
|
sx = 0;
|
||||||
|
dx = (geometry.width() - width) / 2;
|
||||||
|
} else {
|
||||||
|
sx = (width - geometry.width()) / 2;
|
||||||
|
dx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(height <= geometry.height()) {
|
||||||
|
sy = 0;
|
||||||
|
dy = (geometry.height() - height) / 2;
|
||||||
|
} else {
|
||||||
|
sy = (height - geometry.height()) / 2;
|
||||||
|
dy = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: support non-zero sx,sy
|
||||||
|
gdk_cairo_set_source_pixbuf(context, surface, dx, dy);
|
||||||
|
cairo_paint(context);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
auto pCanvas::_onExpose(GdkEventExpose* expose) -> void {
|
auto pCanvas::_onExpose(GdkEventExpose* expose) -> void {
|
||||||
|
#if HIRO_GTK==2
|
||||||
if(surface == nullptr) return;
|
if(surface == nullptr) return;
|
||||||
|
|
||||||
int sx = 0, sy = 0, dx = 0, dy = 0;
|
int sx = 0, sy = 0, dx = 0, dy = 0;
|
||||||
|
@ -132,10 +170,7 @@ auto pCanvas::_onExpose(GdkEventExpose* expose) -> void {
|
||||||
height = geometry.height();
|
height = geometry.height();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if HIRO_GTK==2
|
|
||||||
gdk_draw_pixbuf(gtk_widget_get_window(gtkWidget), nullptr, surface, sx, sy, dx, dy, width, height, GDK_RGB_DITHER_NONE, 0, 0);
|
gdk_draw_pixbuf(gtk_widget_get_window(gtkWidget), nullptr, surface, sx, sy, dx, dy, width, height, GDK_RGB_DITHER_NONE, 0, 0);
|
||||||
#elif HIRO_GTK==3
|
|
||||||
//TODO: use cairo here, but how? no examples show to use sx, sy
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ struct pCanvas : pWidget {
|
||||||
auto setIcon(const image& icon) -> void;
|
auto setIcon(const image& icon) -> void;
|
||||||
auto update() -> void;
|
auto update() -> void;
|
||||||
|
|
||||||
|
auto _onDraw(cairo_t* context) -> void;
|
||||||
auto _onExpose(GdkEventExpose* event) -> void;
|
auto _onExpose(GdkEventExpose* event) -> void;
|
||||||
auto _rasterize() -> void;
|
auto _rasterize() -> void;
|
||||||
auto _redraw() -> void;
|
auto _redraw() -> void;
|
||||||
|
|
|
@ -10,6 +10,10 @@ GtkSelectionData* data, unsigned type, unsigned timestamp, pViewport* p) -> void
|
||||||
p->self().doDrop(paths);
|
p->self().doDrop(paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static auto Viewport_draw(GtkWidget* widget, cairo_t* context, pViewport* p) -> signed {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static auto Viewport_expose(GtkWidget* widget, GdkEventExpose* event) -> signed {
|
static auto Viewport_expose(GtkWidget* widget, GdkEventExpose* event) -> signed {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -59,7 +63,11 @@ auto pViewport::construct() -> void {
|
||||||
g_signal_connect(G_OBJECT(gtkWidget), "button-press-event", G_CALLBACK(Viewport_mousePress), (gpointer)this);
|
g_signal_connect(G_OBJECT(gtkWidget), "button-press-event", G_CALLBACK(Viewport_mousePress), (gpointer)this);
|
||||||
g_signal_connect(G_OBJECT(gtkWidget), "button-release-event", G_CALLBACK(Viewport_mouseRelease), (gpointer)this);
|
g_signal_connect(G_OBJECT(gtkWidget), "button-release-event", G_CALLBACK(Viewport_mouseRelease), (gpointer)this);
|
||||||
g_signal_connect(G_OBJECT(gtkWidget), "drag-data-received", G_CALLBACK(Viewport_dropEvent), (gpointer)this);
|
g_signal_connect(G_OBJECT(gtkWidget), "drag-data-received", G_CALLBACK(Viewport_dropEvent), (gpointer)this);
|
||||||
|
#if HIRO_GTK==2
|
||||||
g_signal_connect(G_OBJECT(gtkWidget), "expose-event", G_CALLBACK(Viewport_expose), (gpointer)this);
|
g_signal_connect(G_OBJECT(gtkWidget), "expose-event", G_CALLBACK(Viewport_expose), (gpointer)this);
|
||||||
|
#elif HIRO_GTK==3
|
||||||
|
g_signal_connect(G_OBJECT(gtkWidget), "draw", G_CALLBACK(Viewport_draw), (gpointer)this);
|
||||||
|
#endif
|
||||||
g_signal_connect(G_OBJECT(gtkWidget), "leave-notify-event", G_CALLBACK(Viewport_mouseLeave), (gpointer)this);
|
g_signal_connect(G_OBJECT(gtkWidget), "leave-notify-event", G_CALLBACK(Viewport_mouseLeave), (gpointer)this);
|
||||||
g_signal_connect(G_OBJECT(gtkWidget), "motion-notify-event", G_CALLBACK(Viewport_mouseMove), (gpointer)this);
|
g_signal_connect(G_OBJECT(gtkWidget), "motion-notify-event", G_CALLBACK(Viewport_mouseMove), (gpointer)this);
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,6 @@ static auto Window_draw(GtkWidget* widget, cairo_t* context, pWindow* p) -> sign
|
||||||
|
|
||||||
cairo_set_operator(context, CAIRO_OPERATOR_SOURCE);
|
cairo_set_operator(context, CAIRO_OPERATOR_SOURCE);
|
||||||
cairo_paint(context);
|
cairo_paint(context);
|
||||||
cairo_destroy(context);
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +38,8 @@ static auto Window_draw(GtkWidget* widget, cairo_t* context, pWindow* p) -> sign
|
||||||
static auto Window_expose(GtkWidget* widget, GdkEvent* event, pWindow* p) -> signed {
|
static auto Window_expose(GtkWidget* widget, GdkEvent* event, pWindow* p) -> signed {
|
||||||
if(auto color = p->state().backgroundColor) {
|
if(auto color = p->state().backgroundColor) {
|
||||||
cairo_t* context = gdk_cairo_create(gtk_widget_get_window(widget));
|
cairo_t* context = gdk_cairo_create(gtk_widget_get_window(widget));
|
||||||
return Window_draw(widget, context, p);
|
Window_draw(widget, context, p);
|
||||||
|
cairo_destroy(context);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,9 +33,16 @@ struct Video {
|
||||||
virtual auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { return false; }
|
virtual auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { return false; }
|
||||||
virtual auto unlock() -> void {}
|
virtual auto unlock() -> void {}
|
||||||
virtual auto output() -> void {}
|
virtual auto output() -> void {}
|
||||||
|
virtual auto poll() -> void {}
|
||||||
|
|
||||||
|
auto onUpdate(const nall::function<void (uint, uint)>& callback) { _onUpdate = callback; }
|
||||||
|
auto doUpdate(uint width, uint height) -> void {
|
||||||
|
if(_onUpdate) return _onUpdate(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
nall::string _driver;
|
nall::string _driver;
|
||||||
|
nall::function<void (uint, uint)> _onUpdate;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Audio {
|
struct Audio {
|
||||||
|
@ -100,7 +107,7 @@ struct Input {
|
||||||
|
|
||||||
auto onChange(const nall::function<void (nall::shared_pointer<nall::HID::Device>, uint, uint, int16_t, int16_t)>& callback) { _onChange = callback; }
|
auto onChange(const nall::function<void (nall::shared_pointer<nall::HID::Device>, uint, uint, int16_t, int16_t)>& callback) { _onChange = callback; }
|
||||||
auto doChange(nall::shared_pointer<nall::HID::Device> device, uint group, uint input, int16_t oldValue, int16_t newValue) -> void {
|
auto doChange(nall::shared_pointer<nall::HID::Device> device, uint group, uint input, int16_t oldValue, int16_t newValue) -> void {
|
||||||
if(_onChange) _onChange(device, group, input, oldValue, newValue);
|
if(_onChange) return _onChange(device, group, input, oldValue, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -91,6 +91,18 @@ struct VideoGLX : Video, OpenGL {
|
||||||
if(_doubleBuffer) glXSwapBuffers(_display, _glXWindow);
|
if(_doubleBuffer) glXSwapBuffers(_display, _glXWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto poll() -> void {
|
||||||
|
while(XPending(_display)) {
|
||||||
|
XEvent event;
|
||||||
|
XNextEvent(_display, &event);
|
||||||
|
if(event.type == Expose) {
|
||||||
|
XWindowAttributes attributes;
|
||||||
|
XGetWindowAttributes(_display, _window, &attributes);
|
||||||
|
doUpdate(attributes.width, attributes.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
auto initialize() -> bool {
|
auto initialize() -> bool {
|
||||||
terminate();
|
terminate();
|
||||||
|
@ -136,6 +148,7 @@ private:
|
||||||
/* x = */ 0, /* y = */ 0, windowAttributes.width, windowAttributes.height,
|
/* x = */ 0, /* y = */ 0, windowAttributes.width, windowAttributes.height,
|
||||||
/* border_width = */ 0, vi->depth, InputOutput, vi->visual,
|
/* border_width = */ 0, vi->depth, InputOutput, vi->visual,
|
||||||
CWColormap | CWBorderPixel, &attributes);
|
CWColormap | CWBorderPixel, &attributes);
|
||||||
|
XSelectInput(_display, _window, ExposureMask);
|
||||||
XSetWindowBackground(_display, _window, /* color = */ 0);
|
XSetWindowBackground(_display, _window, /* color = */ 0);
|
||||||
XMapWindow(_display, _window);
|
XMapWindow(_display, _window);
|
||||||
XFlush(_display);
|
XFlush(_display);
|
||||||
|
|
|
@ -111,6 +111,18 @@ struct VideoGLX2 : Video {
|
||||||
if(_isDoubleBuffered) glXSwapBuffers(_display, _glXWindow);
|
if(_isDoubleBuffered) glXSwapBuffers(_display, _glXWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto poll() -> void {
|
||||||
|
while(XPending(_display)) {
|
||||||
|
XEvent event;
|
||||||
|
XNextEvent(_display, &event);
|
||||||
|
if(event.type == Expose) {
|
||||||
|
XWindowAttributes attributes;
|
||||||
|
XGetWindowAttributes(_display, _window, &attributes);
|
||||||
|
doUpdate(attributes.width, attributes.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
auto initialize() -> bool {
|
auto initialize() -> bool {
|
||||||
terminate();
|
terminate();
|
||||||
|
@ -147,6 +159,7 @@ private:
|
||||||
attributes.border_pixel = 0;
|
attributes.border_pixel = 0;
|
||||||
_window = XCreateWindow(_display, (Window)_context, 0, 0, windowAttributes.width, windowAttributes.height,
|
_window = XCreateWindow(_display, (Window)_context, 0, 0, windowAttributes.width, windowAttributes.height,
|
||||||
0, vi->depth, InputOutput, vi->visual, CWColormap | CWBorderPixel, &attributes);
|
0, vi->depth, InputOutput, vi->visual, CWColormap | CWBorderPixel, &attributes);
|
||||||
|
XSelectInput(_display, _window, ExposureMask);
|
||||||
XSetWindowBackground(_display, _window, 0);
|
XSetWindowBackground(_display, _window, 0);
|
||||||
XMapWindow(_display, _window);
|
XMapWindow(_display, _window);
|
||||||
XFlush(_display);
|
XFlush(_display);
|
||||||
|
|
|
@ -88,12 +88,24 @@ struct VideoXShm : Video {
|
||||||
XFlush(_display);
|
XFlush(_display);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto poll() -> void override {
|
||||||
|
while(XPending(_display)) {
|
||||||
|
XEvent event;
|
||||||
|
XNextEvent(_display, &event);
|
||||||
|
if(event.type == Expose) {
|
||||||
|
XWindowAttributes attributes;
|
||||||
|
XGetWindowAttributes(_display, _window, &attributes);
|
||||||
|
doUpdate(attributes.width, attributes.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
auto initialize() -> bool {
|
auto initialize() -> bool {
|
||||||
terminate();
|
terminate();
|
||||||
if(!_context) return false;
|
if(!_context) return false;
|
||||||
|
|
||||||
_display = XOpenDisplay(0);
|
_display = XOpenDisplay(nullptr);
|
||||||
_screen = DefaultScreen(_display);
|
_screen = DefaultScreen(_display);
|
||||||
|
|
||||||
XWindowAttributes getAttributes;
|
XWindowAttributes getAttributes;
|
||||||
|
@ -109,12 +121,12 @@ private:
|
||||||
|
|
||||||
XSetWindowAttributes setAttributes = {};
|
XSetWindowAttributes setAttributes = {};
|
||||||
setAttributes.border_pixel = 0;
|
setAttributes.border_pixel = 0;
|
||||||
setAttributes.event_mask = ExposureMask;
|
|
||||||
_window = XCreateWindow(_display, (Window)_context,
|
_window = XCreateWindow(_display, (Window)_context,
|
||||||
0, 0, 256, 256, 0,
|
0, 0, 256, 256, 0,
|
||||||
getAttributes.depth, InputOutput, getAttributes.visual,
|
getAttributes.depth, InputOutput, getAttributes.visual,
|
||||||
CWBorderPixel, &setAttributes
|
CWBorderPixel, &setAttributes
|
||||||
);
|
);
|
||||||
|
XSelectInput(_display, _window, ExposureMask);
|
||||||
XSetWindowBackground(_display, _window, 0);
|
XSetWindowBackground(_display, _window, 0);
|
||||||
XMapWindow(_display, _window);
|
XMapWindow(_display, _window);
|
||||||
XFlush(_display);
|
XFlush(_display);
|
||||||
|
|
|
@ -91,6 +91,18 @@ struct VideoXVideo : Video {
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto poll() -> void {
|
||||||
|
while(XPending(_display)) {
|
||||||
|
XEvent event;
|
||||||
|
XNextEvent(_display, &event);
|
||||||
|
if(event.type == Expose) {
|
||||||
|
XWindowAttributes attributes;
|
||||||
|
XGetWindowAttributes(_display, _window, &attributes);
|
||||||
|
doUpdate(attributes.width, attributes.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
auto initialize() -> bool {
|
auto initialize() -> bool {
|
||||||
terminate();
|
terminate();
|
||||||
|
@ -148,11 +160,11 @@ private:
|
||||||
XSetWindowAttributes attributes;
|
XSetWindowAttributes attributes;
|
||||||
attributes.colormap = _colormap;
|
attributes.colormap = _colormap;
|
||||||
attributes.border_pixel = 0;
|
attributes.border_pixel = 0;
|
||||||
attributes.event_mask = StructureNotifyMask;
|
|
||||||
_window = XCreateWindow(_display, /* parent = */ (Window)_context,
|
_window = XCreateWindow(_display, /* parent = */ (Window)_context,
|
||||||
/* x = */ 0, /* y = */ 0, window_attributes.width, window_attributes.height,
|
/* x = */ 0, /* y = */ 0, window_attributes.width, window_attributes.height,
|
||||||
/* border_width = */ 0, _depth, InputOutput, visualInfo->visual,
|
/* border_width = */ 0, _depth, InputOutput, visualInfo->visual,
|
||||||
CWColormap | CWBorderPixel | CWEventMask, &attributes);
|
CWColormap | CWBorderPixel | CWEventMask, &attributes);
|
||||||
|
XSelectInput(_display, _window, ExposureMask);
|
||||||
XFree(visualInfo);
|
XFree(visualInfo);
|
||||||
XSetWindowBackground(_display, _window, /* color = */ 0);
|
XSetWindowBackground(_display, _window, /* color = */ 0);
|
||||||
XMapWindow(_display, _window);
|
XMapWindow(_display, _window);
|
||||||
|
|
Loading…
Reference in New Issue