From 3ed18a76da22c7125d55b3e5d7edcb067725e9b5 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sun, 30 May 2021 20:55:04 +0300 Subject: [PATCH] Added optional OSD (Cocoa) --- Cocoa/AppDelegate.m | 6 +-- Cocoa/Document.h | 2 + Cocoa/Document.m | 13 +++++ Cocoa/Document.xib | 7 ++- Cocoa/GBOSDView.h | 6 +++ Cocoa/GBOSDView.m | 104 ++++++++++++++++++++++++++++++++++++ Cocoa/GBPreferencesWindow.h | 2 +- Cocoa/GBPreferencesWindow.m | 20 ++++++- Cocoa/GBView.h | 2 + Cocoa/GBView.m | 10 ++++ Cocoa/GBVisualizerView.h | 8 --- Cocoa/GBVisualizerView.m | 8 --- Cocoa/Preferences.xib | 46 ++++++++++------ Core/gb.c | 68 +++++++++++++++++++++++ Core/gb.h | 5 ++ Core/save_state.c | 6 +-- Makefile | 2 +- SDL/main.c | 6 +-- libretro/Makefile | 2 +- libretro/jni/Android.mk | 2 +- libretro/libretro.c | 4 +- 21 files changed, 276 insertions(+), 53 deletions(-) create mode 100644 Cocoa/GBOSDView.h create mode 100644 Cocoa/GBOSDView.m diff --git a/Cocoa/AppDelegate.m b/Cocoa/AppDelegate.m index 627a7b6..108a5c8 100644 --- a/Cocoa/AppDelegate.m +++ b/Cocoa/AppDelegate.m @@ -7,8 +7,6 @@ #import #define UPDATE_SERVER "https://sameboy.github.io" -#define str(x) #x -#define xstr(x) str(x) static uint32_t color_to_int(NSColor *color) { @@ -186,7 +184,7 @@ static uint32_t color_to_int(NSColor *color) } NSString *changes = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - NSRange cutoffRange = [changes rangeOfString:@""]; + NSRange cutoffRange = [changes rangeOfString:@""]; if (cutoffRange.location != NSNotFound) { changes = [changes substringToIndex:cutoffRange.location]; } @@ -252,7 +250,7 @@ static uint32_t color_to_int(NSColor *color) if (components.count != 2) return; _lastVersion = components[0]; _updateURL = components[1]; - if (![@xstr(VERSION) isEqualToString:_lastVersion] && + if (![@GB_VERSION isEqualToString:_lastVersion] && ![[[NSUserDefaults standardUserDefaults] stringForKey:@"GBSkippedVersion"] isEqualToString:_lastVersion]) { [self updateFound]; } diff --git a/Cocoa/Document.h b/Cocoa/Document.h index 19228e5..d6f89de 100644 --- a/Cocoa/Document.h +++ b/Cocoa/Document.h @@ -3,6 +3,7 @@ #include "GBImageView.h" #include "GBSplitView.h" #include "GBVisualizerView.h" +#include "GBOSDView.h" @class GBCheatWindowController; @@ -49,6 +50,7 @@ @property (strong) IBOutlet NSButton *gbsRewindButton; @property (strong) IBOutlet NSSegmentedControl *gbsNextPrevButton; @property (strong) IBOutlet GBVisualizerView *gbsVisualizer; +@property (strong) IBOutlet GBOSDView *osdView; -(uint8_t) readMemory:(uint16_t) addr; -(void) writeMemory:(uint16_t) addr value:(uint8_t)value; diff --git a/Cocoa/Document.m b/Cocoa/Document.m index 010cff7..c434934 100644 --- a/Cocoa/Document.m +++ b/Cocoa/Document.m @@ -314,6 +314,7 @@ static void infraredStateChanged(GB_gameboy_t *gb, bool on) self.mainWindow.contentView.bounds.size.width < GB_get_screen_height(&gb)) { [self.mainWindow zoom:nil]; } + self.osdView.usesSGBScale = GB_get_screen_width(&gb) == 256; } - (void) vblank @@ -344,6 +345,7 @@ static void infraredStateChanged(GB_gameboy_t *gb, bool on) } if (self.view.isRewinding) { rewind = true; + [self.osdView displayText:@"Rewinding..."]; } } @@ -621,6 +623,10 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency) [(GBMemoryByteArray *)(hex_controller.byteArray) setSelectedBank:0]; [self hexUpdateBank:self.memoryBankInput ignoreErrors:true]; } + + char title[17]; + GB_get_rom_title(&gb, title); + [self.osdView displayText:[NSString stringWithFormat:@"SameBoy v" GB_VERSION "\n%s\n%08X", title, GB_get_rom_crc32(&gb)]]; } - (IBAction)togglePause:(id)sender @@ -780,6 +786,7 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency) [self initCommon]; self.view.gb = &gb; + self.view.osdView = _osdView; [self.view screenSizeChanged]; if ([self loadROM]) { _mainWindow.alphaValue = 0; // Hack hack ugly hack @@ -1284,6 +1291,9 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency) [GBWarningPopover popoverWithContents:@"Failed to write save state." onWindow:self.mainWindow]; NSBeep(); } + else { + [self.osdView displayText:@"State saved"]; + } } - (int)loadStateFile:(const char *)path noErrorOnNotFound:(bool)noErrorOnFileNotFound; @@ -1301,6 +1311,9 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency) if (result) { NSBeep(); } + else { + [self.osdView displayText:@"State loaded"]; + } if (error) { [GBWarningPopover popoverWithContents:error onWindow:self.mainWindow]; } diff --git a/Cocoa/Document.xib b/Cocoa/Document.xib index a2cf5ee..ec2b450 100644 --- a/Cocoa/Document.xib +++ b/Cocoa/Document.xib @@ -25,6 +25,7 @@ + @@ -65,6 +66,10 @@ + + + + @@ -978,7 +983,7 @@ - + diff --git a/Cocoa/GBOSDView.h b/Cocoa/GBOSDView.h new file mode 100644 index 0000000..4771d2f --- /dev/null +++ b/Cocoa/GBOSDView.h @@ -0,0 +1,6 @@ +#import + +@interface GBOSDView : NSView +@property bool usesSGBScale; +- (void)displayText:(NSString *)text; +@end diff --git a/Cocoa/GBOSDView.m b/Cocoa/GBOSDView.m new file mode 100644 index 0000000..710229e --- /dev/null +++ b/Cocoa/GBOSDView.m @@ -0,0 +1,104 @@ +#import "GBOSDView.h" + +@implementation GBOSDView +{ + bool _usesSGBScale; + NSString *_text; + double _animation; + NSTimer *_timer; +} + +- (void)setUsesSGBScale:(bool)usesSGBScale +{ + _usesSGBScale = usesSGBScale; + [self setNeedsDisplay:true]; +} + +- (bool)usesSGBScale +{ + return _usesSGBScale; +} + +- (void)displayText:(NSString *)text +{ + if (![[NSUserDefaults standardUserDefaults] boolForKey:@"GBOSDEnabled"]) return; + dispatch_async(dispatch_get_main_queue(), ^{ + if (![_text isEqualToString:text]) { + [self setNeedsDisplay:true]; + } + _text = text; + self.alphaValue = 1.0; + _animation = 2.5; + // Longer strings should appear longer + if ([_text rangeOfString:@"\n"].location != NSNotFound) { + _animation += 4; + } + [_timer invalidate]; + self.hidden = false; + _timer = [NSTimer scheduledTimerWithTimeInterval:0.025 target:self selector:@selector(animate) userInfo:nil repeats:true]; + }); +} + +- (void)animate +{ + _animation -= 0.1; + if (_animation < 1.0) { + self.alphaValue = _animation; + }; + if (_animation == 0) { + self.hidden = true; + [_timer invalidate]; + _text = nil; + } +} + +- (void)drawRect:(NSRect)dirtyRect +{ + [super drawRect:dirtyRect]; + if (!_text.length) return; + + double fontSize = 8; + NSSize size = self.frame.size; + if (_usesSGBScale) { + fontSize *= MIN(size.width / 256, size.height / 224); + } + else { + fontSize *= MIN(size.width / 160, size.height / 144); + } + + NSFont *font = [NSFont boldSystemFontOfSize:fontSize]; + + /* The built in stroke attribute uses an inside stroke, which is typographically terrible. + We'll use a naïve manual stroke instead which looks better. */ + + NSDictionary *attributes = @{ + NSFontAttributeName: font, + NSForegroundColorAttributeName: [NSColor blackColor], + }; + + NSAttributedString *text = [[NSAttributedString alloc] initWithString:_text attributes:attributes]; + + [text drawAtPoint:NSMakePoint(fontSize + 1, fontSize + 0)]; + [text drawAtPoint:NSMakePoint(fontSize - 1, fontSize + 0)]; + [text drawAtPoint:NSMakePoint(fontSize + 0, fontSize + 1)]; + [text drawAtPoint:NSMakePoint(fontSize + 0, fontSize - 1)]; + + // The uses of sqrt(2)/2, which is more correct, results in severe ugly-looking rounding errors + if (self.window.screen.backingScaleFactor > 1) { + [text drawAtPoint:NSMakePoint(fontSize + 0.5, fontSize + 0.5)]; + [text drawAtPoint:NSMakePoint(fontSize - 0.5, fontSize + 0.5)]; + [text drawAtPoint:NSMakePoint(fontSize - 0.5, fontSize - 0.5)]; + [text drawAtPoint:NSMakePoint(fontSize + 0.5, fontSize - 0.5)]; + } + + attributes = @{ + NSFontAttributeName: font, + NSForegroundColorAttributeName: [NSColor whiteColor], + }; + + text = [[NSAttributedString alloc] initWithString:_text attributes:attributes]; + + [text drawAtPoint:NSMakePoint(fontSize, fontSize)]; +} + +@end diff --git a/Cocoa/GBPreferencesWindow.h b/Cocoa/GBPreferencesWindow.h index 16e36e2..260ebf9 100644 --- a/Cocoa/GBPreferencesWindow.h +++ b/Cocoa/GBPreferencesWindow.h @@ -27,5 +27,5 @@ @property (nonatomic, weak) IBOutlet NSPopUpButton *playerListButton; @property (nonatomic, weak) IBOutlet NSButton *autoUpdatesCheckbox; @property (weak) IBOutlet NSSlider *volumeSlider; - +@property (weak) IBOutlet NSButton *OSDCheckbox; @end diff --git a/Cocoa/GBPreferencesWindow.m b/Cocoa/GBPreferencesWindow.m index 4398ba3..96e9c16 100644 --- a/Cocoa/GBPreferencesWindow.m +++ b/Cocoa/GBPreferencesWindow.m @@ -31,7 +31,7 @@ NSSlider *_interferenceSlider; NSSlider *_volumeSlider; NSButton *_autoUpdatesCheckbox; - + NSButton *_OSDCheckbox; } + (NSArray *)filterList @@ -741,4 +741,22 @@ _autoUpdatesCheckbox = autoUpdatesCheckbox; [_autoUpdatesCheckbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBAutoUpdatesEnabled"]]; } + +- (NSButton *)OSDCheckbox +{ + return _OSDCheckbox; +} + +- (void)setOSDCheckbox:(NSButton *)OSDCheckbox +{ + _OSDCheckbox = OSDCheckbox; + [_OSDCheckbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBOSDEnabled"]]; +} + +- (IBAction)changeOSDEnabled:(id)sender +{ + [[NSUserDefaults standardUserDefaults] setBool:[(NSButton *)sender state] == NSOnState + forKey:@"GBOSDEnabled"]; + +} @end diff --git a/Cocoa/GBView.h b/Cocoa/GBView.h index f9aab83..cd6a539 100644 --- a/Cocoa/GBView.h +++ b/Cocoa/GBView.h @@ -1,6 +1,7 @@ #import #include #import +#import "GBOSDView.h" @class Document; typedef enum { @@ -20,6 +21,7 @@ typedef enum { @property (nonatomic, getter=isMouseHidingEnabled) BOOL mouseHidingEnabled; @property (nonatomic) bool isRewinding; @property (nonatomic, strong) NSView *internalView; +@property (weak) GBOSDView *osdView; - (void) createInternalView; - (uint32_t *)currentBuffer; - (uint32_t *)previousBuffer; diff --git a/Cocoa/GBView.m b/Cocoa/GBView.m index 04b3543..9d4ccbc 100644 --- a/Cocoa/GBView.m +++ b/Cocoa/GBView.m @@ -117,6 +117,7 @@ static const uint8_t workboy_vk_to_key[] = { NSEventModifierFlags previousModifiers; JOYController *lastController; GB_frame_blending_mode_t _frameBlendingMode; + bool _turbo; } + (instancetype)alloc @@ -283,6 +284,12 @@ static const uint8_t workboy_vk_to_key[] = { } } } + if (clockMultiplier > 1 || _turbo) { + [self.osdView displayText:@"Fast forwarding..."]; + } + else if (clockMultiplier < 1) { + [self.osdView displayText:@"Slow motion..."]; + } current_buffer = (current_buffer + 1) % self.numberOfBuffers; } @@ -329,6 +336,7 @@ static const uint8_t workboy_vk_to_key[] = { else { GB_set_turbo_mode(_gb, true, self.isRewinding); } + _turbo = true; analogClockMultiplierValid = false; break; @@ -336,6 +344,7 @@ static const uint8_t workboy_vk_to_key[] = { if (!self.document.partner) { self.isRewinding = true; GB_set_turbo_mode(_gb, false, false); + _turbo = false; } break; @@ -401,6 +410,7 @@ static const uint8_t workboy_vk_to_key[] = { else { GB_set_turbo_mode(_gb, false, false); } + _turbo = false; analogClockMultiplierValid = false; break; diff --git a/Cocoa/GBVisualizerView.h b/Cocoa/GBVisualizerView.h index 43cda4b..5ee4638 100644 --- a/Cocoa/GBVisualizerView.h +++ b/Cocoa/GBVisualizerView.h @@ -1,11 +1,3 @@ -// -// GBVisualizerView.h -// SameBoySDL -// -// Created by Lior Halphon on 7/4/21. -// Copyright © 2021 Lior Halphon. All rights reserved. -// - #import #include diff --git a/Cocoa/GBVisualizerView.m b/Cocoa/GBVisualizerView.m index c09cfe1..61688e6 100644 --- a/Cocoa/GBVisualizerView.m +++ b/Cocoa/GBVisualizerView.m @@ -1,11 +1,3 @@ -// -// GBVisualizerView.m -// SameBoySDL -// -// Created by Lior Halphon on 7/4/21. -// Copyright © 2021 Lior Halphon. All rights reserved. -// - #import "GBVisualizerView.h" #include diff --git a/Cocoa/Preferences.xib b/Cocoa/Preferences.xib index ad510c8..754a548 100644 --- a/Cocoa/Preferences.xib +++ b/Cocoa/Preferences.xib @@ -67,6 +67,7 @@ + @@ -97,11 +98,11 @@ - + - + @@ -110,7 +111,7 @@ - + @@ -147,7 +148,7 @@ - + @@ -156,7 +157,7 @@ - + @@ -178,7 +179,7 @@ - + @@ -187,7 +188,7 @@ - + @@ -207,7 +208,7 @@ - + @@ -216,7 +217,7 @@ - + @@ -237,7 +238,7 @@ - + @@ -246,7 +247,7 @@ - + @@ -266,7 +267,7 @@ - + @@ -274,7 +275,7 @@ - + @@ -283,7 +284,7 @@ + - + @@ -583,7 +595,7 @@ - + @@ -734,7 +746,7 @@