2010-07-08 22:26:58 +00:00
|
|
|
//
|
|
|
|
// EmulatorView.m
|
|
|
|
// 2Term
|
|
|
|
//
|
|
|
|
// Created by Kelvin Sherlock on 7/3/2010.
|
|
|
|
// Copyright 2010 __MyCompanyName__. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#import "EmulatorView.h"
|
|
|
|
|
|
|
|
#import "CharacterGenerator.h"
|
|
|
|
|
2010-07-09 01:18:30 +00:00
|
|
|
#import "VT52.h"
|
|
|
|
|
|
|
|
#include "OutputChannel.h"
|
|
|
|
|
2010-07-08 22:26:58 +00:00
|
|
|
@implementation EmulatorView
|
|
|
|
|
|
|
|
@synthesize fd = _fd;
|
|
|
|
|
|
|
|
|
|
|
|
-(void)awakeFromNib
|
|
|
|
{
|
|
|
|
_charWidth = 7;
|
|
|
|
_charHeight = 16;
|
|
|
|
|
|
|
|
|
|
|
|
_foregroundColor = [[NSColor greenColor] retain];
|
|
|
|
_backgroundColor = [[NSColor blackColor] retain];
|
|
|
|
|
|
|
|
_charGen = [[CharacterGenerator generator] retain];
|
|
|
|
|
|
|
|
|
2010-07-09 01:18:30 +00:00
|
|
|
_emulator = [VT52 new];
|
2010-07-08 22:26:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
-(BOOL)isFlipped
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
-(void)viewDidMoveToWindow
|
|
|
|
{
|
|
|
|
[self becomeFirstResponder];
|
|
|
|
}
|
|
|
|
|
|
|
|
-(void)viewDidMoveToSuperview
|
|
|
|
{
|
|
|
|
[self becomeFirstResponder];
|
|
|
|
}
|
|
|
|
|
|
|
|
-(BOOL)acceptsFirstResponder
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
-(void)drawRect:(NSRect)dirtyRect
|
|
|
|
{
|
|
|
|
NSRect bounds = [self bounds];
|
|
|
|
|
2010-07-09 01:18:30 +00:00
|
|
|
NSRect screenRect = dirtyRect;
|
2010-07-08 22:26:58 +00:00
|
|
|
|
|
|
|
unsigned x, y;
|
|
|
|
|
2010-07-09 01:18:30 +00:00
|
|
|
screenRect.origin.x -= _paddingLeft;
|
|
|
|
screenRect.origin.y -= _paddingTop;
|
|
|
|
|
|
|
|
if (screenRect.origin.x < 0)
|
|
|
|
{
|
|
|
|
screenRect.size.width -= screenRect.origin.x;
|
|
|
|
screenRect.origin.x = 0;
|
|
|
|
}
|
|
|
|
if (screenRect.origin.y < 0)
|
|
|
|
{
|
|
|
|
screenRect.size.width -= screenRect.origin.y;
|
|
|
|
screenRect.origin.y = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned minX = floor(screenRect.origin.x / _charWidth);
|
|
|
|
unsigned maxX = ceil((screenRect.origin.x + screenRect.size.width) / _charWidth);
|
2010-07-08 22:26:58 +00:00
|
|
|
|
2010-07-09 01:18:30 +00:00
|
|
|
unsigned minY = floor(screenRect.origin.y / _charHeight);
|
|
|
|
unsigned maxY = ceil((screenRect.origin.y + screenRect.size.height) / _charHeight);
|
2010-07-08 22:26:58 +00:00
|
|
|
|
|
|
|
// x/y are 0-indexed here.
|
|
|
|
|
2010-07-09 01:18:30 +00:00
|
|
|
maxY = std::min(_screen.height() - 1, maxY);
|
|
|
|
maxX = std::min(_screen.width() - 1, maxX);
|
2010-07-08 22:26:58 +00:00
|
|
|
|
|
|
|
[_backgroundColor setFill];
|
|
|
|
NSRectFill(dirtyRect);
|
|
|
|
|
|
|
|
[_foregroundColor setFill];
|
|
|
|
|
2010-07-09 01:18:30 +00:00
|
|
|
_screen.lock();
|
2010-07-08 22:26:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
for (x = minX; x <= maxX; ++x)
|
|
|
|
{
|
|
|
|
for (y = minY; y <= maxY; ++y)
|
|
|
|
{
|
|
|
|
NSImage *img;
|
2010-07-09 01:18:30 +00:00
|
|
|
CharInfo ci = _screen.getc(x, y);
|
2010-07-08 22:26:58 +00:00
|
|
|
|
|
|
|
// todo -- check flags to determine fg/bg color, etc.
|
|
|
|
|
|
|
|
|
|
|
|
img = [_charGen imageForCharacter: ci.c];
|
|
|
|
|
|
|
|
/*
|
|
|
|
[img drawAtPoint: NSMakePoint(x * _charWidth, y * _charHeight)
|
|
|
|
fromRect: NSZeroRect
|
|
|
|
operation:NSCompositeCopy
|
|
|
|
fraction: 1.0];
|
|
|
|
*/
|
|
|
|
if (img)
|
|
|
|
{
|
2010-07-09 01:18:30 +00:00
|
|
|
[img drawInRect: NSMakeRect(_paddingLeft + x * _charWidth, _paddingTop + y *_charHeight, _charWidth, _charHeight)
|
2010-07-08 22:26:58 +00:00
|
|
|
fromRect: NSZeroRect operation: NSCompositeCopy
|
|
|
|
fraction: 1.0
|
|
|
|
respectFlipped: YES
|
|
|
|
hints: nil];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-09 01:18:30 +00:00
|
|
|
_screen.unlock();
|
|
|
|
|
2010-07-08 22:26:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
-(void)dealloc
|
|
|
|
{
|
2010-07-09 01:18:30 +00:00
|
|
|
close(_fd);
|
|
|
|
|
2010-07-08 22:26:58 +00:00
|
|
|
[_readerThread release];
|
2010-07-09 01:18:30 +00:00
|
|
|
|
|
|
|
[_emulator release];
|
2010-07-08 22:26:58 +00:00
|
|
|
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
2010-07-09 01:18:30 +00:00
|
|
|
-(void)keyDown:(NSEvent *)theEvent
|
|
|
|
{
|
|
|
|
OutputChannel channel(_fd);
|
|
|
|
|
|
|
|
[_emulator keyDown: theEvent screen: &_screen output: &channel];
|
|
|
|
}
|
|
|
|
|
2010-07-08 22:26:58 +00:00
|
|
|
-(void)startBackgroundReader
|
|
|
|
{
|
|
|
|
if (_readerThread) return;
|
|
|
|
|
|
|
|
_readerThread = [[NSThread alloc] initWithTarget: self selector: @selector(_readerThread) object: nil];
|
|
|
|
|
|
|
|
[_readerThread start];
|
|
|
|
}
|
|
|
|
-(void)_readerThread
|
|
|
|
{
|
|
|
|
// I would prefer to poll(2) but it's broken on os x for ptys.
|
|
|
|
|
|
|
|
int fd = _fd;
|
|
|
|
|
|
|
|
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
|
|
|
fd_set read_set;
|
|
|
|
fd_set error_set;
|
|
|
|
|
|
|
|
FD_ZERO(&read_set);
|
|
|
|
FD_SET(fd, &read_set);
|
|
|
|
FD_ZERO(&error_set);
|
|
|
|
FD_SET(fd, &error_set);
|
|
|
|
|
|
|
|
|
|
|
|
n = select(fd + 1, &read_set, NULL, &error_set, NULL);
|
|
|
|
|
|
|
|
if (n == 0) continue;
|
|
|
|
if (n < 0) break;
|
|
|
|
|
|
|
|
if (FD_ISSET(fd, &error_set)) break;
|
|
|
|
if (FD_ISSET(fd, &read_set)) [self dataAvailable];
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-07-09 01:18:30 +00:00
|
|
|
-(void)dataAvailable
|
|
|
|
{
|
|
|
|
typedef void (*ProcessCharFX)(id, SEL, uint8_t, Screen *, OutputChannel *);
|
|
|
|
|
|
|
|
ProcessCharFX fx;
|
|
|
|
SEL cmd;
|
|
|
|
OutputChannel channel(_fd);
|
|
|
|
|
|
|
|
cmd = @selector(processCharacter: screen: output:);
|
|
|
|
fx = (ProcessCharFX)[_emulator methodForSelector: cmd];
|
|
|
|
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
NSAutoreleasePool *pool;
|
|
|
|
iRect updateRect;
|
|
|
|
CGRect rect;
|
|
|
|
uint8_t buffer[512];
|
|
|
|
ssize_t size;
|
|
|
|
|
|
|
|
size = read(_fd, buffer, sizeof(buffer));
|
|
|
|
|
|
|
|
if (size == 0) break;
|
|
|
|
if (size < 0)
|
|
|
|
{
|
|
|
|
if (errno == EINTR || errno == EAGAIN) continue;
|
|
|
|
|
|
|
|
perror("[EmulatorView dataAvailable]");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pool = [NSAutoreleasePool new];
|
|
|
|
_screen.beginUpdate();
|
|
|
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < size; ++i)
|
|
|
|
{
|
|
|
|
fx(_emulator,cmd, buffer[i], &_screen, &channel);
|
|
|
|
}
|
|
|
|
|
|
|
|
updateRect = _screen.endUpdate();
|
|
|
|
|
|
|
|
rect.origin.x = updateRect.origin.x;
|
|
|
|
rect.origin.y = updateRect.origin.y;
|
|
|
|
rect.size.width = updateRect.size.width;
|
|
|
|
rect.size.height = updateRect.size.height;
|
|
|
|
|
|
|
|
rect.origin.x *= _charWidth;
|
|
|
|
rect.origin.y *= _charHeight;
|
|
|
|
rect.size.width *= _charWidth;
|
|
|
|
rect.size.height *= _charHeight;
|
|
|
|
|
|
|
|
rect.origin.x += _paddingLeft;
|
|
|
|
rect.origin.y += _paddingTop;
|
|
|
|
|
|
|
|
[self setNeedsDisplayInRect: rect];
|
|
|
|
|
|
|
|
[pool release];
|
|
|
|
}
|
|
|
|
}
|
2010-07-08 22:26:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
@end
|