Completed log viewer

This commit is contained in:
Akop Karapetyan 2019-10-24 16:04:14 -07:00
parent bbc4a7d27b
commit a56d7ab028
5 changed files with 119 additions and 53 deletions

View File

@ -33,7 +33,7 @@ static AppDelegate *sharedInstance = nil;
- (void) dealloc
{
_runloop.delegate = nil;
[_runloop removeObserver:self];
screen.delegate = nil;
_video.delegate = nil;
}
@ -51,7 +51,7 @@ static AppDelegate *sharedInstance = nil;
tbAccessory.layoutAttribute = NSLayoutAttributeRight;
_runloop = [FBMainThread new];
_runloop.delegate = self;
[_runloop addObserver:self];
_video = [FBVideo new];
_video.delegate = screen;
screen.delegate = self;
@ -96,6 +96,9 @@ static AppDelegate *sharedInstance = nil;
{
NSLog(@"windowWillClose");
[_runloop cancel];
NSLog(@"Emulator window closed; shutting down");
[[NSApplication sharedApplication] terminate:nil];
}
- (NSSize) windowWillResize:(NSWindow *) sender
@ -126,11 +129,6 @@ static AppDelegate *sharedInstance = nil;
return frameSize;
}
- (BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *) sender
{
return YES;
}
- (BOOL) application:(NSApplication *) sender
openFile:(NSString *) filename
{

View File

@ -8,7 +8,9 @@
#import <Cocoa/Cocoa.h>
@interface FBLogViewerController : NSWindowController<NSWindowDelegate>
#import "FBMainThread.h"
@interface FBLogViewerController : NSWindowController<NSWindowDelegate, FBMainThreadDelegate>
{
IBOutlet NSTextView *textView;
}

View File

@ -10,64 +10,93 @@
#import "AppDelegate.h"
@interface FBLogViewerController()
@interface NSString (LogFormatter)
- (void) loadText:(NSString *) text;
- (NSMutableAttributedString *) applyFormat;
@end
@implementation FBLogViewerController
{
NSFont *plainFont;
NSFont *boldFont;
NSMutableAttributedString *logContent;
}
- (id) init
{
if (self = [super initWithWindowNibName:@"LogViewer"]) {
plainFont = [NSFont fontWithName:@"Menlo" size:11];
boldFont = [NSFont fontWithName:@"Menlo-Bold" size:11];
logContent = [NSMutableAttributedString new];
}
return self;
}
- (void) awakeFromNib
{
[AppDelegate.sharedInstance.runloop addObserver:self];
}
- (void) dealloc
{
[AppDelegate.sharedInstance.runloop removeObserver:self];
}
#pragma mark - NSWindowController
- (void) windowDidLoad
{
[super windowDidLoad];
[self loadText:AppDelegate.sharedInstance.runloop.log];
[logContent appendAttributedString:[AppDelegate.sharedInstance.runloop.log applyFormat]];
textView.textStorage.attributedString = logContent;
}
#pragma mark - Private
#pragma mark - FBMainThreadDelegate
- (void) loadText:(NSString *) text
- (void) logDidUpdate:(NSString *) message
{
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:text];
[logContent appendAttributedString:[message applyFormat]];
textView.textStorage.attributedString = logContent;
if (self.window.visible)
[textView scrollToEndOfDocument:self];
}
- (void) logDidClear
{
[logContent setAttributedString:[NSAttributedString new]];
}
@end
#pragma mark - NSString (LogFormatter)
@implementation NSString (LogFormatter)
- (NSMutableAttributedString *) applyFormat
{
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:self];
[str addAttribute:NSFontAttributeName
value:plainFont
value:[NSFont fontWithName:@"Menlo" size:11]
range:NSMakeRange(0, str.length)];
NSRegularExpression *rx = [NSRegularExpression regularExpressionWithPattern:@"^!.*$"
options:NSRegularExpressionAnchorsMatchLines
error:NULL];
NSArray *matches = [rx matchesInString:text
NSArray *matches = [rx matchesInString:self
options:0
range:NSMakeRange(0, text.length)];
range:NSMakeRange(0, self.length)];
for (NSTextCheckingResult *match in matches) {
[str addAttribute:NSForegroundColorAttributeName
value:NSColor.redColor
range:match.range];
[str addAttribute:NSFontAttributeName
value:boldFont
value:[NSFont fontWithName:@"Menlo-Bold" size:11]
range:match.range];
}
textView.textStorage.attributedString = str;
return str;
}
@end

View File

