From 95831d3675dc93900a0016c3276db60551d674d9 Mon Sep 17 00:00:00 2001 From: byuu <2107894+byuu@users.noreply.github.com> Date: Sun, 18 Aug 2019 03:20:14 +0900 Subject: [PATCH] v108.11 * added (primary-monitor only) fullscreen support for macOS * improved settings windows a bit * correct Program::focused() move from Video::exclusive()->fullScreen() --- bsnes/emulator/emulator.hpp | 2 +- bsnes/sfc/interface/configuration.cpp | 2 +- bsnes/sfc/interface/configuration.hpp | 2 +- bsnes/target-bsnes/program/game.cpp | 2 +- bsnes/target-bsnes/program/utility.cpp | 4 +- bsnes/target-bsnes/settings/drivers.cpp | 10 ++- bsnes/target-bsnes/settings/emulator.cpp | 6 +- bsnes/target-bsnes/settings/settings.cpp | 2 +- bsnes/target-bsnes/settings/settings.hpp | 14 ++-- ruby/video/cgl.cpp | 82 ++++++++++++++++++++++-- ruby/video/video.cpp | 6 +- 11 files changed, 99 insertions(+), 33 deletions(-) diff --git a/bsnes/emulator/emulator.hpp b/bsnes/emulator/emulator.hpp index 9f51bdc1..da49f0e7 100644 --- a/bsnes/emulator/emulator.hpp +++ b/bsnes/emulator/emulator.hpp @@ -29,7 +29,7 @@ using namespace nall; namespace Emulator { static const string Name = "bsnes"; - static const string Version = "108.10"; + static const string Version = "108.11"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org"; diff --git a/bsnes/sfc/interface/configuration.cpp b/bsnes/sfc/interface/configuration.cpp index 822a4992..d9392e7b 100644 --- a/bsnes/sfc/interface/configuration.cpp +++ b/bsnes/sfc/interface/configuration.cpp @@ -18,8 +18,8 @@ auto Configuration::process(Markup::Node document, bool load) -> void { bind(natural, "Hacks/CPU/Overclock", hacks.cpu.overclock); bind(boolean, "Hacks/PPU/Fast", hacks.ppu.fast); - bind(boolean, "Hacks/PPU/NoSpriteLimit", hacks.ppu.noSpriteLimit); bind(boolean, "Hacks/PPU/Deinterlace", hacks.ppu.deinterlace); + bind(boolean, "Hacks/PPU/NoSpriteLimit", hacks.ppu.noSpriteLimit); bind(natural, "Hacks/PPU/Mode7/Scale", hacks.ppu.mode7.scale); bind(boolean, "Hacks/PPU/Mode7/Perspective", hacks.ppu.mode7.perspective); bind(boolean, "Hacks/PPU/Mode7/Supersample", hacks.ppu.mode7.supersample); diff --git a/bsnes/sfc/interface/configuration.hpp b/bsnes/sfc/interface/configuration.hpp index 2ee9b68a..6ea850b8 100644 --- a/bsnes/sfc/interface/configuration.hpp +++ b/bsnes/sfc/interface/configuration.hpp @@ -30,8 +30,8 @@ struct Configuration { } cpu; struct PPU { bool fast = true; - bool noSpriteLimit = true; bool deinterlace = true; + bool noSpriteLimit = false; struct Mode7 { uint scale = 1; bool perspective = true; diff --git a/bsnes/target-bsnes/program/game.cpp b/bsnes/target-bsnes/program/game.cpp index 4958bcdc..c3b5b6e3 100644 --- a/bsnes/target-bsnes/program/game.cpp +++ b/bsnes/target-bsnes/program/game.cpp @@ -3,8 +3,8 @@ auto Program::load() -> void { emulator->configure("Hacks/CPU/Overclock", settings.emulator.hack.cpu.overclock); emulator->configure("Hacks/PPU/Fast", settings.emulator.hack.ppu.fast); - emulator->configure("Hacks/PPU/NoSpriteLimit", settings.emulator.hack.ppu.noSpriteLimit); emulator->configure("Hacks/PPU/Deinterlace", settings.emulator.hack.ppu.deinterlace); + emulator->configure("Hacks/PPU/NoSpriteLimit", settings.emulator.hack.ppu.noSpriteLimit); emulator->configure("Hacks/PPU/Mode7/Scale", settings.emulator.hack.ppu.mode7.scale); emulator->configure("Hacks/PPU/Mode7/Perspective", settings.emulator.hack.ppu.mode7.perspective); emulator->configure("Hacks/PPU/Mode7/Supersample", settings.emulator.hack.ppu.mode7.supersample); diff --git a/bsnes/target-bsnes/program/utility.cpp b/bsnes/target-bsnes/program/utility.cpp index bd101787..cf6a1ec7 100644 --- a/bsnes/target-bsnes/program/utility.cpp +++ b/bsnes/target-bsnes/program/utility.cpp @@ -74,8 +74,8 @@ auto Program::inactive() -> bool { } auto Program::focused() -> bool { - //exclusive mode creates its own top-level window: presentation window will not have focus - if(video.exclusive() || presentation.focused()) { + //full-screen mode creates its own top-level window: presentation window will not have focus + if(video.fullScreen() || presentation.focused()) { mute &= ~Mute::Unfocused; return true; } else { diff --git a/bsnes/target-bsnes/settings/drivers.cpp b/bsnes/target-bsnes/settings/drivers.cpp index aa9a3e71..2a949b74 100644 --- a/bsnes/target-bsnes/settings/drivers.cpp +++ b/bsnes/target-bsnes/settings/drivers.cpp @@ -8,13 +8,13 @@ auto DriverSettings::create() -> void { videoDriverUpdate.setText(videoDriverOption.selected().text() != video.driver() ? "Change" : "Reload"); }); videoDriverUpdate.setText("Change").onActivate([&] { videoDriverChange(); }); - videoMonitorLabel.setText("Fullscreen Monitor:").setToolTip( + videoMonitorLabel.setText("Fullscreen monitor:").setToolTip( "Sets which monitor video is sent to in fullscreen mode." ); videoMonitorOption.onChange([&] { videoMonitorChange(); }); videoFormatLabel.setText("Format:"); videoFormatOption.onChange([&] { videoFormatChange(); }); - videoExclusiveToggle.setText("Exclusive").setToolTip( + videoExclusiveToggle.setText("Exclusive mode").setToolTip( "Causes fullscreen mode to take over all monitors.\n" "This allows adaptive sync to work better and reduces input latency.\n" "However, multi-monitor users should turn this option off.\n" @@ -47,13 +47,12 @@ auto DriverSettings::create() -> void { videoSpacer.setColor({192, 192, 192}); audioLabel.setText("Audio").setFont(Font().setBold()); - audioLayout.setSize({2, 2}); audioDriverLabel.setText("Driver:"); audioDriverOption.onChange([&] { audioDriverUpdate.setText(audioDriverOption.selected().text() != audio.driver() ? "Change" : "Reload"); }); audioDriverUpdate.setText("Change").onActivate([&] { audioDriverChange(); }); - audioDeviceLabel.setText("Device:"); + audioDeviceLabel.setText("Output device:"); audioDeviceOption.onChange([&] { audioDeviceChange(); }); audioFrequencyLabel.setText("Frequency:"); audioFrequencyOption.onChange([&] { audioFrequencyChange(); }); @@ -92,7 +91,6 @@ auto DriverSettings::create() -> void { audioSpacer.setColor({192, 192, 192}); inputLabel.setText("Input").setFont(Font().setBold()); - inputLayout.setSize({2, 1}); inputDriverLabel.setText("Driver:"); inputDriverOption.onChange([&] { inputDriverUpdate.setText(inputDriverOption.selected().text() != input.driver() ? "Change" : "Reload"); @@ -167,7 +165,7 @@ auto DriverSettings::videoFormatChanged() -> void { item.setText(format); if(format == video.format()) item.setSelected(); } -//videoFormatOption.setEnabled(video.hasFormat()); + videoFormatOption.setEnabled(videoFormatOption.itemCount() > 1); setGeometry(geometry()); videoFormatChange(); } diff --git a/bsnes/target-bsnes/settings/emulator.cpp b/bsnes/target-bsnes/settings/emulator.cpp index 964a1f53..e020694c 100644 --- a/bsnes/target-bsnes/settings/emulator.cpp +++ b/bsnes/target-bsnes/settings/emulator.cpp @@ -35,13 +35,13 @@ auto EmulatorSettings::create() -> void { mode7Layout.setEnabled(true); } }).doToggle(); - noSpriteLimit.setText("No sprite limit").setChecked(settings.emulator.hack.ppu.noSpriteLimit).onToggle([&] { - settings.emulator.hack.ppu.noSpriteLimit = noSpriteLimit.checked(); - }); deinterlace.setText("Deinterlace").setChecked(settings.emulator.hack.ppu.deinterlace).onToggle([&] { settings.emulator.hack.ppu.deinterlace = deinterlace.checked(); emulator->configure("Hacks/PPU/Deinterlace", settings.emulator.hack.ppu.deinterlace); }); + noSpriteLimit.setText("No sprite limit").setChecked(settings.emulator.hack.ppu.noSpriteLimit).onToggle([&] { + settings.emulator.hack.ppu.noSpriteLimit = noSpriteLimit.checked(); + }); mode7Label.setText("HD Mode 7 (fast PPU only)").setFont(Font().setBold()); mode7ScaleLabel.setText("Scale:"); mode7Scale.append(ComboButtonItem().setText( "240p").setProperty("multiplier", 1)); diff --git a/bsnes/target-bsnes/settings/settings.cpp b/bsnes/target-bsnes/settings/settings.cpp index 50ab99eb..6eb1d992 100644 --- a/bsnes/target-bsnes/settings/settings.cpp +++ b/bsnes/target-bsnes/settings/settings.cpp @@ -114,8 +114,8 @@ auto Settings::process(bool load) -> void { bind(boolean, "Emulator/AutoLoadStateOnLoad", emulator.autoLoadStateOnLoad); bind(natural, "Emulator/Hack/CPU/Overclock", emulator.hack.cpu.overclock); bind(boolean, "Emulator/Hack/PPU/Fast", emulator.hack.ppu.fast); - bind(boolean, "Emulator/Hack/PPU/NoSpriteLimit", emulator.hack.ppu.noSpriteLimit); bind(boolean, "Emulator/Hack/PPU/Deinterlace", emulator.hack.ppu.deinterlace); + bind(boolean, "Emulator/Hack/PPU/NoSpriteLimit", emulator.hack.ppu.noSpriteLimit); bind(natural, "Emulator/Hack/PPU/Mode7/Scale", emulator.hack.ppu.mode7.scale); bind(boolean, "Emulator/Hack/PPU/Mode7/Perspective", emulator.hack.ppu.mode7.perspective); bind(boolean, "Emulator/Hack/PPU/Mode7/Supersample", emulator.hack.ppu.mode7.supersample); diff --git a/bsnes/target-bsnes/settings/settings.hpp b/bsnes/target-bsnes/settings/settings.hpp index 92d0c36d..5d7339c0 100644 --- a/bsnes/target-bsnes/settings/settings.hpp +++ b/bsnes/target-bsnes/settings/settings.hpp @@ -100,8 +100,8 @@ struct Settings : Markup::Node { } cpu; struct PPU { bool fast = true; - bool noSpriteLimit = true; bool deinterlace = true; + bool noSpriteLimit = false; struct Mode7 { uint scale = 1; bool perspective = true; @@ -334,8 +334,8 @@ public: Label ppuLabel{this, Size{~0, 0}, 2}; HorizontalLayout ppuLayout{this, Size{~0, 0}}; CheckLabel fastPPU{&ppuLayout, Size{0, 0}}; - CheckLabel noSpriteLimit{&ppuLayout, Size{0, 0}}; CheckLabel deinterlace{&ppuLayout, Size{0, 0}}; + CheckLabel noSpriteLimit{&ppuLayout, Size{0, 0}}; Label mode7Label{this, Size{~0, 0}, 2}; HorizontalLayout mode7Layout{this, Size{~0, 0}}; Label mode7ScaleLabel{&mode7Layout, Size{0, 0}}; @@ -392,14 +392,14 @@ public: CheckLabel videoFlushToggle{&videoToggleLayout, Size{0, 0}}; Canvas videoSpacer{this, Size{~0, 1}}; Label audioLabel{this, Size{~0, 0}, 2}; - TableLayout audioLayout{this, Size{~0, 0}}; - Label audioDriverLabel{&audioLayout, Size{0, 0}}; + VerticalLayout audioLayout{this, Size{~0, 0}}; HorizontalLayout audioDriverLayout{&audioLayout, Size{~0, 0}}; + Label audioDriverLabel{&audioDriverLayout, Size{0, 0}}; ComboButton audioDriverOption{&audioDriverLayout, Size{0, 0}}; Button audioDriverUpdate{&audioDriverLayout, Size{0, 0}}; Label audioDriverActive{&audioDriverLayout, Size{0, 0}}; - Label audioDeviceLabel{&audioLayout, Size{0, 0}}; HorizontalLayout audioPropertyLayout{&audioLayout, Size{~0, 0}}; + Label audioDeviceLabel{&audioPropertyLayout, Size{0, 0}}; ComboButton audioDeviceOption{&audioPropertyLayout, Size{0, 0}}; Label audioFrequencyLabel{&audioPropertyLayout, Size{0, 0}}; ComboButton audioFrequencyOption{&audioPropertyLayout, Size{0, 0}}; @@ -411,9 +411,9 @@ public: CheckLabel audioDynamicToggle{&audioToggleLayout, Size{0, 0}}; Canvas audioSpacer{this, Size{~0, 1}}; Label inputLabel{this, Size{~0, 0}, 2}; - TableLayout inputLayout{this, Size{~0, 0}}; - Label inputDriverLabel{&inputLayout, Size{0, 0}}; + VerticalLayout inputLayout{this, Size{~0, 0}}; HorizontalLayout inputDriverLayout{&inputLayout, Size{~0, 0}}; + Label inputDriverLabel{&inputDriverLayout, Size{0, 0}}; ComboButton inputDriverOption{&inputDriverLayout, Size{0, 0}}; Button inputDriverUpdate{&inputDriverLayout, Size{0, 0}}; Label inputDriverActive{&inputDriverLayout, Size{0, 0}}; diff --git a/ruby/video/cgl.cpp b/ruby/video/cgl.cpp index 321eb26f..1c9d9d6d 100755 --- a/ruby/video/cgl.cpp +++ b/ruby/video/cgl.cpp @@ -9,6 +9,16 @@ struct VideoCGL; } -(id) initWith:(VideoCGL*)video pixelFormat:(NSOpenGLPixelFormat*)pixelFormat; -(void) reshape; +-(BOOL) acceptsFirstResponder; +@end + +@interface RubyWindowCGL : NSWindow { +@public + VideoCGL* video; +} +-(id) initWith:(VideoCGL*)video; +-(BOOL) canBecomeKeyWindow; +-(BOOL) canBecomeMainWindow; @end struct VideoCGL : VideoDriver, OpenGL { @@ -23,11 +33,16 @@ struct VideoCGL : VideoDriver, OpenGL { auto driver() -> string override { return "OpenGL 3.2"; } auto ready() -> bool override { return _ready; } + auto hasFullScreen() -> bool override { return true; } auto hasContext() -> bool override { return true; } auto hasBlocking() -> bool override { return true; } auto hasFlush() -> bool override { return true; } auto hasShader() -> bool override { return true; } + auto setFullScreen(bool fullScreen) -> bool override { + return initialize(); + } + auto setContext(uintptr context) -> bool override { return initialize(); } @@ -100,9 +115,16 @@ struct VideoCGL : VideoDriver, OpenGL { private: auto initialize() -> bool { terminate(); - if(!self.context) return false; + if(!self.fullScreen && !self.context) return false; @autoreleasepool { + if(self.fullScreen) { + window = [[RubyWindowCGL alloc] initWith:this]; + [window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; + [window toggleFullScreen:nil]; + //[NSApp setPresentationOptions:NSApplicationPresentationFullScreen]; + } + NSOpenGLPixelFormatAttribute attributeList[] = { NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, NSOpenGLPFAColorSize, 24, @@ -111,7 +133,7 @@ private: 0 }; - auto context = (NSView*)self.context; + auto context = self.fullScreen ? [window contentView] : (NSView*)self.context; auto size = [context frame].size; auto format = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attributeList] autorelease]; auto openGLContext = [[[NSOpenGLContext alloc] initWithFormat:format shareContext:nil] autorelease]; @@ -123,7 +145,7 @@ private: [view setWantsBestResolutionOpenGLSurface:YES]; [context addSubview:view]; [openGLContext setView:view]; - + [[view window] makeFirstResponder:view]; [view lockFocus]; OpenGL::initialize(self.shader); @@ -142,15 +164,26 @@ private: _ready = false; OpenGL::terminate(); - if(!view) return; @autoreleasepool { - [view removeFromSuperview]; - [view release]; - view = nil; + if(view) { + [view removeFromSuperview]; + [view release]; + view = nil; + } + + if(window) { + //[NSApp setPresentationOptions:NSApplicationPresentationDefault]; + [window toggleFullScreen:nil]; + [window setCollectionBehavior:NSWindowCollectionBehaviorDefault]; + [window close]; + [window release]; + window = nil; + } } } RubyVideoCGL* view = nullptr; + RubyWindowCGL* window = nullptr; bool _ready = false; }; @@ -168,4 +201,39 @@ private: video->output(0, 0); } +-(BOOL) acceptsFirstResponder { + return YES; +} + +-(void) keyDown:(NSEvent*)event { +} + +-(void) keyUp:(NSEvent*)event { +} + +@end + +@implementation RubyWindowCGL : NSWindow + +-(id) initWith:(VideoCGL*)videoPointer { + auto primaryRect = [[[NSScreen screens] objectAtIndex:0] frame]; + if(self = [super initWithContentRect:primaryRect styleMask:0 backing:NSBackingStoreBuffered defer:YES]) { + video = videoPointer; + [self setDelegate:self]; + [self setReleasedWhenClosed:NO]; + [self setAcceptsMouseMovedEvents:YES]; + [self setTitle:@""]; + [self makeKeyAndOrderFront:nil]; + } + return self; +} + +-(BOOL) canBecomeKeyWindow { + return YES; +} + +-(BOOL) canBecomeMainWindow { + return YES; +} + @end diff --git a/ruby/video/video.cpp b/ruby/video/video.cpp index c673f8a3..cf377ae7 100644 --- a/ruby/video/video.cpp +++ b/ruby/video/video.cpp @@ -327,14 +327,14 @@ auto Video::hasMonitors() -> vector { auto displayID = [screenID unsignedIntValue]; auto displayPort = CGDisplayIOServicePort(displayID); auto dictionary = IODisplayCreateInfoDictionary(displayPort, 0); - if(auto names = CFDictionaryGetValue(dictionary, CFSTR(kDisplayProductName))) { + if(auto names = (CFDictionaryRef)CFDictionaryGetValue(dictionary, CFSTR(kDisplayProductName))) { auto languageKeys = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - CFDictionaryApplyFunction((CFDictionaryRef)names, MonitorKeyArrayCallback, (void*)languageKeys); + CFDictionaryApplyFunction(names, MonitorKeyArrayCallback, (void*)languageKeys); auto orderLanguageKeys = CFBundleCopyPreferredLocalizationsFromArray(languageKeys); CFRelease(languageKeys); if(orderLanguageKeys && CFArrayGetCount(orderLanguageKeys)) { auto languageKey = CFArrayGetValueAtIndex(orderLanguageKeys, 0); - auto localName = CFDictionaryGetValue((CFDictionaryRef)names, languageKey); + auto localName = CFDictionaryGetValue(names, languageKey); monitor.name = {1 + monitors.size(), ": ", [(__bridge NSString*)localName UTF8String]}; CFRelease(localName); }