📄 osxwin.m
字号:
/*
* osxwin.m: code to manage a session window in Mac OS X PuTTY.
*/
#import <Cocoa/Cocoa.h>
#include "putty.h"
#include "terminal.h"
#include "osxclass.h"
/* Colours come in two flavours: configurable, and xterm-extended. */
#define NCFGCOLOURS (lenof(((Config *)0)->colours))
#define NEXTCOLOURS 240 /* 216 colour-cube plus 24 shades of grey */
#define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS)
/*
* The key component of the per-session data is the SessionWindow
* class. A pointer to this is used as the frontend handle, to be
* passed to all the platform-independent subsystems that require
* one.
*/
@interface TerminalView : NSImageView
{
NSFont *font;
NSImage *image;
Terminal *term;
Config cfg;
NSColor *colours[NALLCOLOURS];
float fw, fasc, fdesc, fh;
}
- (void)drawStartFinish:(BOOL)start;
- (void)setColour:(int)n r:(float)r g:(float)g b:(float)b;
- (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y
attr:(unsigned long)attr lattr:(int)lattr;
@end
@implementation TerminalView
- (BOOL)isFlipped
{
return YES;
}
- (id)initWithTerminal:(Terminal *)aTerm config:(Config)aCfg
{
float w, h;
self = [self initWithFrame:NSMakeRect(0,0,100,100)];
term = aTerm;
cfg = aCfg;
/*
* Initialise the fonts we're going to use.
*
* FIXME: for the moment I'm sticking with exactly one default font.
*/
font = [NSFont userFixedPitchFontOfSize:0];
/*
* Now determine the size of the primary font.
*
* FIXME: If we have multiple fonts, we may need to set fasc
* and fdesc to the _maximum_ asc and desc out of all the
* fonts, _before_ adding them together to get fh.
*/
fw = [font widthOfString:@"A"];
fasc = [font ascender];
fdesc = -[font descender];
fh = fasc + fdesc;
fh = (int)fh + (fh > (int)fh); /* round up, ickily */
/*
* Use this to figure out the size of the terminal view.
*/
w = fw * term->cols;
h = fh * term->rows;
/*
* And set our size and subimage.
*/
image = [[NSImage alloc] initWithSize:NSMakeSize(w,h)];
[image setFlipped:YES];
[self setImage:image];
[self setFrame:NSMakeRect(0,0,w,h)];
term_invalidate(term);
return self;
}
- (void)drawStartFinish:(BOOL)start
{
if (start)
[image lockFocus];
else
[image unlockFocus];
}
- (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y
attr:(unsigned long)attr lattr:(int)lattr
{
int nfg, nbg, rlen, widefactor;
float ox, oy, tw, th;
NSDictionary *attrdict;
/* FIXME: TATTR_COMBINING */
nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
if (attr & ATTR_REVERSE) {
int t = nfg;
nfg = nbg;
nbg = t;
}
if (cfg.bold_colour && (attr & ATTR_BOLD)) {
if (nfg < 16) nfg |= 8;
else if (nfg >= 256) nfg |= 1;
}
if (cfg.bold_colour && (attr & ATTR_BLINK)) {
if (nbg < 16) nbg |= 8;
else if (nbg >= 256) nbg |= 1;
}
if (attr & TATTR_ACTCURS) {
nfg = 260;
nbg = 261;
}
if (attr & ATTR_WIDE) {
widefactor = 2;
/* FIXME: what do we actually have to do about wide characters? */
} else {
widefactor = 1;
}
/* FIXME: ATTR_BOLD without cfg.bold_colour */
if ((lattr & LATTR_MODE) != LATTR_NORM) {
x *= 2;
if (x >= term->cols)
return;
if (x + len*2*widefactor > term->cols)
len = (term->cols-x)/2/widefactor;/* trim to LH half */
rlen = len * 2;
} else
rlen = len;
/* FIXME: how do we actually implement double-{width,height} lattrs? */
ox = x * fw;
oy = y * fh;
tw = rlen * widefactor * fw;
th = fh;
/*
* Set the clipping rectangle.
*/
[[NSGraphicsContext currentContext] saveGraphicsState];
[NSBezierPath clipRect:NSMakeRect(ox, oy, tw, th)];
attrdict = [NSDictionary dictionaryWithObjectsAndKeys:
colours[nfg], NSForegroundColorAttributeName,
colours[nbg], NSBackgroundColorAttributeName,
font, NSFontAttributeName, nil];
/*
* Create an NSString and draw it.
*
* Annoyingly, although our input is wchar_t which is four
* bytes wide on OS X and terminal.c supports 32-bit Unicode,
* we must convert into the two-byte type `unichar' to store in
* NSString, so we lose display capability for extra-BMP stuff
* at this point.
*/
{
NSString *string;
unichar *utext;
int i;
utext = snewn(len, unichar);
for (i = 0; i < len; i++)
utext[i] = (text[i] >= 0x10000 ? 0xFFFD : text[i]);
string = [NSString stringWithCharacters:utext length:len];
[string drawAtPoint:NSMakePoint(ox, oy) withAttributes:attrdict];
sfree(utext);
}
/*
* Restore the graphics state from before the clipRect: call.
*/
[[NSGraphicsContext currentContext] restoreGraphicsState];
/*
* And flag this area as needing display.
*/
[self setNeedsDisplayInRect:NSMakeRect(ox, oy, tw, th)];
}
- (void)setColour:(int)n r:(float)r g:(float)g b:(float)b
{
assert(n >= 0 && n < lenof(colours));
colours[n] = [[NSColor colorWithDeviceRed:r green:g blue:b alpha:1.0]
retain];
}
@end
@implementation SessionWindow
- (id)initWithConfig:(Config)aCfg
{
NSRect rect = { {0,0}, {0,0} };
alert_ctx = NULL;
cfg = aCfg; /* structure copy */
init_ucs(&ucsdata, cfg.line_codepage, cfg.utf8_override,
CS_UTF8, cfg.vtmode);
term = term_init(&cfg, &ucsdata, self);
logctx = log_init(self, &cfg);
term_provide_logctx(term, logctx);
term_size(term, cfg.height, cfg.width, cfg.savelines);
termview = [[[TerminalView alloc] initWithTerminal:term config:cfg]
autorelease];
/*
* Now work out the size of the window.
*/
rect = [termview frame];
rect.origin = NSMakePoint(0,0);
rect.size.width += 2 * cfg.window_border;
rect.size.height += 2 * cfg.window_border;
/*
* Set up a backend.
*/
{
int i;
back = &pty_backend;
for (i = 0; backends[i].backend != NULL; i++)
if (backends[i].protocol == cfg.protocol) {
back = backends[i].backend;
break;
}
}
{
const char *error;
char *realhost = NULL;
error = back->init(self, &backhandle, &cfg, cfg.host, cfg.port,
&realhost, cfg.tcp_nodelay, cfg.tcp_keepalives);
if (error) {
fatalbox("%s\n", error); /* FIXME: connection_fatal at worst */
}
if (realhost)
sfree(realhost); /* FIXME: do something with this */
}
back->provide_logctx(backhandle, logctx);
/*
* Create a line discipline. (This must be done after creating
* the terminal _and_ the backend, since it needs to be passed
* pointers to both.)
*/
ldisc = ldisc_create(&cfg, term, back, backhandle, self);
/*
* FIXME: Set up a scrollbar.
*/
self = [super initWithContentRect:rect
styleMask:(NSTitledWindowMask | NSMiniaturizableWindowMask |
NSClosableWindowMask)
backing:NSBackingStoreBuffered
defer:YES];
[self setTitle:@"PuTTY"];
[self setIgnoresMouseEvents:NO];
/*
* Put the terminal view in the window.
*/
rect = [termview frame];
rect.origin = NSMakePoint(cfg.window_border, cfg.window_border);
[termview setFrame:rect];
[[self contentView] addSubview:termview];
/*
* Set up the colour palette.
*/
palette_reset(self);
/*
* FIXME: Only the _first_ document window should be centred.
* The subsequent ones should appear down and to the right of
* it, probably using the cascade function provided by Cocoa.
* Also we're apparently required by the HIG to remember and
* reuse previous positions of windows, although I'm not sure
* how that works if the user opens more than one of the same
* session type.
*/
[self center]; /* :-) */
exited = FALSE;
return self;
}
- (void)dealloc
{
/*
* FIXME: Here we must deallocate all sorts of stuff: the
* terminal, the backend, the ldisc, the logctx, you name it.
* Do so.
*/
sfree(alert_ctx);
if (back)
back->free(backhandle);
if (ldisc)
ldisc_free(ldisc);
/* ldisc must be freed before term, since ldisc_free expects term
* still to be around. */
if (logctx)
log_free(logctx);
if (term)
term_free(term);
[super dealloc];
}
- (void)drawStartFinish:(BOOL)start
{
[termview drawStartFinish:start];
}
- (void)setColour:(int)n r:(float)r g:(float)g b:(float)b
{
[termview setColour:n r:r g:g b:b];
}
- (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y
attr:(unsigned long)attr lattr:(int)lattr
{
/* Pass this straight on to the TerminalView. */
[termview doText:text len:len x:x y:y attr:attr lattr:lattr];
}
- (Config *)cfg
{
return &cfg;
}
- (void)keyDown:(NSEvent *)ev
{
NSString *s = [ev characters];
int i;
int n = [s length], c = [s characterAtIndex:0], m = [ev modifierFlags];
int cm = [[ev charactersIgnoringModifiers] characterAtIndex:0];
wchar_t output[32];
char coutput[32];
int use_coutput = FALSE, special = FALSE, start, end;
//printf("n=%d c=U+%04x cm=U+%04x m=%08x\n", n, c, cm, m);
/*
* FIXME: Alt+numberpad codes.
*/
/*
* Shift and Ctrl with PageUp/PageDown for scrollback.
*/
if (n == 1 && c == NSPageUpFunctionKey && (m & NSShiftKeyMask)) {
term_scroll(term, 0, -term->rows/2);
return;
}
if (n == 1 && c == NSPageUpFunctionKey && (m & NSControlKeyMask)) {
term_scroll(term, 0, -1);
return;
}
if (n == 1 && c == NSPageDownFunctionKey && (m & NSShiftKeyMask)) {
term_scroll(term, 0, +term->rows/2);
return;
}
if (n == 1 && c == NSPageDownFunctionKey && (m & NSControlKeyMask)) {
term_scroll(term, 0, +1);
return;
}
/*
* FIXME: Shift-Ins for paste? Or is that not Maccy enough?
*/
/*
* FIXME: Alt (Option? Command?) prefix in general.
*
* (Note that Alt-Shift-thing will work just by looking at
* charactersIgnoringModifiers; but Alt-Ctrl-thing will need
* processing properly, and Alt-as-in-Option won't happen at
* all. Hmmm.)
*
* (Note also that we need to be able to override menu key
* equivalents before this is particularly useful.)
*/
start = 1;
end = start;
/*
* Ctrl-` is the same as Ctrl-\, unless we already have a
* better idea.
*/
if ((m & NSControlKeyMask) && n == 1 && cm == '`' && c == '`') {
output[1] = '\x1c';
end = 2;
}
/* We handle Return ourselves, because it needs to be flagged as
* special to ldisc. */
if (n == 1 && c == '\015') {
coutput[1] = '\015';
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -