Completed log viewer
This commit is contained in:
parent
bbc4a7d27b
commit
a56d7ab028
|
@ -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
|
||||
{
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface FBLogViewerController : NSWindowController<NSWindowDelegate>
|
||||
#import "FBMainThread.h"
|
||||
|
||||
@interface FBLogViewerController : NSWindowController<NSWindowDelegate, FBMainThreadDelegate>
|
||||
{
|
||||
IBOutlet NSTextView *textView;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue