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:
Tim Allen 2010-09-28 00:38:32 +10:00
parent 449a3ad426
commit da5263bfc3
45 changed files with 340 additions and 43 deletions

View File

@ -1,7 +1,7 @@
include nall/Makefile
snes := snes
profile := compatibility
ui := ui-qt
profile := performance
ui := ui-phoenix
# compiler
c := $(compiler) -std=gnu99

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -17,6 +17,10 @@ struct Font::Data {
PangoFontDescription *font;
};
struct Widget::Data {
Window *parent;
};
struct Window::Data {
Font *defaultFont;
};

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -27,7 +27,9 @@ unsigned ComboBox::selection() {
}
void ComboBox::setSelection(unsigned row) {
object->locked = true;
comboBox->setCurrentIndex(row);
object->locked = false;
}
ComboBox::ComboBox() {

View File

@ -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);

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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()));
}

View File

@ -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;

View File

@ -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();
}
}
}
}

View File

@ -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();

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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");

View File

@ -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 {

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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];
}

View File

@ -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;

View File

@ -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();

View File

@ -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);
}

View File

@ -0,0 +1,12 @@
struct AudioSettings : Window {
Label volumeLabel;
Label volumeValue;
HorizontalSlider volumeSlider;
Label frequencyLabel;
Label frequencyValue;
HorizontalSlider frequencySlider;
void create();
};
extern AudioSettings audioSettings;

View File

@ -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;
}

View File

@ -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);

View File

@ -1,4 +1,5 @@
#include "../base.hpp"
#include "video.cpp"
#include "audio.cpp"
#include "input.cpp"
#include "advanced.cpp"

View File

@ -1,3 +1,4 @@
#include "video.hpp"
#include "audio.hpp"
#include "input.hpp"
#include "advanced.hpp"

View File

@ -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);

View File

@ -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);

View File

@ -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);