mirror of https://github.com/bsnes-emu/bsnes.git
Update to v070r01 release.
byuu says: - phoenix supports onActivate, or return key pressed, on text boxes - phoenix supports setGeometry() on all widgets - input settings can now map analog axes and analog buttons - analog button support is simplified over bsnes/Qt, and it supports the trigger inversion you see between Xbox 360 and Thrustmaster controllers - load cartridge window lets you press enter in the path box to select the folder, but currently allows invalid folders (makes list empty) - load cartridge won't reset your view if the folder doesn't change - this means the last ROM you loaded is highlighted the next time you go to load cartridge; you're welcome, FitzRoy :P - removed quit system menu option - added dummy controller port system menu options, there's no code behind them yet - added power/reset menu options, dropped the power checkbox in favor of a standard power cycle option, removes unnecessary complexity - added video mode scaling, 1x to 5x; and aspect ratio correction - added video mode smooth toggle - added audio settings panel with volume and input frequency adjustment - config file is where you can control output frequency and latency, they are too niche for a GUI - fixed a realpath() crash on Linux when the bsnes binary was in /usr/bin
This commit is contained in:
parent
449a3ad426
commit
da5263bfc3
|
@ -1,7 +1,7 @@
|
|||
include nall/Makefile
|
||||
snes := snes
|
||||
profile := compatibility
|
||||
ui := ui-qt
|
||||
profile := performance
|
||||
ui := ui-phoenix
|
||||
|
||||
# compiler
|
||||
c := $(compiler) -std=gnu99
|
||||
|
|
|
@ -32,9 +32,9 @@ ifeq ($(compiler),)
|
|||
ifeq ($(platform),win)
|
||||
compiler := gcc
|
||||
else ifeq ($(platform),osx)
|
||||
compiler := gcc-mp-4.4
|
||||
compiler := gcc-mp-4.5
|
||||
else
|
||||
compiler := gcc
|
||||
compiler := gcc-4.5
|
||||
endif
|
||||
endif
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ static void Button_tick(Button *self) {
|
|||
|
||||
void Button::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) {
|
||||
object->widget = gtk_button_new_with_label(text);
|
||||
widget->parent = &parent;
|
||||
gtk_widget_set_size_request(object->widget, width, height);
|
||||
g_signal_connect_swapped(G_OBJECT(object->widget), "clicked", G_CALLBACK(Button_tick), (gpointer)this);
|
||||
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
|
||||
|
|
|
@ -22,6 +22,7 @@ void Canvas::create(Window &parent, unsigned x, unsigned y, unsigned width, unsi
|
|||
canvas->pitch = width * sizeof(uint32_t);
|
||||
|
||||
object->widget = gtk_drawing_area_new();
|
||||
widget->parent = &parent;
|
||||
GdkColor color;
|
||||
color.pixel = color.red = color.green = color.blue = 0;
|
||||
gtk_widget_modify_bg(object->widget, GTK_STATE_NORMAL, &color);
|
||||
|
|
|
@ -4,6 +4,7 @@ static void CheckBox_tick(CheckBox *self) {
|
|||
|
||||
void CheckBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) {
|
||||
object->widget = gtk_check_button_new_with_label(text);
|
||||
widget->parent = &parent;
|
||||
gtk_widget_set_size_request(object->widget, width, height);
|
||||
g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(CheckBox_tick), (gpointer)this);
|
||||
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
|
||||
|
|
|
@ -4,6 +4,7 @@ void ComboBox_change(ComboBox *self) {
|
|||
|
||||
void ComboBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) {
|
||||
object->widget = gtk_combo_box_new_text();
|
||||
widget->parent = &parent;
|
||||
gtk_widget_set_size_request(object->widget, width, height);
|
||||
g_signal_connect_swapped(G_OBJECT(object->widget), "changed", G_CALLBACK(ComboBox_change), (gpointer)this);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ static void EditBox_change(EditBox *self) {
|
|||
|
||||
void EditBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) {
|
||||
object->widget = gtk_scrolled_window_new(0, 0);
|
||||
widget->parent = &parent;
|
||||
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(object->widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
||||
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(object->widget), GTK_SHADOW_ETCHED_IN);
|
||||
gtk_widget_set_size_request(object->widget, width, height);
|
||||
|
|
|
@ -76,6 +76,11 @@ struct Widget : Object {
|
|||
void setEnabled(bool enabled = true);
|
||||
virtual bool focused();
|
||||
virtual void setFocused();
|
||||
virtual void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height);
|
||||
Widget();
|
||||
//private:
|
||||
struct Data;
|
||||
Data *widget;
|
||||
};
|
||||
|
||||
struct Window : Widget {
|
||||
|
@ -190,6 +195,7 @@ private:
|
|||
};
|
||||
|
||||
struct TextBox : Widget {
|
||||
nall::function<void ()> onActivate;
|
||||
nall::function<void ()> onChange;
|
||||
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = "");
|
||||
void setEditable(bool editable = true);
|
||||
|
|
|
@ -8,6 +8,7 @@ void HorizontalSlider::create(Window &parent, unsigned x, unsigned y, unsigned w
|
|||
object->position = 0;
|
||||
length += (length == 0);
|
||||
object->widget = gtk_hscale_new_with_range(0, length - 1, 1);
|
||||
widget->parent = &parent;
|
||||
gtk_scale_set_draw_value(GTK_SCALE(object->widget), false);
|
||||
gtk_widget_set_size_request(object->widget, width, height);
|
||||
g_signal_connect_swapped(G_OBJECT(object->widget), "value-changed", G_CALLBACK(HorizontalSlider_change), (gpointer)this);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
void Label::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) {
|
||||
object->widget = gtk_label_new(text);
|
||||
widget->parent = &parent;
|
||||
gtk_misc_set_alignment(GTK_MISC(object->widget), 0.0, 0.5);
|
||||
gtk_widget_set_size_request(object->widget, width, height);
|
||||
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
|
||||
|
|
|
@ -16,6 +16,7 @@ static void ListBox_activate(ListBox *self) {
|
|||
void ListBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) {
|
||||
listBox->selection = -1;
|
||||
object->widget = gtk_scrolled_window_new(0, 0);
|
||||
widget->parent = &parent;
|
||||
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(object->widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
||||
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(object->widget), GTK_SHADOW_ETCHED_IN);
|
||||
gtk_widget_set_size_request(object->widget, width, height);
|
||||
|
|
|
@ -17,6 +17,10 @@ struct Font::Data {
|
|||
PangoFontDescription *font;
|
||||
};
|
||||
|
||||
struct Widget::Data {
|
||||
Window *parent;
|
||||
};
|
||||
|
||||
struct Window::Data {
|
||||
Font *defaultFont;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
void ProgressBar::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) {
|
||||
object->widget = gtk_progress_bar_new();
|
||||
widget->parent = &parent;
|
||||
gtk_widget_set_size_request(object->widget, width, height);
|
||||
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
|
||||
gtk_widget_show(object->widget);
|
||||
|
|
|
@ -6,6 +6,7 @@ void RadioBox::create(Window &parent, unsigned x, unsigned y, unsigned width, un
|
|||
first = this;
|
||||
object->parentWindow = &parent;
|
||||
object->widget = gtk_radio_button_new_with_label(0, text);
|
||||
widget->parent = &parent;
|
||||
gtk_widget_set_size_request(object->widget, width, height);
|
||||
g_signal_connect_swapped(G_OBJECT(object->widget), "toggled", G_CALLBACK(RadioBox_tick), (gpointer)this);
|
||||
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
static void TextBox_activate(TextBox *self) {
|
||||
if(self->onActivate) self->onActivate();
|
||||
}
|
||||
|
||||
static void TextBox_change(TextBox *self) {
|
||||
if(self->object->locked == false && self->onChange) self->onChange();
|
||||
}
|
||||
|
||||
void TextBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text) {
|
||||
object->widget = gtk_entry_new();
|
||||
widget->parent = &parent;
|
||||
gtk_entry_set_text(GTK_ENTRY(object->widget), text);
|
||||
gtk_widget_set_size_request(object->widget, width, height);
|
||||
g_signal_connect_swapped(G_OBJECT(object->widget), "activate", G_CALLBACK(TextBox_activate), (gpointer)this);
|
||||
g_signal_connect_swapped(G_OBJECT(object->widget), "changed", G_CALLBACK(TextBox_change), (gpointer)this);
|
||||
if(parent.window->defaultFont) setFont(*parent.window->defaultFont);
|
||||
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
|
||||
|
|
|
@ -8,6 +8,7 @@ void VerticalSlider::create(Window &parent, unsigned x, unsigned y, unsigned wid
|
|||
object->position = 0;
|
||||
length += (length == 0);
|
||||
object->widget = gtk_vscale_new_with_range(0, length - 1, 1);
|
||||
widget->parent = &parent;
|
||||
gtk_scale_set_draw_value(GTK_SCALE(object->widget), false);
|
||||
gtk_widget_set_size_request(object->widget, width, height);
|
||||
g_signal_connect_swapped(G_OBJECT(object->widget), "value-changed", G_CALLBACK(VerticalSlider_change), (gpointer)this);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
void Viewport::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height) {
|
||||
object->widget = gtk_drawing_area_new();
|
||||
widget->parent = &parent;
|
||||
gtk_widget_set_double_buffered(object->widget, false);
|
||||
gtk_widget_set_size_request(object->widget, width, height);
|
||||
gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y);
|
||||
|
|
|
@ -34,3 +34,14 @@ void Widget::setFocused() {
|
|||
if(visible() == false) setVisible(true);
|
||||
gtk_widget_grab_focus(object->widget);
|
||||
}
|
||||
|
||||
void Widget::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) {
|
||||
if(widget->parent == 0) return;
|
||||
gtk_fixed_move(GTK_FIXED(widget->parent->object->formContainer), object->widget, x, y);
|
||||
gtk_widget_set_size_request(object->widget, width, height);
|
||||
}
|
||||
|
||||
Widget::Widget() {
|
||||
widget = new Widget::Data;
|
||||
widget->parent = 0;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,9 @@ unsigned ComboBox::selection() {
|
|||
}
|
||||
|
||||
void ComboBox::setSelection(unsigned row) {
|
||||
object->locked = true;
|
||||
comboBox->setCurrentIndex(row);
|
||||
object->locked = false;
|
||||
}
|
||||
|
||||
ComboBox::ComboBox() {
|
||||
|
|
|
@ -255,6 +255,7 @@ struct RadioBox : Widget {
|
|||
};
|
||||
|
||||
struct TextBox : Widget {
|
||||
nall::function<void ()> onActivate;
|
||||
nall::function<void ()> onChange;
|
||||
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = "");
|
||||
void setEditable(bool editable = true);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/****************************************************************************
|
||||
** Meta object code from reading C++ file 'qt.moc.hpp'
|
||||
**
|
||||
** Created: Sat Sep 25 06:31:14 2010
|
||||
** Created: Mon Sep 27 04:00:47 2010
|
||||
** by: The Qt Meta Object Compiler version 62 (Qt 4.6.2)
|
||||
**
|
||||
** WARNING! All changes made in this file will be lost!
|
||||
|
@ -761,7 +761,7 @@ static const uint qt_meta_data_TextBox__Data[] = {
|
|||
4, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
1, 14, // methods
|
||||
2, 14, // methods
|
||||
0, 0, // properties
|
||||
0, 0, // enums/sets
|
||||
0, 0, // constructors
|
||||
|
@ -770,12 +770,13 @@ static const uint qt_meta_data_TextBox__Data[] = {
|
|||
|
||||
// slots: signature, parameters, type, tag, flags
|
||||
15, 14, 14, 14, 0x0a,
|
||||
28, 14, 14, 14, 0x0a,
|
||||
|
||||
0 // eod
|
||||
};
|
||||
|
||||
static const char qt_meta_stringdata_TextBox__Data[] = {
|
||||
"TextBox::Data\0\0onChange()\0"
|
||||
"TextBox::Data\0\0onActivate()\0onChange()\0"
|
||||
};
|
||||
|
||||
const QMetaObject TextBox::Data::staticMetaObject = {
|
||||
|
@ -807,10 +808,11 @@ int TextBox::Data::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|||
return _id;
|
||||
if (_c == QMetaObject::InvokeMetaMethod) {
|
||||
switch (_id) {
|
||||
case 0: onChange(); break;
|
||||
case 0: onActivate(); break;
|
||||
case 1: onChange(); break;
|
||||
default: ;
|
||||
}
|
||||
_id -= 1;
|
||||
_id -= 2;
|
||||
}
|
||||
return _id;
|
||||
}
|
||||
|
|
|
@ -164,7 +164,7 @@ public:
|
|||
|
||||
public slots:
|
||||
void onChange() {
|
||||
if(self.onChange) self.onChange();
|
||||
if(self.object->locked == false && self.onChange) self.onChange();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -262,6 +262,10 @@ public:
|
|||
}
|
||||
|
||||
public slots:
|
||||
void onActivate() {
|
||||
if(self.onActivate) self.onActivate();
|
||||
}
|
||||
|
||||
void onChange() {
|
||||
if(self.onChange) self.onChange();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ void TextBox::create(Window &parent, unsigned x, unsigned y, unsigned width, uns
|
|||
textBox->setText(text);
|
||||
if(parent.window->defaultFont) textBox->setFont(*parent.window->defaultFont);
|
||||
textBox->show();
|
||||
textBox->connect(textBox, SIGNAL(returnPressed()), SLOT(onActivate()));
|
||||
textBox->connect(textBox, SIGNAL(textEdited(const QString&)), SLOT(onChange()));
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,10 @@ void Widget::setFocused() {
|
|||
SetFocus(widget->window);
|
||||
}
|
||||
|
||||
void Widget::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) {
|
||||
SetWindowPos(widget->window, NULL, x, y, width, height, SWP_NOZORDER);
|
||||
}
|
||||
|
||||
Widget::Widget() {
|
||||
os.objects.append(this);
|
||||
widget = new Widget::Data;
|
||||
|
|
|
@ -211,10 +211,17 @@ static void OS_keyboardProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
|||
info.cbSize = sizeof(GUITHREADINFO);
|
||||
GetGUIThreadInfo(GetCurrentThreadId(), &info);
|
||||
Object *object_ptr = (Object*)GetWindowLongPtr(info.hwndFocus, GWLP_USERDATA);
|
||||
if(object_ptr && dynamic_cast<ListBox*>(object_ptr)) {
|
||||
ListBox &listBox = (ListBox&)*object_ptr;
|
||||
if(wparam == VK_RETURN) {
|
||||
if(listBox.onActivate) listBox.onActivate();
|
||||
if(object_ptr) {
|
||||
if(dynamic_cast<ListBox*>(object_ptr)) {
|
||||
ListBox &listBox = (ListBox&)*object_ptr;
|
||||
if(wparam == VK_RETURN) {
|
||||
if(listBox.onActivate) listBox.onActivate();
|
||||
}
|
||||
} else if(dynamic_cast<TextBox*>(object_ptr)) {
|
||||
TextBox &textBox = (TextBox&)*object_ptr;
|
||||
if(wparam == VK_RETURN) {
|
||||
if(textBox.onActivate) textBox.onActivate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,6 +84,7 @@ struct Widget : Object {
|
|||
void setEnabled(bool enabled = true);
|
||||
bool focused();
|
||||
void setFocused();
|
||||
virtual void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height);
|
||||
Widget();
|
||||
//private:
|
||||
struct Data;
|
||||
|
@ -211,6 +212,7 @@ struct RadioBox : Widget {
|
|||
};
|
||||
|
||||
struct TextBox : Widget {
|
||||
nall::function<void ()> onActivate;
|
||||
nall::function<void ()> onChange;
|
||||
void create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const char *text = "");
|
||||
nall::string text();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
namespace SNES {
|
||||
namespace Info {
|
||||
static const char Name[] = "bsnes";
|
||||
static const char Version[] = "070";
|
||||
static const char Version[] = "070.01";
|
||||
static const unsigned SerializerVersion = 13;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,19 +8,15 @@ bool Cartridge::loadNormal(const char *filename) {
|
|||
SNES::cartridge.load(SNES::Cartridge::Mode::Normal, lstring() << baseXML);
|
||||
loadMemory(SNES::memory::cartram, baseName, ".srm");
|
||||
loadMemory(SNES::memory::cartrtc, baseName, ".rtc");
|
||||
cheatEditor.load(baseName);
|
||||
utility.setTitle(notdir(baseName));
|
||||
utility.showMessage(string("Loaded ", notdir(baseName)));
|
||||
config.path.current = dir(baseName);
|
||||
utility.cartridgeLoaded();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Cartridge::unload() {
|
||||
if(SNES::cartridge.loaded() == false) return;
|
||||
cheatEditor.save(baseName);
|
||||
saveMemory(SNES::memory::cartram, baseName, ".srm");
|
||||
saveMemory(SNES::memory::cartrtc, baseName, ".rtc");
|
||||
SNES::cartridge.unload();
|
||||
utility.cartridgeUnloaded();
|
||||
}
|
||||
|
||||
bool Cartridge::loadCartridge(SNES::MappedRAM &memory, string &XML, const char *filename) {
|
||||
|
|
|
@ -14,6 +14,9 @@ void Configuration::create() {
|
|||
|
||||
attach(video.driver = "", "video.driver");
|
||||
attach(video.synchronize = false, "video.synchronize");
|
||||
attach(video.smooth = true, "video.smooth");
|
||||
attach(video.scale = 2, "video.scale");
|
||||
attach(video.aspectRatioCorrection = true, "video.aspectRatioCorrection");
|
||||
attach(video.contrast = 100, "video.contrast");
|
||||
attach(video.brightness = 100, "video.brightness");
|
||||
attach(video.gamma = 100, "video.gamma");
|
||||
|
@ -22,6 +25,10 @@ void Configuration::create() {
|
|||
attach(audio.driver = "", "audio.driver");
|
||||
attach(audio.synchronize = true, "audio.synchronize");
|
||||
attach(audio.mute = false, "audio.mute");
|
||||
attach(audio.volume = 100, "audio.volume");
|
||||
attach(audio.latency = 60, "audio.latency");
|
||||
attach(audio.inputFrequency = 32000, "audio.inputFrequency");
|
||||
attach(audio.outputFrequency = 44100, "audio.outputFrequency");
|
||||
|
||||
attach(input.driver = "", "input.driver");
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@ struct Configuration : public configuration {
|
|||
struct Video {
|
||||
string driver;
|
||||
bool synchronize;
|
||||
bool smooth;
|
||||
unsigned scale;
|
||||
bool aspectRatioCorrection;
|
||||
unsigned contrast;
|
||||
unsigned brightness;
|
||||
unsigned gamma;
|
||||
|
@ -18,6 +21,10 @@ struct Configuration : public configuration {
|
|||
string driver;
|
||||
bool synchronize;
|
||||
bool mute;
|
||||
unsigned volume;
|
||||
unsigned latency;
|
||||
unsigned inputFrequency;
|
||||
unsigned outputFrequency;
|
||||
} audio;
|
||||
|
||||
struct Input {
|
||||
|
|
|
@ -16,6 +16,7 @@ void FileBrowser::create() {
|
|||
|
||||
setGeometry(160, 160, 640, y);
|
||||
|
||||
pathBox.onActivate = []() { fileBrowser.setFolder(fileBrowser.pathBox.text()); };
|
||||
browseButton.onTick = { &FileBrowser::folderBrowse, this };
|
||||
upButton.onTick = { &FileBrowser::folderUp, this };
|
||||
contentsBox.onActivate = { &FileBrowser::fileActivate, this };
|
||||
|
@ -29,6 +30,10 @@ void FileBrowser::fileOpen(const char *pathname) {
|
|||
}
|
||||
|
||||
void FileBrowser::setFolder(const char *pathname) {
|
||||
string path = pathname;
|
||||
path.rtrim("/");
|
||||
if(folder == path) return;
|
||||
|
||||
contentsBox.reset();
|
||||
contents.reset();
|
||||
|
||||
|
@ -42,6 +47,7 @@ void FileBrowser::setFolder(const char *pathname) {
|
|||
}
|
||||
foreach(item, contents) contentsBox.addItem(item);
|
||||
contentsBox.setSelection(0);
|
||||
contentsBox.setFocused();
|
||||
}
|
||||
|
||||
void FileBrowser::folderBrowse() {
|
||||
|
@ -61,7 +67,6 @@ void FileBrowser::fileActivate() {
|
|||
} else {
|
||||
filename = string(folder, "/", filename);
|
||||
cartridge.loadNormal(filename);
|
||||
SNES::system.power();
|
||||
setVisible(false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,18 +9,42 @@ void MainWindow::create() {
|
|||
|
||||
system.create(*this, "System");
|
||||
systemLoadCartridge.create(system, "Load Cartridge ...");
|
||||
systemSeparator.create(system);
|
||||
systemQuit.create(system, "Quit");
|
||||
systemSeparator1.create(system);
|
||||
systemPower.create(system, "Power Cycle");
|
||||
systemReset.create(system, "Reset");
|
||||
systemSeparator2.create(system);
|
||||
systemPort1.create(system, "Controller Port 1");
|
||||
systemPort1.setEnabled(false);
|
||||
systemPort2.create(system, "Controller Port 2");
|
||||
systemPort2.setEnabled(false);
|
||||
|
||||
settings.create(*this, "Settings");
|
||||
settingsVideoMode.create(settings, "Video Mode");
|
||||
settingsVideoMode1x.create(settingsVideoMode, "Scale 1x");
|
||||
settingsVideoMode2x.create(settingsVideoMode1x, "Scale 2x");
|
||||
settingsVideoMode3x.create(settingsVideoMode1x, "Scale 3x");
|
||||
settingsVideoMode4x.create(settingsVideoMode1x, "Scale 4x");
|
||||
settingsVideoMode5x.create(settingsVideoMode1x, "Scale 5x");
|
||||
if(config.video.scale == 1) settingsVideoMode1x.setChecked();
|
||||
if(config.video.scale == 2) settingsVideoMode2x.setChecked();
|
||||
if(config.video.scale == 3) settingsVideoMode3x.setChecked();
|
||||
if(config.video.scale == 4) settingsVideoMode4x.setChecked();
|
||||
if(config.video.scale == 5) settingsVideoMode5x.setChecked();
|
||||
settingsVideoModeSeparator.create(settingsVideoMode);
|
||||
settingsVideoModeAspectRatioCorrection.create(settingsVideoMode, "Correct Aspect Ratio");
|
||||
settingsVideoModeAspectRatioCorrection.setChecked(config.video.aspectRatioCorrection);
|
||||
settingsSmoothVideo.create(settings, "Smooth Video");
|
||||
settingsSmoothVideo.setChecked(config.video.smooth);
|
||||
settingsSeparator1.create(settings);
|
||||
settingsSynchronizeVideo.create(settings, "Synchronize Video");
|
||||
settingsSynchronizeVideo.setChecked(config.video.synchronize);
|
||||
settingsSynchronizeAudio.create(settings, "Synchronize Audio");
|
||||
settingsSynchronizeAudio.setChecked(config.audio.synchronize);
|
||||
settingsMuteAudio.create(settings, "Mute Audio");
|
||||
settingsMuteAudio.setChecked(config.audio.mute);
|
||||
settingsSeparator.create(settings);
|
||||
settingsSeparator2.create(settings);
|
||||
settingsVideo.create(settings, "Video Settings ...");
|
||||
settingsAudio.create(settings, "Audio Settings ...");
|
||||
settingsInput.create(settings, "Input Settings ...");
|
||||
settingsAdvanced.create(settings, "Advanced Settings ...");
|
||||
|
||||
|
@ -52,7 +76,30 @@ void MainWindow::create() {
|
|||
utility.loadCartridgeNormal();
|
||||
};
|
||||
|
||||
systemQuit.onTick = []() { application.quit = true; };
|
||||
systemPower.onTick = []() {
|
||||
SNES::system.power();
|
||||
utility.showMessage("System was power cycled");
|
||||
};
|
||||
|
||||
systemReset.onTick = []() {
|
||||
SNES::system.reset();
|
||||
utility.showMessage("System was reset");
|
||||
};
|
||||
|
||||
settingsVideoMode1x.onTick = []() { utility.setScale(1); };
|
||||
settingsVideoMode2x.onTick = []() { utility.setScale(2); };
|
||||
settingsVideoMode3x.onTick = []() { utility.setScale(3); };
|
||||
settingsVideoMode4x.onTick = []() { utility.setScale(4); };
|
||||
settingsVideoMode5x.onTick = []() { utility.setScale(5); };
|
||||
|
||||
settingsVideoModeAspectRatioCorrection.onTick = []() {
|
||||
utility.setAspectRatioCorrection(mainWindow.settingsVideoModeAspectRatioCorrection.checked());
|
||||
};
|
||||
|
||||
settingsSmoothVideo.onTick = []() {
|
||||
config.video.smooth = mainWindow.settingsSmoothVideo.checked();
|
||||
video.set(Video::Filter, (unsigned)config.video.smooth);
|
||||
};
|
||||
|
||||
settingsSynchronizeVideo.onTick = []() {
|
||||
config.video.synchronize = mainWindow.settingsSynchronizeVideo.checked();
|
||||
|
@ -67,6 +114,7 @@ void MainWindow::create() {
|
|||
settingsMuteAudio.onTick = []() { config.audio.mute = mainWindow.settingsMuteAudio.checked(); };
|
||||
|
||||
settingsVideo.onTick = []() { videoSettings.setVisible(); };
|
||||
settingsAudio.onTick = []() { audioSettings.setVisible(); };
|
||||
settingsInput.onTick = []() { inputSettings.setVisible(); };
|
||||
settingsAdvanced.onTick = []() { advancedSettings.setVisible(); };
|
||||
|
||||
|
@ -98,4 +146,20 @@ void MainWindow::create() {
|
|||
application.quit = true;
|
||||
return false;
|
||||
};
|
||||
|
||||
synchronize();
|
||||
}
|
||||
|
||||
void MainWindow::synchronize() {
|
||||
if(SNES::cartridge.loaded() == false) {
|
||||
systemPower.setEnabled(false);
|
||||
systemReset.setEnabled(false);
|
||||
toolsStateSave.setEnabled(false);
|
||||
toolsStateLoad.setEnabled(false);
|
||||
} else {
|
||||
systemPower.setEnabled(true);
|
||||
systemReset.setEnabled(true);
|
||||
toolsStateSave.setEnabled(true);
|
||||
toolsStateLoad.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,29 @@
|
|||
struct MainWindow : Window {
|
||||
Menu system;
|
||||
MenuItem systemLoadCartridge;
|
||||
MenuSeparator systemSeparator;
|
||||
MenuItem systemQuit;
|
||||
MenuSeparator systemSeparator1;
|
||||
MenuItem systemPower;
|
||||
MenuItem systemReset;
|
||||
MenuSeparator systemSeparator2;
|
||||
Menu systemPort1;
|
||||
Menu systemPort2;
|
||||
Menu settings;
|
||||
Menu settingsVideoMode;
|
||||
MenuRadioItem settingsVideoMode1x;
|
||||
MenuRadioItem settingsVideoMode2x;
|
||||
MenuRadioItem settingsVideoMode3x;
|
||||
MenuRadioItem settingsVideoMode4x;
|
||||
MenuRadioItem settingsVideoMode5x;
|
||||
MenuSeparator settingsVideoModeSeparator;
|
||||
MenuCheckItem settingsVideoModeAspectRatioCorrection;
|
||||
MenuCheckItem settingsSmoothVideo;
|
||||
MenuSeparator settingsSeparator1;
|
||||
MenuCheckItem settingsSynchronizeVideo;
|
||||
MenuCheckItem settingsSynchronizeAudio;
|
||||
MenuCheckItem settingsMuteAudio;
|
||||
MenuSeparator settingsSeparator;
|
||||
MenuSeparator settingsSeparator2;
|
||||
MenuItem settingsVideo;
|
||||
MenuItem settingsAudio;
|
||||
MenuItem settingsInput;
|
||||
MenuItem settingsAdvanced;
|
||||
Menu tools;
|
||||
|
@ -31,6 +46,7 @@ struct MainWindow : Window {
|
|||
Viewport viewport;
|
||||
|
||||
void create();
|
||||
void synchronize();
|
||||
};
|
||||
|
||||
extern MainWindow mainWindow;
|
||||
|
|
|
@ -6,6 +6,8 @@ void InputMapper::AbstractInput::bind() {
|
|||
else if(strend(mapping, ".Down")) type = Type::HatDown;
|
||||
else if(strend(mapping, ".Left")) type = Type::HatLeft;
|
||||
else if(strend(mapping, ".Right")) type = Type::HatRight;
|
||||
else if(strend(mapping, ".Lo")) type = Type::AxisLo;
|
||||
else if(strend(mapping, ".Hi")) type = Type::AxisHi;
|
||||
else type = Type::Button;
|
||||
|
||||
string mappingValue = mapping;
|
||||
|
@ -21,6 +23,8 @@ int16_t InputMapper::DigitalInput::poll() {
|
|||
case AbstractInput::Type::HatDown: return (bool)(value & Joypad::HatDown);
|
||||
case AbstractInput::Type::HatLeft: return (bool)(value & Joypad::HatLeft);
|
||||
case AbstractInput::Type::HatRight: return (bool)(value & Joypad::HatRight);
|
||||
case AbstractInput::Type::AxisLo: return (bool)(value < -16384);
|
||||
case AbstractInput::Type::AxisHi: return (bool)(value > +16384);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,3 +210,7 @@ int16_t InputMapper::poll(bool port, SNES::Input::Device device, unsigned index,
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int16_t InputMapper::value(unsigned scancode) {
|
||||
return state[activeState][scancode];
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
struct InputMapper {
|
||||
struct AbstractInput {
|
||||
enum class Type : unsigned { Button, HatUp, HatDown, HatLeft, HatRight } type;
|
||||
enum class Type : unsigned { Button, HatUp, HatDown, HatLeft, HatRight, AxisLo, AxisHi } type;
|
||||
string name;
|
||||
string mapping;
|
||||
unsigned scancode;
|
||||
|
@ -76,6 +76,7 @@ struct InputMapper {
|
|||
void bind();
|
||||
void poll();
|
||||
int16_t poll(bool port, SNES::Input::Device device, unsigned index, unsigned id);
|
||||
int16_t value(unsigned scancode);
|
||||
};
|
||||
|
||||
extern InputMapper inputMapper;
|
||||
|
|
|
@ -11,10 +11,10 @@ void Application::main(int argc, char **argv) {
|
|||
inputMapper.create();
|
||||
|
||||
char temp[PATH_MAX];
|
||||
config.path.base = realpath(argv[0], temp);
|
||||
if(realpath(argv[0], temp)) config.path.base = temp;
|
||||
config.path.base.transform("\\", "/");
|
||||
config.path.base = dir(config.path.base);
|
||||
config.path.user = userpath(temp);
|
||||
if(userpath(temp)) config.path.user = temp;
|
||||
config.path.user.transform("\\", "/");
|
||||
if(strend(config.path.user, "/") == false) config.path.user.append("/");
|
||||
config.path.user.append(".bsnes/");
|
||||
|
@ -41,16 +41,18 @@ void Application::main(int argc, char **argv) {
|
|||
mainWindow.create();
|
||||
fileBrowser.create();
|
||||
videoSettings.create();
|
||||
audioSettings.create();
|
||||
inputSettings.create();
|
||||
advancedSettings.create();
|
||||
cheatEditor.create();
|
||||
utility.setScale(config.video.scale);
|
||||
mainWindow.setVisible();
|
||||
os.run();
|
||||
|
||||
video.driver(config.video.driver);
|
||||
video.set(Video::Handle, mainWindow.viewport.handle());
|
||||
video.set(Video::Synchronize, config.video.synchronize);
|
||||
video.set(Video::Filter, (unsigned)Video::FilterLinear);
|
||||
video.set(Video::Filter, (unsigned)config.video.smooth);
|
||||
if(video.init() == false) {
|
||||
MessageWindow::critical(mainWindow, "Failed to initialize video.");
|
||||
video.driver("None");
|
||||
|
@ -60,7 +62,11 @@ void Application::main(int argc, char **argv) {
|
|||
audio.driver(config.audio.driver);
|
||||
audio.set(Audio::Handle, mainWindow.viewport.handle());
|
||||
audio.set(Audio::Synchronize, config.audio.synchronize);
|
||||
audio.set(Audio::Frequency, (unsigned)32000);
|
||||
audio.set(Audio::Volume, config.audio.volume);
|
||||
audio.set(Audio::Latency, config.audio.latency);
|
||||
audio.set(Audio::Frequency, config.audio.outputFrequency);
|
||||
audio.set(Audio::Resample, true);
|
||||
audio.set(Audio::ResampleRatio, (double)config.audio.inputFrequency / (double)config.audio.outputFrequency);
|
||||
if(audio.init() == false) {
|
||||
MessageWindow::critical(mainWindow, "Failed to initialize audio.");
|
||||
audio.driver("None");
|
||||
|
@ -76,10 +82,7 @@ void Application::main(int argc, char **argv) {
|
|||
}
|
||||
|
||||
SNES::system.init(&interface);
|
||||
if(argc == 2) {
|
||||
cartridge.loadNormal(argv[1]);
|
||||
SNES::system.power();
|
||||
}
|
||||
if(argc == 2) cartridge.loadNormal(argv[1]);
|
||||
|
||||
while(quit == false) {
|
||||
os.run();
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
AudioSettings audioSettings;
|
||||
|
||||
void AudioSettings::create() {
|
||||
application.windows.append(this);
|
||||
Window::create(0, 0, 256, 256, "Audio Settings");
|
||||
setDefaultFont(application.proportionalFont);
|
||||
|
||||
unsigned x = 5, y = 5;
|
||||
|
||||
volumeLabel.create(*this, x, y, 70, Style::SliderHeight, "Volume:");
|
||||
volumeValue.create(*this, x + 70, y, 60, Style::SliderHeight, "100%");
|
||||
volumeSlider.create(*this, x + 130, y, 300, Style::SliderHeight, 201); y += Style::SliderHeight + 5;
|
||||
volumeSlider.setPosition(config.audio.volume);
|
||||
|
||||
frequencyLabel.create(*this, x, y, 70, Style::SliderHeight, "Frequency:");
|
||||
frequencyValue.create(*this, x + 70, y, 60, Style::SliderHeight, "32000hz");
|
||||
frequencySlider.create(*this, x + 130, y, 300, Style::SliderHeight, 2001); y += Style::SliderHeight + 5;
|
||||
frequencySlider.setPosition(config.audio.inputFrequency - 31000);
|
||||
|
||||
volumeSlider.onChange = []() {
|
||||
config.audio.volume = audioSettings.volumeSlider.position();
|
||||
audio.set(Audio::Volume, config.audio.volume);
|
||||
audioSettings.volumeValue.setText(string(config.audio.volume, "%"));
|
||||
};
|
||||
|
||||
frequencySlider.onChange = []() {
|
||||
config.audio.inputFrequency = audioSettings.frequencySlider.position() + 31000;
|
||||
audio.set(Audio::ResampleRatio, (double)config.audio.inputFrequency / (double)config.audio.outputFrequency);
|
||||
audioSettings.frequencyValue.setText(string(config.audio.inputFrequency, "hz"));
|
||||
};
|
||||
|
||||
setGeometry(160, 160, 440, y);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
struct AudioSettings : Window {
|
||||
Label volumeLabel;
|
||||
Label volumeValue;
|
||||
HorizontalSlider volumeSlider;
|
||||
Label frequencyLabel;
|
||||
Label frequencyValue;
|
||||
HorizontalSlider frequencySlider;
|
||||
|
||||
void create();
|
||||
};
|
||||
|
||||
extern AudioSettings audioSettings;
|
|
@ -17,7 +17,7 @@ void InputSettings::create() {
|
|||
deviceLabel.create(*this, x + 255, y, 50, Style::ComboBoxHeight, "Device:");
|
||||
deviceBox.create(*this, x + 305, y, 200, Style::ComboBoxHeight); y += Style::ComboBoxHeight + 5;
|
||||
|
||||
mappingList.create(*this, x, y, 505, 300, "Name\tMapping"); y += 300 + 5;
|
||||
mappingList.create(*this, x, y, 505, 265, "Name\tMapping"); y += 265 + 5;
|
||||
mappingList.setHeaderVisible();
|
||||
mappingList.setFocused();
|
||||
|
||||
|
@ -104,8 +104,41 @@ void InputSettings::inputEvent(uint16_t scancode, int16_t value) {
|
|||
else if(value == Joypad::HatDown) setMapping(string(mapping, ".Down"));
|
||||
else if(value == Joypad::HatLeft) setMapping(string(mapping, ".Left"));
|
||||
else if(value == Joypad::HatRight) setMapping(string(mapping, ".Right"));
|
||||
} else if(Joypad::isAnyAxis(scancode)) {
|
||||
if(joypadsCalibrated == false) return calibrateJoypads();
|
||||
unsigned joypadNumber = Joypad::numberDecode(scancode);
|
||||
unsigned axisNumber = Joypad::axisDecode(scancode);
|
||||
int16_t calibration = joypadCalibration[joypadNumber][axisNumber];
|
||||
if(calibration > -12288 && calibration < +12288 && value < -24576) setMapping(string(mapping, ".Lo"));
|
||||
else if(calibration > -12288 && calibration < +12288 && value > +24576) setMapping(string(mapping, ".Hi"));
|
||||
else if(calibration <= -12288 && value >= +12288) setMapping(string(mapping, ".Hi"));
|
||||
else if(calibration >= +12288 && value <= -12288) setMapping(string(mapping, ".Lo"));
|
||||
} else if(Joypad::isAnyButton(scancode) && value) {
|
||||
setMapping(mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputSettings::calibrateJoypads() {
|
||||
if(joypadsCalibrating == true) return;
|
||||
joypadsCalibrating = true;
|
||||
MessageWindow::information(*this,
|
||||
"Analog joypads must be calibrated prior to use.\n\n"
|
||||
"Please move all analog axes, and press all analog buttons.\n"
|
||||
"Please do this for every controller you wish to use.\n"
|
||||
"Once finished, please let go of all axes and buttons, and press OK."
|
||||
);
|
||||
inputMapper.poll();
|
||||
for(unsigned j = 0; j < Joypad::Count &&j<2; j++) {
|
||||
for(unsigned a = 0; a < Joypad::Axes; a++) {
|
||||
joypadCalibration[j][a] = inputMapper.value(joypad(j).axis(a));
|
||||
}
|
||||
}
|
||||
joypadsCalibrating = false;
|
||||
joypadsCalibrated = true;
|
||||
}
|
||||
|
||||
InputSettings::InputSettings() {
|
||||
joypadsCalibrated = false;
|
||||
joypadsCalibrating = false;
|
||||
}
|
||||
|
|
|
@ -6,9 +6,15 @@ struct InputSettings : Window {
|
|||
ListBox mappingList;
|
||||
|
||||
void inputEvent(uint16_t scancode, int16_t value);
|
||||
void calibrateJoypads();
|
||||
void create();
|
||||
InputSettings();
|
||||
|
||||
private:
|
||||
bool joypadsCalibrated;
|
||||
bool joypadsCalibrating;
|
||||
int16_t joypadCalibration[Joypad::Count][Joypad::Axes];
|
||||
|
||||
void portChanged();
|
||||
void deviceChanged();
|
||||
void setMapping(const char *mapping);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "../base.hpp"
|
||||
#include "video.cpp"
|
||||
#include "audio.cpp"
|
||||
#include "input.cpp"
|
||||
#include "advanced.cpp"
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "video.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "input.hpp"
|
||||
#include "advanced.hpp"
|
||||
|
|
|
@ -22,9 +22,9 @@ void VideoSettings::create() {
|
|||
gammaValue.create (*this, x + 80, y, 50, Style::SliderHeight, "100%");
|
||||
gammaSlider.create (*this, x + 130, y, 300, Style::SliderHeight, 201); y += Style::SliderHeight + 5;
|
||||
|
||||
gammaRampCheck.create (*this, x, y, 430, 15, "Enable NTSC gamma ramp simulation"); y += 15;
|
||||
gammaRampCheck.create (*this, x, y, 430, Style::CheckBoxHeight, "Enable NTSC gamma ramp simulation"); y += Style::CheckBoxHeight + 5;
|
||||
|
||||
setGeometry(160, 160, 440, y + 5);
|
||||
setGeometry(160, 160, 440, y);
|
||||
|
||||
contrastSlider.setPosition(config.video.contrast);
|
||||
brightnessSlider.setPosition(config.video.brightness);
|
||||
|
|
|
@ -11,7 +11,14 @@ void Utility::setTitle(const char *text) {
|
|||
|
||||
void Utility::updateStatus() {
|
||||
time_t currentTime = time(0);
|
||||
string text = ((currentTime - statusTime) > 3) ? statusText : statusMessage;
|
||||
string text;
|
||||
if((currentTime - statusTime) <= 3) {
|
||||
text = statusMessage;
|
||||
} else if(SNES::cartridge.loaded() == false) {
|
||||
text = "No cartridge loaded";
|
||||
} else {
|
||||
text = statusText;
|
||||
}
|
||||
if(text != statusCurrentText) {
|
||||
mainWindow.setStatusText(statusCurrentText = text);
|
||||
}
|
||||
|
@ -27,6 +34,35 @@ void Utility::showMessage(const char *text) {
|
|||
statusTime = time(0);
|
||||
}
|
||||
|
||||
void Utility::setScale(unsigned scale) {
|
||||
config.video.scale = scale;
|
||||
unsigned width = 256 * scale;
|
||||
unsigned height = 224 * scale;
|
||||
if(config.video.aspectRatioCorrection) width *= 54.0 / 47.0; //PAL = 32.0 / 23.0
|
||||
mainWindow.viewport.setGeometry(0, 0, width, height);
|
||||
mainWindow.setGeometry(128, 128, width, height);
|
||||
}
|
||||
|
||||
void Utility::setAspectRatioCorrection(bool aspectRatioCorrection) {
|
||||
config.video.aspectRatioCorrection = aspectRatioCorrection;
|
||||
setScale(config.video.scale);
|
||||
}
|
||||
|
||||
void Utility::cartridgeLoaded() {
|
||||
SNES::system.power();
|
||||
cheatEditor.load(cartridge.baseName);
|
||||
mainWindow.synchronize();
|
||||
utility.setTitle(notdir(cartridge.baseName));
|
||||
utility.showMessage(string("Loaded ", notdir(cartridge.baseName)));
|
||||
config.path.current = dir(cartridge.baseName);
|
||||
}
|
||||
|
||||
void Utility::cartridgeUnloaded() {
|
||||
SNES::cartridge.unload();
|
||||
cheatEditor.save(cartridge.baseName);
|
||||
mainWindow.synchronize();
|
||||
}
|
||||
|
||||
void Utility::loadCartridgeNormal() {
|
||||
if(config.settings.useNativeDialogs == false) {
|
||||
fileBrowser.fileOpen(config.path.current);
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
struct Utility {
|
||||
struct Utility : property<Utility> {
|
||||
void setTitle(const char *text);
|
||||
void updateStatus();
|
||||
void setStatus(const char *text);
|
||||
void showMessage(const char *text);
|
||||
|
||||
void setScale(unsigned scale);
|
||||
void setAspectRatioCorrection(bool aspectRatioCorrection);
|
||||
|
||||
void cartridgeLoaded();
|
||||
void cartridgeUnloaded();
|
||||
|
||||
void loadCartridgeNormal();
|
||||
void saveState(unsigned slot);
|
||||
void loadState(unsigned slot);
|
||||
|
|
Loading…
Reference in New Issue