diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 2371ef69..1a4131ad 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -12,7 +12,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "101.05"; + static const string Version = "101.06"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/emulator/interface.hpp b/higan/emulator/interface.hpp index c54f5a53..f6cd0768 100644 --- a/higan/emulator/interface.hpp +++ b/higan/emulator/interface.hpp @@ -6,10 +6,7 @@ struct Interface { struct Information { string manufacturer; string name; - uint width; - uint height; bool overscan; - double aspectRatio; bool resettable; struct Capability { bool states; @@ -70,6 +67,9 @@ struct Interface { virtual auto title() -> string = 0; //video information + struct VideoSize { uint width, height; }; + virtual auto videoSize() -> VideoSize = 0; + virtual auto videoSize(uint width, uint height, bool arc) -> VideoSize = 0; virtual auto videoFrequency() -> double = 0; virtual auto videoColors() -> uint32 = 0; virtual auto videoColor(uint32 color) -> uint64 = 0; diff --git a/higan/fc/interface/interface.cpp b/higan/fc/interface/interface.cpp index 81ac8933..17de2fe3 100644 --- a/higan/fc/interface/interface.cpp +++ b/higan/fc/interface/interface.cpp @@ -10,10 +10,7 @@ Interface::Interface() { information.manufacturer = "Nintendo"; information.name = "Famicom"; - information.width = 256; - information.height = 240; information.overscan = true; - information.aspectRatio = 8.0 / 7.0; information.resettable = true; information.capability.states = true; @@ -54,6 +51,17 @@ auto Interface::title() -> string { return cartridge.title(); } +auto Interface::videoSize() -> VideoSize { + return {256, 240}; +} + +auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize { + uint w = 256 * (arc ? 8.0 / 7.0 : 1.0); + uint h = 240; + uint m = min(width / w, height / h); + return {w * m, h * m}; +} + auto Interface::videoFrequency() -> double { return 21477272.0 / (262.0 * 1364.0 - 4.0); } diff --git a/higan/fc/interface/interface.hpp b/higan/fc/interface/interface.hpp index 53989f46..c710aa82 100644 --- a/higan/fc/interface/interface.hpp +++ b/higan/fc/interface/interface.hpp @@ -25,9 +25,13 @@ struct Interface : Emulator::Interface { auto manifest() -> string override; auto title() -> string override; + + auto videoSize() -> VideoSize override; + auto videoSize(uint width, uint height, bool arc) -> VideoSize override; auto videoFrequency() -> double override; auto videoColors() -> uint32 override; auto videoColor(uint32 color) -> uint64 override; + auto audioFrequency() -> double override; auto loaded() -> bool override; diff --git a/higan/gb/interface/interface.cpp b/higan/gb/interface/interface.cpp index 37694ad4..354c25f4 100644 --- a/higan/gb/interface/interface.cpp +++ b/higan/gb/interface/interface.cpp @@ -11,10 +11,7 @@ Interface::Interface() { information.manufacturer = "Nintendo"; information.name = "Game Boy"; - information.width = 160; - information.height = 144; information.overscan = false; - information.aspectRatio = 1.0; information.resettable = false; information.capability.states = true; @@ -48,6 +45,17 @@ auto Interface::title() -> string { return cartridge.title(); } +auto Interface::videoSize() -> VideoSize { + return {160, 144}; +} + +auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize { + uint w = 160; + uint h = 144; + uint m = min(width / w, height / h); + return {w * m, h * m}; +} + auto Interface::videoFrequency() -> double { return 4194304.0 / (154.0 * 456.0); } diff --git a/higan/gb/interface/interface.hpp b/higan/gb/interface/interface.hpp index df85351a..fce0be53 100644 --- a/higan/gb/interface/interface.hpp +++ b/higan/gb/interface/interface.hpp @@ -24,9 +24,13 @@ struct Interface : Emulator::Interface { auto manifest() -> string override; auto title() -> string override; + + auto videoSize() -> VideoSize override; + auto videoSize(uint width, uint height, bool arc) -> VideoSize override; auto videoFrequency() -> double override; auto videoColors() -> uint32 override; auto videoColor(uint32 color) -> uint64 override; + auto audioFrequency() -> double override; auto loaded() -> bool override; diff --git a/higan/gba/interface/interface.cpp b/higan/gba/interface/interface.cpp index baebfa33..cb1a4803 100644 --- a/higan/gba/interface/interface.cpp +++ b/higan/gba/interface/interface.cpp @@ -10,10 +10,7 @@ Interface::Interface() { information.manufacturer = "Nintendo"; information.name = "Game Boy Advance"; - information.width = 240; - information.height = 160; information.overscan = false; - information.aspectRatio = 1.0; information.resettable = false; information.capability.states = true; @@ -49,6 +46,17 @@ auto Interface::title() -> string { return cartridge.title(); } +auto Interface::videoSize() -> VideoSize { + return {240, 160}; +} + +auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize { + uint w = 240; + uint h = 160; + uint m = min(width / w, height / h); + return {w * m, h * m}; +} + auto Interface::videoFrequency() -> double { return 16777216.0 / (228.0 * 1232.0); } diff --git a/higan/gba/interface/interface.hpp b/higan/gba/interface/interface.hpp index fc2a983f..f5eee4bb 100644 --- a/higan/gba/interface/interface.hpp +++ b/higan/gba/interface/interface.hpp @@ -22,9 +22,13 @@ struct Interface : Emulator::Interface { auto manifest() -> string override; auto title() -> string override; + + auto videoSize() -> VideoSize override; + auto videoSize(uint width, uint height, bool arc) -> VideoSize override; auto videoFrequency() -> double override; auto videoColors() -> uint32 override; auto videoColor(uint32 color) -> uint64 override; + auto audioFrequency() -> double override; auto loaded() -> bool override; diff --git a/higan/md/interface/interface.cpp b/higan/md/interface/interface.cpp index 35803183..1b39979f 100644 --- a/higan/md/interface/interface.cpp +++ b/higan/md/interface/interface.cpp @@ -10,10 +10,7 @@ Interface::Interface() { information.manufacturer = "Sega"; information.name = "Mega Drive"; - information.width = 320; - information.height = 240; information.overscan = true; - information.aspectRatio = 1.0; information.resettable = true; information.capability.states = false; @@ -52,6 +49,17 @@ auto Interface::title() -> string { return cartridge.title(); } +auto Interface::videoSize() -> VideoSize { + return {1280, 480}; +} + +auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize { + uint w = 320; + uint h = 240; + uint m = min(width / w, height / h); + return {w * m, h * m}; +} + auto Interface::videoFrequency() -> double { return 60.0; } diff --git a/higan/md/interface/interface.hpp b/higan/md/interface/interface.hpp index d3bd664b..37fa5862 100644 --- a/higan/md/interface/interface.hpp +++ b/higan/md/interface/interface.hpp @@ -23,9 +23,13 @@ struct Interface : Emulator::Interface { auto manifest() -> string override; auto title() -> string override; + + auto videoSize() -> VideoSize override; + auto videoSize(uint width, uint height, bool arc) -> VideoSize override; auto videoFrequency() -> double override; auto videoColors() -> uint32 override; auto videoColor(uint32 color) -> uint64 override; + auto audioFrequency() -> double override; auto loaded() -> bool override; diff --git a/higan/sfc/interface/interface.cpp b/higan/sfc/interface/interface.cpp index 84ea2f48..76d7a8bc 100644 --- a/higan/sfc/interface/interface.cpp +++ b/higan/sfc/interface/interface.cpp @@ -12,10 +12,7 @@ Interface::Interface() { information.manufacturer = "Nintendo"; information.name = "Super Famicom"; - information.width = 256; - information.height = 240; information.overscan = true; - information.aspectRatio = 8.0 / 7.0; information.resettable = true; information.capability.states = true; @@ -132,6 +129,17 @@ auto Interface::title() -> string { return cartridge.title(); } +auto Interface::videoSize() -> VideoSize { + return {512, 480}; +} + +auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize { + uint w = 256 * (arc ? 8.0 / 7.0 : 1.0); + uint h = 240; + uint m = min(width / w, height / h); + return {w * m, h * m}; +} + auto Interface::videoFrequency() -> double { switch(system.region()) { default: case System::Region::NTSC: return (system.colorburst() * 6.0) / (262.0 * 1364.0 - 4.0); diff --git a/higan/sfc/interface/interface.hpp b/higan/sfc/interface/interface.hpp index c2c2bf05..5a55b50a 100644 --- a/higan/sfc/interface/interface.hpp +++ b/higan/sfc/interface/interface.hpp @@ -40,9 +40,13 @@ struct Interface : Emulator::Interface { auto manifest() -> string override; auto title() -> string override; + + auto videoSize() -> VideoSize override; + auto videoSize(uint width, uint height, bool arc) -> VideoSize override; auto videoFrequency() -> double override; auto videoColors() -> uint32 override; auto videoColor(uint32 color) -> uint64 override; + auto audioFrequency() -> double override; auto loaded() -> bool override; diff --git a/higan/target-tomoko/presentation/presentation.cpp b/higan/target-tomoko/presentation/presentation.cpp index 7d1b2f2a..88e7240e 100644 --- a/higan/target-tomoko/presentation/presentation.cpp +++ b/higan/target-tomoko/presentation/presentation.cpp @@ -200,40 +200,37 @@ auto Presentation::updateEmulator() -> void { } auto Presentation::resizeViewport() -> void { - int width = emulator ? emulator->information.width : 256; - int height = emulator ? emulator->information.height : 240; - double stretch = emulator ? emulator->information.aspectRatio : 1.0; - if(stretch != 1.0) { - //aspect correction is always enabled in fullscreen mode - if(!fullScreen() && !settings["Video/AspectCorrection"].boolean()) stretch = 1.0; - } - - int scale = 2; + uint scale = 2; if(settings["Video/Scale"].text() == "Small" ) scale = 2; if(settings["Video/Scale"].text() == "Medium") scale = 3; if(settings["Video/Scale"].text() == "Large" ) scale = 4; - int windowWidth = 0, windowHeight = 0; + uint windowWidth = 0, windowHeight = 0; + bool aspectCorrection = true; if(!fullScreen()) { windowWidth = 320 * scale; windowHeight = 240 * scale; + aspectCorrection = settings["Video/AspectCorrection"].boolean(); } else { windowWidth = geometry().width(); windowHeight = geometry().height(); } - - int multiplier = min(windowWidth / (int)(width * stretch), windowHeight / height); - width = width * multiplier * stretch; - height = height * multiplier; - if(!fullScreen()) setSize({windowWidth, windowHeight}); - viewport.setGeometry({(windowWidth - width) / 2, (windowHeight - height) / 2, width, height}); - if(!emulator) drawSplashScreen(); + if(!emulator) { + viewport.setGeometry({0, 0, windowWidth, windowHeight}); + draw(Resource::Logo::higan); + } else { + auto videoSize = emulator->videoSize(windowWidth, windowHeight, aspectCorrection); + viewport.setGeometry({ + (windowWidth - videoSize.width) / 2, (windowHeight - videoSize.height) / 2, + videoSize.width, videoSize.height + }); + } } auto Presentation::toggleFullScreen() -> void { - if(fullScreen() == false) { + if(!fullScreen()) { menuBar.setVisible(false); statusBar.setVisible(false); setResizable(true); @@ -251,15 +248,33 @@ auto Presentation::toggleFullScreen() -> void { resizeViewport(); } -auto Presentation::drawSplashScreen() -> void { +auto Presentation::draw(image logo) -> void { if(!video) return; + uint32_t* output; - uint length; - if(video->lock(output, length, 256, 240)) { - for(auto y : range(240)) { - auto dp = output + y * (length >> 2); - for(auto x : range(256)) *dp++ = 0xff000000; + uint length = 0; + uint width = viewport.geometry().width(); + uint height = viewport.geometry().height(); + if(video->lock(output, length, width, height)) { + uint cx = (width - logo.width()) - 10; + uint cy = (height - logo.height()) - 10; + + image backdrop; + backdrop.allocate(width, height); + if(logo && !program->hasQuit) { + backdrop.sphericalGradient(0xff0000bf, 0xff000000, logo.width(), logo.height() / 2, width, height); + backdrop.impose(image::blend::sourceAlpha, cx, cy, logo, 0, 0, logo.width(), logo.height()); + } else { + backdrop.fill(0xff000000); } + + auto data = (uint32_t*)backdrop.data(); + for(auto y : range(height)) { + auto dp = output + y * (length >> 2); + auto sp = data + y * width; + for(auto x : range(width)) *dp++ = *sp++; + } + video->unlock(); video->refresh(); } diff --git a/higan/target-tomoko/presentation/presentation.hpp b/higan/target-tomoko/presentation/presentation.hpp index 4661c0d7..e55a5412 100644 --- a/higan/target-tomoko/presentation/presentation.hpp +++ b/higan/target-tomoko/presentation/presentation.hpp @@ -13,7 +13,7 @@ struct Presentation : Window { auto updateEmulator() -> void; auto resizeViewport() -> void; auto toggleFullScreen() -> void; - auto drawSplashScreen() -> void; + auto draw(image logo = {}) -> void; auto loadShaders() -> void; MenuBar menuBar{this}; diff --git a/higan/target-tomoko/program/medium.cpp b/higan/target-tomoko/program/medium.cpp index bb6c6f2b..1f4825fd 100644 --- a/higan/target-tomoko/program/medium.cpp +++ b/higan/target-tomoko/program/medium.cpp @@ -29,6 +29,7 @@ auto Program::loadMedium(Emulator::Interface& interface, const Emulator::Interfa } updateAudioDriver(); updateAudioEffects(); + presentation->draw(); emulator->power(); presentation->resizeViewport(); @@ -44,12 +45,14 @@ auto Program::loadMedium(Emulator::Interface& interface, const Emulator::Interfa auto Program::unloadMedium() -> void { if(!emulator) return; + presentation->draw(); toolsManager->cheatEditor.saveCheats(); emulator->unload(); emulator = nullptr; mediumPaths.reset(); - presentation->drawSplashScreen(); + presentation->resizeViewport(); + presentation->draw(Resource::Logo::higan); presentation->setTitle({"higan v", Emulator::Version}); presentation->systemMenu.setVisible(false); presentation->toolsMenu.setVisible(false); diff --git a/higan/target-tomoko/program/program.cpp b/higan/target-tomoko/program/program.cpp index b8931cec..59b65b14 100644 --- a/higan/target-tomoko/program/program.cpp +++ b/higan/target-tomoko/program/program.cpp @@ -31,7 +31,7 @@ Program::Program(string_vector args) { video->set(Video::Synchronize, settings["Video/Synchronize"].boolean()); if(!video->init()) video = Video::create("None"); - presentation->drawSplashScreen(); + presentation->draw(Resource::Logo::higan); audio = Audio::create(settings["Audio/Driver"].text()); audio->set(Audio::Device, settings["Audio/Device"].text()); @@ -87,6 +87,7 @@ auto Program::main() -> void { } auto Program::quit() -> void { + hasQuit = true; unloadMedium(); settings.quit(); inputManager->quit(); diff --git a/higan/target-tomoko/program/program.hpp b/higan/target-tomoko/program/program.hpp index 7372aa5d..4e75749b 100644 --- a/higan/target-tomoko/program/program.hpp +++ b/higan/target-tomoko/program/program.hpp @@ -36,6 +36,7 @@ struct Program : Emulator::Interface::Bind { auto updateAudioDriver() -> void; auto updateAudioEffects() -> void; + bool hasQuit = false; bool pause = false; vector emulators; diff --git a/higan/ws/interface/interface.cpp b/higan/ws/interface/interface.cpp index d08a5695..be376247 100644 --- a/higan/ws/interface/interface.cpp +++ b/higan/ws/interface/interface.cpp @@ -10,10 +10,7 @@ Interface::Interface() { information.manufacturer = "Bandai"; information.name = "WonderSwan"; - information.width = 224; //note: technically 224x144; but screen can be rotated - information.height = 224; //by using a square size; this can be done in the core information.overscan = false; - information.aspectRatio = 1.0; information.resettable = false; information.capability.states = true; @@ -54,6 +51,17 @@ auto Interface::title() -> string { return cartridge.information.title; } +auto Interface::videoSize() -> VideoSize { + return {224, 224}; +} + +auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize { + uint w = 224; + uint h = 224; + uint m = min(width / w, height / h); + return {w * m, h * m}; +} + auto Interface::videoFrequency() -> double { return 3072000.0 / (159.0 * 256.0); //~75.47hz } diff --git a/higan/ws/interface/interface.hpp b/higan/ws/interface/interface.hpp index 8ee538af..1119d6f3 100644 --- a/higan/ws/interface/interface.hpp +++ b/higan/ws/interface/interface.hpp @@ -24,9 +24,13 @@ struct Interface : Emulator::Interface { auto manifest() -> string override; auto title() -> string override; + + auto videoSize() -> VideoSize override; + auto videoSize(uint width, uint height, bool arc) -> VideoSize override; auto videoFrequency() -> double override; auto videoColors() -> uint32; auto videoColor(uint32 color) -> uint64; + auto audioFrequency() -> double override; auto loaded() -> bool override; diff --git a/hiro/core/core.hpp b/hiro/core/core.hpp index 7c235d27..002fefbf 100644 --- a/hiro/core/core.hpp +++ b/hiro/core/core.hpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -203,6 +204,8 @@ struct Position { Position(); Position(signed x, signed y); + template + Position(X x, Y y) : Position((signed)x, (signed)y) {} explicit operator bool() const; auto operator==(const Position& source) const -> bool; @@ -230,6 +233,8 @@ struct Size { Size(); Size(signed width, signed height); + template + Size(W width, H height) : Size((signed)width, (signed)height) {} explicit operator bool() const; auto operator==(const Size& source) const -> bool; @@ -261,6 +266,8 @@ struct Geometry { Geometry(); Geometry(Position position, Size size); Geometry(signed x, signed y, signed width, signed height); + template + Geometry(X x, Y y, W width, H height) : Geometry((signed)x, (signed)y, (signed)width, (signed)height) {} explicit operator bool() const; auto operator==(const Geometry& source) const -> bool; diff --git a/hiro/gtk/widget/widget.cpp b/hiro/gtk/widget/widget.cpp index 8d7a783d..e4474f71 100644 --- a/hiro/gtk/widget/widget.cpp +++ b/hiro/gtk/widget/widget.cpp @@ -41,7 +41,23 @@ auto pWidget::setFont(const Font& font) -> void { auto pWidget::setGeometry(Geometry geometry) -> void { if(!gtkWidget) return; if(gtkParent) gtk_fixed_move(GTK_FIXED(gtkParent), gtkWidget, geometry.x(), geometry.y()); - gtk_widget_set_size_request(gtkWidget, max(1, geometry.width()), max(1, geometry.height())); + if(geometry.width() < 1) geometry.setWidth (1); + if(geometry.height() < 1) geometry.setHeight(1); + gtk_widget_set_size_request(gtkWidget, geometry.width(), geometry.height()); + if(gtk_widget_get_realized(gtkWidget)) { + static bool locked = false; + if(!locked) { + locked = true; + auto time = chrono::millisecond(); + while(chrono::millisecond() - time < 20) { + gtk_main_iteration_do(false); + if(gtkWidget->allocation.width != geometry.width ()) continue; + if(gtkWidget->allocation.height != geometry.height()) continue; + break; + } + locked = false; + } + } self().doSize(); } diff --git a/hiro/gtk/window.cpp b/hiro/gtk/window.cpp index ef0d43c0..f4c7e050 100644 --- a/hiro/gtk/window.cpp +++ b/hiro/gtk/window.cpp @@ -266,6 +266,8 @@ auto pWindow::setFullScreen(bool fullScreen) -> void { gtk_window_unfullscreen(GTK_WINDOW(widget)); state().geometry = windowedGeometry; } + auto time = chrono::millisecond(); + while(chrono::millisecond() - time < 20) gtk_main_iteration_do(false); } auto pWindow::setGeometry(Geometry geometry) -> void { @@ -278,7 +280,12 @@ auto pWindow::setGeometry(Geometry geometry) -> void { gtk_window_set_geometry_hints(GTK_WINDOW(widget), GTK_WIDGET(widget), &geom, GDK_HINT_MIN_SIZE); gtk_widget_set_size_request(formContainer, geometry.width(), geometry.height()); + auto time1 = chrono::millisecond(); + while(chrono::millisecond() - time1 < 20) gtk_main_iteration_do(false); + gtk_window_resize(GTK_WINDOW(widget), geometry.width(), geometry.height() + _menuHeight() + _statusHeight()); + auto time2 = chrono::millisecond(); + while(chrono::millisecond() - time2 < 20) gtk_main_iteration_do(false); } auto pWindow::setModal(bool modal) -> void { diff --git a/hiro/gtk/window.hpp b/hiro/gtk/window.hpp index ac6fc841..132f294f 100644 --- a/hiro/gtk/window.hpp +++ b/hiro/gtk/window.hpp @@ -44,8 +44,8 @@ struct pWindow : pObject { GtkWidget* gtkMenu = nullptr; GtkWidget* gtkStatus = nullptr; GtkAllocation lastAllocation = {0}; - bool onSizePending = false; Geometry windowedGeometry{128, 128, 256, 256}; + bool onSizePending = false; }; }