@ -17,14 +17,19 @@
- (void) gameSessionDidStart:(NSString *) name;
- (void) gameSessionDidEnd;
- (void) logDidUpdate:(NSString *) message;
- (void) logDidClear;
@end
@interface FBMainThread : NSThread
@property (readonly) NSString *running;
@property (nonatomic, weak) id<FBMainThreadDelegate> delegate;
- (NSString *) log;
- (void) load:(NSString *) path;
- (void) addObserver:(id<FBMainThreadDelegate>) observer;
- (void) removeObserver:(id<FBMainThreadDelegate>) observer;
@end

View File

@ -20,12 +20,14 @@ typedef enum LogEntryType {
{
NSString *pathToLoad;
NSMutableString *log;
NSMutableArray *observers;
}
- (instancetype) init
{
if (self = [super init]) {
log = [NSMutableString new];
observers = [NSMutableArray new];
}
return self;
}
@ -42,12 +44,35 @@ typedef enum LogEntryType {
return log;
}
- (void) addObserver:(id<FBMainThreadDelegate>) observer
{
@synchronized (observer) {
[observers addObject:observer];
}
}
- (void) removeObserver:(id<FBMainThreadDelegate>) observer
{
@synchronized (observer) {
[observers removeObject:observer];
}
}
#pragma mark - Private
- (void) updateProgress:(const char *) message
type:(LogEntryType) entryType
{
[log appendFormat:@"%c %s\n", entryType == LogEntryError ? '!' : ' ', message];
NSString *str = [NSString stringWithFormat:@"%c %s\n",
entryType == LogEntryError ? '!' : ' ', message];
[log appendString:str];
for (id<FBMainThreadDelegate> o in observers) {
dispatch_async(dispatch_get_main_queue(), ^{
if ([o respondsToSelector:@selector(logDidUpdate:)])
[o logDidUpdate:str];
});
}
}
#pragma mark - NSThread
@ -65,25 +90,30 @@ typedef enum LogEntryType {
NSString *setPath = [[pathToLoad stringByDeletingLastPathComponent] stringByAppendingString:@"/"];
NSString *setName = [[pathToLoad lastPathComponent] stringByDeletingPathExtension];
{
id<FBMainThreadDelegate> del = _delegate;
dispatch_async(dispatch_get_main_queue(), ^{
if ([del respondsToSelector:@selector(driverInitDidStart)])
[del driverInitDidStart];
});
log.string = @"";
@synchronized (observers) {
for (id<FBMainThreadDelegate> o in observers) {
dispatch_async(dispatch_get_main_queue(), ^{
if ([o respondsToSelector:@selector(logDidClear)])
[o logDidClear];
if ([o respondsToSelector:@selector(driverInitDidStart)])
[o driverInitDidStart];
});
}
}
if (!MainInit([setPath cStringUsingEncoding:NSUTF8StringEncoding],
[setName cStringUsingEncoding:NSUTF8StringEncoding])) {
pathToLoad = nil;
{
id<FBMainThreadDelegate> del = _delegate;
dispatch_async(dispatch_get_main_queue(), ^{
if ([del respondsToSelector:@selector(driverInitDidEnd:success:)])
[del driverInitDidEnd:setName
success:NO];
});
@synchronized (observers) {
for (id<FBMainThreadDelegate> o in observers) {
dispatch_async(dispatch_get_main_queue(), ^{
if ([o respondsToSelector:@selector(driverInitDidEnd:success:)])
[o driverInitDidEnd:setName
success:NO];
});
}
}
continue;
@ -92,26 +122,28 @@ typedef enum LogEntryType {
_running = pathToLoad;
pathToLoad = nil;
{
id<FBMainThreadDelegate> del = _delegate;
dispatch_async(dispatch_get_main_queue(), ^{
if ([del respondsToSelector:@selector(driverInitDidEnd:success:)])
[del driverInitDidEnd:setName
success:YES];
if ([del respondsToSelector:@selector(gameSessionDidStart:)])
[del gameSessionDidStart:setName];
});
@synchronized (observers) {
for (id<FBMainThreadDelegate> o in observers) {
dispatch_async(dispatch_get_main_queue(), ^{
if ([o respondsToSelector:@selector(driverInitDidEnd:success:)])
[o driverInitDidEnd:setName
success:YES];
if ([o respondsToSelector:@selector(gameSessionDidStart:)])
[o gameSessionDidStart:setName];
});
}
}
while (!self.isCancelled && pathToLoad == nil)
MainFrame();
{
id<FBMainThreadDelegate> del = _delegate;
dispatch_async(dispatch_get_main_queue(), ^{
if ([del respondsToSelector:@selector(gameSessionDidEnd)])
[del gameSessionDidEnd];
});
@synchronized (observers) {
for (id<FBMainThreadDelegate> o in observers) {
dispatch_async(dispatch_get_main_queue(), ^{
if ([o respondsToSelector:@selector(gameSessionDidEnd)])
[o gameSessionDidEnd];
});
}
}
_running = nil;