diff --git a/bsnes/nall/platform.hpp b/bsnes/nall/platform.hpp index 8e7bb5fe..955ae811 100755 --- a/bsnes/nall/platform.hpp +++ b/bsnes/nall/platform.hpp @@ -125,7 +125,10 @@ } inline char *getcwd(char *path) { - return getcwd(path, PATH_MAX); + auto unused = getcwd(path, PATH_MAX); + unsigned length = strlen(path); + if(path[length] != '/') strcpy(path + length, "/"); + return path; } #endif diff --git a/bsnes/nall/string/core.hpp b/bsnes/nall/string/core.hpp index 4ffda4ee..9903a6f7 100755 --- a/bsnes/nall/string/core.hpp +++ b/bsnes/nall/string/core.hpp @@ -66,11 +66,13 @@ bool string::operator> (const char *str) const { return strcmp(data, str) > 0; bool string::operator>=(const char *str) const { return strcmp(data, str) >= 0; } string& string::operator=(const string &value) { + if(&value == this) return *this; assign(value); return *this; } string& string::operator=(string &&source) { + if(&source == this) return *this; if(data) free(data); size = source.size; data = source.data; @@ -87,11 +89,13 @@ template string::string(Args&&... args) { } string::string(const string &value) { + if(&value == this) return; size = strlen(value); data = strdup(value); } string::string(string &&source) { + if(&source == this) return; size = source.size; data = source.data; source.data = 0; diff --git a/bsnes/phoenix/core/core.cpp b/bsnes/phoenix/core/core.cpp index 4cdee332..4330c714 100755 --- a/bsnes/phoenix/core/core.cpp +++ b/bsnes/phoenix/core/core.cpp @@ -306,6 +306,10 @@ Window::~Window() { //Action //====== +bool Action::enabled() { + return state.enabled; +} + void Action::setEnabled(bool enabled) { state.enabled = enabled; return p.setEnabled(enabled); @@ -316,6 +320,10 @@ void Action::setVisible(bool visible) { return p.setVisible(visible); } +bool Action::visible() { + return state.visible; +} + Action::Action(pAction &p): state(*new State), Object(p), diff --git a/bsnes/phoenix/core/core.hpp b/bsnes/phoenix/core/core.hpp index cca25992..e1db8cd8 100755 --- a/bsnes/phoenix/core/core.hpp +++ b/bsnes/phoenix/core/core.hpp @@ -162,8 +162,10 @@ struct Window : private nall::base_from_member, Object { }; struct Action : Object { + bool enabled(); void setEnabled(bool enabled = true); void setVisible(bool visible = true); + bool visible(); Action(pAction &p); ~Action(); diff --git a/bsnes/phoenix/gtk/window.cpp b/bsnes/phoenix/gtk/window.cpp index 175bfd34..078e743e 100755 --- a/bsnes/phoenix/gtk/window.cpp +++ b/bsnes/phoenix/gtk/window.cpp @@ -31,16 +31,18 @@ static gboolean Window_configure(GtkWidget *widget, GdkEvent *event, Window *win if(gtk_widget_get_realized(window->p.widget) == false) return false; GdkWindow *gdkWindow = gtk_widget_get_window(widget); - //update geometry settings GdkRectangle border, client; gdk_window_get_frame_extents(gdkWindow, &border); gdk_window_get_geometry(gdkWindow, 0, 0, &client.width, &client.height, 0); gdk_window_get_origin(gdkWindow, &client.x, &client.y); - settings.frameGeometryX = client.x - border.x; - settings.frameGeometryY = client.y - border.y; - settings.frameGeometryWidth = border.width - client.width; - settings.frameGeometryHeight = border.height - client.height; + if(window->state.fullScreen == false) { + //update geometry settings + settings.frameGeometryX = client.x - border.x; + settings.frameGeometryY = client.y - border.y; + settings.frameGeometryWidth = border.width - client.width; + settings.frameGeometryHeight = border.height - client.height; + } //move if(event->configure.x != window->p.lastConfigure.x @@ -167,10 +169,20 @@ void pWindow::setFullScreen(bool fullScreen) { } void pWindow::setGeometry(const Geometry &geometry) { + OS::processEvents(); + Geometry margin = frameMargin(); gtk_window_move(GTK_WINDOW(widget), geometry.x - margin.x, geometry.y - margin.y); - gtk_window_resize(GTK_WINDOW(widget), 1, 1); + +//GdkGeometry geom; +//geom.min_width = 1; +//geom.min_height = 1; +//gtk_window_set_geometry_hints(GTK_WINDOW(widget), GTK_WIDGET(widget), &geom, GDK_HINT_MIN_SIZE); + + gtk_window_set_policy(GTK_WINDOW(widget), true, true, false); gtk_widget_set_size_request(formContainer, geometry.width, geometry.height); + gtk_window_resize(GTK_WINDOW(widget), geometry.width + margin.width, geometry.height + margin.height); + foreach(layout, window.state.layout) { Geometry geometry = this->geometry(); geometry.x = geometry.y = 0; diff --git a/bsnes/phoenix/qt/platform.moc b/bsnes/phoenix/qt/platform.moc index c2c84d43..0ecd3114 100755 --- a/bsnes/phoenix/qt/platform.moc +++ b/bsnes/phoenix/qt/platform.moc @@ -1,7 +1,7 @@ /**************************************************************************** ** Meta object code from reading C++ file 'platform.moc.hpp' ** -** Created: Tue Aug 30 09:38:15 2011 +** Created: Thu Sep 8 17:34:23 2011 ** by: The Qt Meta Object Compiler version 62 (Qt 4.7.0) ** ** WARNING! All changes made in this file will be lost! diff --git a/bsnes/phoenix/qt/window.cpp b/bsnes/phoenix/qt/window.cpp index b8b031b8..ac185a93 100755 --- a/bsnes/phoenix/qt/window.cpp +++ b/bsnes/phoenix/qt/window.cpp @@ -97,6 +97,8 @@ void pWindow::setGeometry(const Geometry &geometry_) { setResizable(window.state.resizable); qtWindow->move(geometry.x - margin.x, geometry.y - margin.y); qtWindow->adjustSize(); + qtWindow->setMinimumSize(1u, 1u); + qtContainer->setMinimumSize(1u, 1u); foreach(layout, window.state.layout) { geometry = geometry_; diff --git a/bsnes/phoenix/windows/platform.cpp b/bsnes/phoenix/windows/platform.cpp index a9ac5c50..acbe4fc2 100755 --- a/bsnes/phoenix/windows/platform.cpp +++ b/bsnes/phoenix/windows/platform.cpp @@ -32,6 +32,7 @@ #include "widget/viewport.cpp" static void OS_keyboardProc(HWND, UINT, WPARAM, LPARAM); +static void OS_processDialogMessage(MSG&); static LRESULT CALLBACK OS_windowProc(HWND, UINT, WPARAM, LPARAM); Geometry pOS::availableGeometry() { @@ -129,13 +130,7 @@ string pOS::folderSelect(Window &parent, const string &path) { void pOS::main() { MSG msg; while(GetMessage(&msg, 0, 0, 0)) { - if(msg.message == WM_KEYDOWN || msg.message == WM_KEYUP) { - OS_keyboardProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); - } - if(!IsDialogMessage(GetParent(msg.hwnd) ? GetParent(msg.hwnd) : msg.hwnd, &msg)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } + OS_processDialogMessage(msg); } } @@ -148,17 +143,35 @@ void pOS::processEvents() { while(pendingEvents()) { MSG msg; if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { - if(msg.message == WM_KEYDOWN || msg.message == WM_KEYUP) { - OS_keyboardProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); - } - if(!IsDialogMessage(GetParent(msg.hwnd) ? GetParent(msg.hwnd) : msg.hwnd, &msg)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } + OS_processDialogMessage(msg); } } } +void OS_processDialogMessage(MSG &msg) { + if(msg.message == WM_KEYDOWN || msg.message == WM_KEYUP) { + OS_keyboardProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); + } + + wchar_t className[256]; + GetClassName(msg.hwnd, className, 255); + + //if this HWND accepts tabs to move between controls ... + if(!wcscmp(className, L"BUTTON") //Button, CheckBox, RadioBox + || !wcscmp(className, L"COMBOBOX") //ComboBox + || !wcscmp(className, L"EDIT") //HexEdit, LineEdit, TextEdit + || !wcscmp(className, L"SCROLLBAR") //HorizontalScrollBar, VerticalScrollBar + || !wcscmp(className, TRACKBAR_CLASS) //HorizontalSlider, VerticalSlider + || !wcscmp(className, WC_LISTVIEW) //ListView + ) { + //... return if the message is a dialog command + if(IsDialogMessage(msg.hwnd, &msg)) return; + } + + TranslateMessage(&msg); + DispatchMessage(&msg); +} + void pOS::quit() { PostQuitMessage(0); } diff --git a/bsnes/phoenix/windows/window.cpp b/bsnes/phoenix/windows/window.cpp index 5461c628..bc879f1f 100755 --- a/bsnes/phoenix/windows/window.cpp +++ b/bsnes/phoenix/windows/window.cpp @@ -8,6 +8,7 @@ void pWindow::append(Layout &layout) { } void pWindow::append(Menu &menu) { + menu.p.parentWindow = &window; updateMenu(); } @@ -192,7 +193,9 @@ void pWindow::updateMenu() { foreach(menu, window.state.menu) { menu.p.update(window); - AppendMenu(hmenu, MF_STRING | MF_POPUP, (UINT_PTR)menu.p.hmenu, utf16_t(menu.state.text)); + if(menu.visible()) { + AppendMenu(hmenu, MF_STRING | MF_POPUP, (UINT_PTR)menu.p.hmenu, utf16_t(menu.state.text)); + } } SetMenu(hwnd, window.state.menuVisible ? hmenu : 0); diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index e3aa96cc..a405b627 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -4,7 +4,7 @@ namespace SNES { namespace Info { static const char Name[] = "bsnes"; - static const char Version[] = "082.04"; + static const char Version[] = "082.05"; static const unsigned SerializerVersion = 21; } } diff --git a/bsnes/ui/base.hpp b/bsnes/ui/base.hpp index b85b4e32..08722007 100755 --- a/bsnes/ui/base.hpp +++ b/bsnes/ui/base.hpp @@ -24,12 +24,12 @@ using namespace ruby; #include "general/general.hpp" struct Application { + bool quit; + string title; string normalFont; string boldFont; - Timer timer; - void run(); Application(int argc, char **argv); ~Application(); diff --git a/bsnes/ui/general/file-browser.cpp b/bsnes/ui/general/file-browser.cpp new file mode 100755 index 00000000..04698cc8 --- /dev/null +++ b/bsnes/ui/general/file-browser.cpp @@ -0,0 +1,83 @@ +FileBrowser *fileBrowser = 0; + +FileBrowser::FileBrowser() { + setGeometry({ 128, 128, 640, 400 }); + setWidgetFont(application->normalFont); + + pathBrowse.setText("Browse ..."); + pathUp.setText(".."); + + layout.setMargin(5); + + append(layout); + layout.append(pathLayout, ~0, 0, 5); + pathLayout.append(pathEdit, ~0, 0, 5); + pathLayout.append(pathBrowse, 0, 0, 5); + pathLayout.append(pathUp, 0, 0); + layout.append(fileList, ~0, ~0); + + pathEdit.onActivate = [&] { + string path = pathEdit.text(); + path.transform("\\", "/"); + if(path.endswith("/") == false) path.append("/"); + setPath(path); + }; + + pathBrowse.onTick = [&] { + string path = OS::folderSelect(*this, activePath); + if(path != "") setPath(path); + }; + + pathUp.onTick = [&] { + if(activePath == "/") return; + string path = activePath; + path.rtrim<1>("/"); + path = dir(path); + setPath(path); + }; + + fileList.onActivate = { &FileBrowser::fileListActivate, this }; + + char path[PATH_MAX]; + auto unused = getcwd(path); + setPath(path); +} + +void FileBrowser::open(const string &title, const lstring &filterList, function callback) { + this->callback = callback; + this->filterList = filterList; + + setTitle(title); + setPath(activePath); + setVisible(); +} + +void FileBrowser::setPath(const string &path) { + activePath = path; + pathEdit.setText(activePath); + + fileList.reset(); + fileNameList.reset(); + + lstring contentsList = directory::contents(path); + foreach(fileName, contentsList) { + if(fileName.endswith("/")) { + fileNameList.append(fileName); + } else foreach(filter, filterList) { + if(fileName.wildcard(filter)) fileNameList.append(fileName); + } + } + + foreach(fileName, fileNameList) fileList.append(fileName); + fileList.setSelection(0); + fileList.setFocused(); +} + +void FileBrowser::fileListActivate() { + unsigned selection = fileList.selection(); + string fileName = fileNameList[selection]; + if(fileName.endswith("/")) return setPath({ activePath, fileName }); + + if(callback) callback({ activePath, fileName }); + setVisible(false); +} diff --git a/bsnes/ui/general/file-browser.hpp b/bsnes/ui/general/file-browser.hpp new file mode 100755 index 00000000..95376f53 --- /dev/null +++ b/bsnes/ui/general/file-browser.hpp @@ -0,0 +1,23 @@ +struct FileBrowser : Window { + VerticalLayout layout; + HorizontalLayout pathLayout; + LineEdit pathEdit; + Button pathBrowse; + Button pathUp; + ListView fileList; + lstring fileNameList; + + void open(const string &title, const lstring &filterList, function callback); + + FileBrowser(); + +private: + string activePath; + lstring filterList; + function callback; + + void setPath(const string &path); + void fileListActivate(); +}; + +extern FileBrowser *fileBrowser; diff --git a/bsnes/ui/general/general.cpp b/bsnes/ui/general/general.cpp index 1d067ea8..1c06a519 100755 --- a/bsnes/ui/general/general.cpp +++ b/bsnes/ui/general/general.cpp @@ -1,2 +1,3 @@ #include "../base.hpp" #include "main-window.cpp" +#include "file-browser.cpp" diff --git a/bsnes/ui/general/general.hpp b/bsnes/ui/general/general.hpp index 9f5a58c7..737238df 100755 --- a/bsnes/ui/general/general.hpp +++ b/bsnes/ui/general/general.hpp @@ -1 +1,2 @@ #include "main-window.hpp" +#include "file-browser.hpp" diff --git a/bsnes/ui/general/main-window.cpp b/bsnes/ui/general/main-window.cpp index 4ae57df8..3479af2e 100755 --- a/bsnes/ui/general/main-window.cpp +++ b/bsnes/ui/general/main-window.cpp @@ -31,6 +31,9 @@ MainWindow::MainWindow() { settingsSynchronizeAudio.setChecked(); settingsMuteAudio.setText("Mute Audio"); + toolsMenu.setText("Tools"); + toolsShrinkWindow.setText("Shrink Window"); + helpMenu.setText("Help"); helpAbout.setText("About ..."); @@ -61,6 +64,9 @@ MainWindow::MainWindow() { settingsMenu.append(settingsSynchronizeAudio); settingsMenu.append(settingsMuteAudio); + append(toolsMenu); + toolsMenu.append(toolsShrinkWindow); + append(helpMenu); helpMenu.append(helpAbout); @@ -74,25 +80,25 @@ MainWindow::MainWindow() { layout.append(viewport, { 0, 0, 512, 480 }); append(layout); - onClose = &OS::quit; - onSize = { &Utility::resizeMainWindow, utility }; - - cartridgeLoadSNES.onTick = [&] { - string filename = OS::fileLoad(*this, "/media/sdb1/root/snes_roms/", "SNES images (*.sfc)"); - if(filename == "") return; - interface->loadCartridgeSNES(filename); - }; + onClose = [&] { application->quit = true; }; + onSize = [&] { utility->resizeMainWindow(); }; cartridgeLoadNES.onTick = [&] { - string filename = OS::fileLoad(*this, "/media/sdb1/root/nes_images/", "NES images (*.nes)"); - if(filename == "") return; - interface->loadCartridgeNES(filename); + fileBrowser->open("Load NES Cartridge", { "*.nes" }, [](string filename) { + interface->loadCartridgeNES(filename); + }); + }; + + cartridgeLoadSNES.onTick = [&] { + fileBrowser->open("Load SNES Cartridge", { "*.sfc" }, [](string filename) { + interface->loadCartridgeSNES(filename); + }); }; cartridgeLoadGameBoy.onTick = [&] { - string filename = OS::fileLoad(*this, "/media/sdb1/root/gameboy_images/", "Game Boy images (*.gb, *.gbc)"); - if(filename == "") return; - interface->loadCartridgeGameBoy(filename); + fileBrowser->open("Load Game Boy Cartridge", { "*.gb", "*.gbc" }, [](string filename) { + interface->loadCartridgeGameBoy(filename); + }); }; nesPower.onTick = { &Interface::power, interface }; @@ -118,6 +124,8 @@ MainWindow::MainWindow() { dspaudio.setVolume(settingsMuteAudio.checked() ? 0.0 : 1.0); }; + toolsShrinkWindow.onTick = [&] { utility->resizeMainWindow(true); }; + helpAbout.onTick = [&] { MessageWindow::information(*this, { application->title, "\n\n", diff --git a/bsnes/ui/general/main-window.hpp b/bsnes/ui/general/main-window.hpp index a22cb9d5..a1ece516 100755 --- a/bsnes/ui/general/main-window.hpp +++ b/bsnes/ui/general/main-window.hpp @@ -29,6 +29,9 @@ struct MainWindow : Window { CheckItem settingsSynchronizeAudio; CheckItem settingsMuteAudio; + Menu toolsMenu; + Item toolsShrinkWindow; + Menu helpMenu; Item helpAbout; diff --git a/bsnes/ui/main.cpp b/bsnes/ui/main.cpp index 133c34e1..718d1a6f 100755 --- a/bsnes/ui/main.cpp +++ b/bsnes/ui/main.cpp @@ -3,12 +3,23 @@ Application *application = 0; nall::DSP dspaudio; -Application::Application(int argc, char **argv) { +void Application::run() { + interface->input_poll(); + + if(interface->loaded() == false) { + usleep(20 * 1000); + return; + } + + interface->run(); +} + +Application::Application(int argc, char **argv) : quit(false) { application = this; interface = new Interface; utility = new Utility; - title = "batch v000"; + title = "batch"; #if defined(PLATFORM_WIN) string videoDriver = "Direct3D", audioDriver = "XAudio2", inputDriver = "RawInput"; @@ -21,6 +32,7 @@ Application::Application(int argc, char **argv) { #endif mainWindow = new MainWindow; + fileBrowser = new FileBrowser; utility->setMode(Interface::Mode::None); mainWindow->setVisible(); @@ -47,30 +59,19 @@ Application::Application(int argc, char **argv) { input.set(Input::Handle, mainWindow->viewport.handle()); input.init(); - timer.onTimeout = { &Application::run, this }; - timer.setInterval(0); - timer.setEnabled(); - - OS::main(); + while(quit == false) { + OS::processEvents(); + Application::run(); + } } Application::~Application() { + delete fileBrowser; delete mainWindow; delete utility; delete interface; } -void Application::run() { - interface->input_poll(); - - if(interface->loaded() == false) { - usleep(20 * 1000); - return; - } - - interface->run(); -} - int main(int argc, char **argv) { new Application(argc, argv); delete application; diff --git a/bsnes/ui/utility/utility.cpp b/bsnes/ui/utility/utility.cpp index bd8a0cc0..d608f993 100755 --- a/bsnes/ui/utility/utility.cpp +++ b/bsnes/ui/utility/utility.cpp @@ -37,7 +37,7 @@ void Utility::setMode(Interface::Mode mode) { resizeMainWindow(); } -void Utility::resizeMainWindow() { +void Utility::resizeMainWindow(bool shrink) { Geometry geometry = mainWindow->geometry(); unsigned width = geometry.width, height = geometry.height; @@ -54,10 +54,18 @@ void Utility::resizeMainWindow() { width = width * maxM; height = height * maxM; - mainWindow->viewport.setGeometry({ - (geometry.width - width) / 2, (geometry.height - height) / 2, - width, height - }); + if(shrink == false) { + mainWindow->viewport.setGeometry({ + (geometry.width - width) / 2, (geometry.height - height) / 2, + width, height + }); + } else { + mainWindow->setGeometry({ geometry.x, geometry.y, width, height }); + mainWindow->viewport.setGeometry({ 0, 0, width, height }); + } +} + +void Utility::shrinkMainWindow() { } void Utility::toggleFullScreen() { diff --git a/bsnes/ui/utility/utility.hpp b/bsnes/ui/utility/utility.hpp index 2fc3a290..e226b42f 100755 --- a/bsnes/ui/utility/utility.hpp +++ b/bsnes/ui/utility/utility.hpp @@ -1,6 +1,7 @@ struct Utility { void setMode(Interface::Mode mode); - void resizeMainWindow(); + void resizeMainWindow(bool shrink = false); + void shrinkMainWindow(); void toggleFullScreen(); };