зеркало из https://github.com/github/putty.git
Delete the old 'macosx' directory completely.
The current state of the OS X GTK port is looking more or less plausible - it's not finished, of course, but then neither was the old native Cocoa port. So I'm inclined to advertise it as *the* unfinished OS X port: it's the one I intend to keep working on, and it's the one I'd prefer people offered us help with if they're going to offer. Hence, leaving the old macosx directory around is just confusing; that directory is long-unmaintained, probably doesn't even compile, and its only effect will be to mislead people into thinking it's still relevant. I'm unilaterally deleting it; of course we can always recover it from source control history if it's ever necessary to do so.
This commit is contained in:
Родитель
bb6dd987ee
Коммит
e30e6b0f1d
|
@ -95,12 +95,6 @@
|
|||
/icons/*.icns
|
||||
/icons/*.xpm
|
||||
/icons/*.c
|
||||
/macosx/Makefile
|
||||
/macosx/*.app
|
||||
/macosx/puttygen
|
||||
/macosx/plink
|
||||
/macosx/psftp
|
||||
/macosx/pscp
|
||||
/testdata/bignum.txt
|
||||
/unix/Makefile.gtk
|
||||
/unix/Makefile.ux
|
||||
|
|
10
Recipe
10
Recipe
|
@ -20,7 +20,6 @@
|
|||
!makefile gtk unix/Makefile.gtk
|
||||
!makefile unix unix/Makefile.ux
|
||||
!makefile am Makefile.am
|
||||
!makefile osx macosx/Makefile
|
||||
!makefile devcppproj windows/DEVCPP
|
||||
!makefile vstudio10 windows/VS2010
|
||||
!makefile vstudio12 windows/VS2012
|
||||
|
@ -28,7 +27,6 @@
|
|||
!srcdir charset/
|
||||
!srcdir windows/
|
||||
!srcdir unix/
|
||||
!srcdir macosx/
|
||||
|
||||
# Help text added to the top of each Makefile, with /D converted
|
||||
# into -D as appropriate for the particular Makefile.
|
||||
|
@ -173,9 +171,6 @@ install:
|
|||
install-strip:
|
||||
$(MAKE) install INSTALL_PROGRAM="$(INSTALL_PROGRAM) -s"
|
||||
!end
|
||||
!begin osx vars
|
||||
CFLAGS += -DMACOSX
|
||||
!end
|
||||
|
||||
# List the man pages for the automake makefile.
|
||||
!begin am
|
||||
|
@ -231,7 +226,6 @@ GUITERM = TERMINAL window windlg winctrls sizetip winucs winprint
|
|||
UXTERM = TERMINAL uxcfg sercfg uxucs uxprint timing callback miscucs
|
||||
GTKTERM = UXTERM gtkwin gtkcfg gtkdlg gtkfont gtkcols gtkmisc xkeysym
|
||||
+ x11misc gtkcomm
|
||||
OSXTERM = UXTERM osxwin osxdlg osxctrls
|
||||
|
||||
# Non-SSH back ends (putty, puttytel, plink).
|
||||
NONSSH = telnet raw rlogin ldisc pinger
|
||||
|
@ -254,7 +248,6 @@ MISC = timing callback misc version settings tree234 proxy conf be_misc
|
|||
WINMISC = MISC winstore winnet winhandl cmdline windefs winmisc winproxy
|
||||
+ wintime winhsock errsock winsecur
|
||||
UXMISC = MISC uxstore uxsel uxnet uxpeer cmdline uxmisc uxproxy time
|
||||
OSXMISC = MISC uxstore uxsel osxsel uxnet uxpeer uxmisc uxproxy time
|
||||
|
||||
# import.c and dependencies, for PuTTYgen-like utilities that have to
|
||||
# load foreign key files.
|
||||
|
@ -331,9 +324,6 @@ pageant : [X] uxpgnt uxagentc pageant sshrsa sshpubk sshdes sshbn sshmd5
|
|||
+ conf uxsignal nocproxy nogss be_none x11fwd ux_x11 uxcons gtkask
|
||||
+ gtkmisc UXMISC
|
||||
|
||||
PuTTY : [MX] osxmain OSXTERM OSXMISC CHARSET U_BE_ALL NONSSH UXSSH
|
||||
+ ux_x11 uxpty uxsignal testback putty.icns info.plist
|
||||
|
||||
ptermapp : [XT] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore
|
||||
+ uxsignal CHARSET cmdline uxpterm version time xpmpterm xpmptcfg
|
||||
+ nogss gtkapp
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
This directory contains a Mac OS X port of PuTTY/pterm, running as a
|
||||
native Aqua GUI application.
|
||||
|
||||
THIS PORT IS CURRENTLY UNFINISHED AND EXPERIMENTAL. It is _not_
|
||||
considered to be of release quality, even if you've found it (and
|
||||
are reading this) in a PuTTY release source archive. You are welcome
|
||||
to try using it, but don't be surprised at unexpected behaviour. I'm
|
||||
not kidding.
|
||||
|
||||
In particular, I have not yet decided where OS X PuTTY should store
|
||||
its configuration data. Options include storing it in ~/.putty to be
|
||||
compatible with Unix PuTTY, storing it wherever is compatible with
|
||||
Mac Classic PuTTY, storing it in a natively OS X location, or
|
||||
sorting out the `config-locations' wishlist item and doing all
|
||||
three. Therefore, if you start using this port and create a whole
|
||||
load of saved sessions, you should not be surprised if a future
|
||||
version of the port decides to look somewhere completely different
|
||||
for the data and therefore loses them all. If that happens, don't
|
||||
say you weren't warned!
|
||||
|
||||
Other ways in which the port is currently unfinished include:
|
||||
|
||||
Bit rot
|
||||
-------
|
||||
|
||||
- the conversion of the old fixed-size 'Config' structure to the
|
||||
new dynamic 'Conf' was never applied to this directory
|
||||
|
||||
- probably other things are out of date too; it would need some
|
||||
work to make it compile again
|
||||
|
||||
Missing terminal window features
|
||||
--------------------------------
|
||||
|
||||
- terminal display is horribly slow
|
||||
|
||||
- fonts aren't configurable
|
||||
|
||||
- several features are unimplemented in the terminal display:
|
||||
underlining, non-solid-block cursors, double-width and
|
||||
double-height line attributes, bold as font rather than as
|
||||
colour, wide (CJK) characters, combining characters.
|
||||
|
||||
- there's no scrollbar
|
||||
|
||||
- terminal window resizing isn't implemented yet
|
||||
|
||||
- proper window placement (cascading down and right from the
|
||||
starting position, plus remembering previous window positions per
|
||||
the Apple HIG) is not implemented
|
||||
|
||||
Missing alert box features
|
||||
--------------------------
|
||||
|
||||
- warn-on-close isn't implemented
|
||||
|
||||
Missing input features
|
||||
----------------------
|
||||
|
||||
- use of Alt+numberpad to enter arbitrary numeric character codes
|
||||
is not yet supported
|
||||
|
||||
- there's no Meta key yet. (I'd like to at least have the
|
||||
possibility of using Command rather than Option as the Meta key,
|
||||
since the latter is necessary to send some characters, including
|
||||
the rather important # on Apple UK keyboards; but trapping
|
||||
Command-<key> and sending it to the window rather than the
|
||||
application menu requires me to make a positive effort of some
|
||||
sort and I haven't got round to it yet. For those Mac users who
|
||||
consider their Command key sacrosanct, don't worry, this option
|
||||
_will_ be configurable and _will_ be off by default.)
|
||||
|
||||
- there's no specials menu
|
||||
|
||||
- mouse activity isn't supported (neither cut-and-paste nor xterm
|
||||
mouse tracking)
|
||||
|
||||
Missing terminal emulation features
|
||||
-----------------------------------
|
||||
|
||||
- currently no support for server-side window management requests
|
||||
(i.e. escape sequences to minimise or maximise the window,
|
||||
request or change its position and size, change its title etc)
|
||||
|
||||
- window title is currently fixed
|
||||
|
||||
Other missing features
|
||||
----------------------
|
||||
|
||||
- no Event Log
|
||||
|
||||
- no mid-session Change Settings
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>PuTTY.icns</string>
|
||||
</dict>
|
||||
</plist>
|
34
macosx/osx.h
34
macosx/osx.h
|
@ -1,34 +0,0 @@
|
|||
#ifndef PUTTY_OSX_H
|
||||
#define PUTTY_OSX_H
|
||||
|
||||
/*
|
||||
* Cocoa defines `FontSpec' itself, so we must change its name.
|
||||
* (Arrgh.)
|
||||
*/
|
||||
#define FontSpec FontSpec_OSX_Proof
|
||||
|
||||
/*
|
||||
* Define the various compatibility symbols to make uxpty.c compile
|
||||
* correctly on OS X.
|
||||
*/
|
||||
#define BSD_PTYS
|
||||
#define OMIT_UTMP
|
||||
#define HAVE_NO_SETRESUID
|
||||
#define NOT_X_WINDOWS
|
||||
|
||||
/*
|
||||
* OS X is largely just Unix, so we can include most of this
|
||||
* unchanged.
|
||||
*/
|
||||
#include "unix.h"
|
||||
|
||||
/*
|
||||
* Functions exported by osxsel.m. (Both of these functions are
|
||||
* expected to be called in the _main_ thread: the select subthread
|
||||
* is an implementation detail of osxsel.m and ideally should not
|
||||
* be visible at all outside it.)
|
||||
*/
|
||||
void osxsel_init(void); /* call this to kick things off */
|
||||
void osxsel_process_results(void); /* call this on receipt of a netevent */
|
||||
|
||||
#endif
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
* Header file for the Objective-C parts of Mac OS X PuTTY. This
|
||||
* file contains the class definitions, which would cause compile
|
||||
* failures in the pure C modules if they appeared in osx.h.
|
||||
*/
|
||||
|
||||
#ifndef PUTTY_OSXCLASS_H
|
||||
#define PUTTY_OSXCLASS_H
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
/*
|
||||
* The application controller class, defined in osxmain.m.
|
||||
*/
|
||||
@interface AppController : NSObject
|
||||
{
|
||||
NSTimer *timer;
|
||||
}
|
||||
- (void)newSessionConfig:(id)sender;
|
||||
- (void)newTerminal:(id)sender;
|
||||
- (void)newSessionWithConfig:(id)cfg;
|
||||
- (void)setTimer:(long)next;
|
||||
@end
|
||||
extern AppController *controller;
|
||||
|
||||
/*
|
||||
* The SessionWindow class, defined in osxwin.m.
|
||||
*/
|
||||
|
||||
struct alert_queue {
|
||||
struct alert_queue *next;
|
||||
NSAlert *alert;
|
||||
void (*callback)(void *, int);
|
||||
void *ctx;
|
||||
};
|
||||
|
||||
@class SessionWindow;
|
||||
@class TerminalView;
|
||||
|
||||
@interface SessionWindow : NSWindow
|
||||
{
|
||||
Terminal *term;
|
||||
TerminalView *termview;
|
||||
struct unicode_data ucsdata;
|
||||
void *logctx;
|
||||
Config cfg;
|
||||
void *ldisc;
|
||||
Backend *back;
|
||||
void *backhandle;
|
||||
int exited;
|
||||
/*
|
||||
* The following two members relate to the currently active
|
||||
* alert sheet, if any. They are NULL if there isn't one.
|
||||
*/
|
||||
void (*alert_callback)(void *, int);
|
||||
void *alert_ctx;
|
||||
/* This queues future alerts that need to be shown. */
|
||||
struct alert_queue *alert_qhead, *alert_qtail;
|
||||
}
|
||||
- (id)initWithConfig:(Config)cfg;
|
||||
- (void)drawStartFinish:(BOOL)start;
|
||||
- (void)setColour:(int)n r:(float)r g:(float)g b:(float)b;
|
||||
- (Config *)cfg;
|
||||
- (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y
|
||||
attr:(unsigned long)attr lattr:(int)lattr;
|
||||
- (int)fromBackend:(const char *)data len:(int)len isStderr:(int)is_stderr;
|
||||
- (int)fromBackendUntrusted:(const char *)data len:(int)len;
|
||||
- (void)startAlert:(NSAlert *)alert
|
||||
withCallback:(void (*)(void *, int))callback andCtx:(void *)ctx;
|
||||
- (void)endSession:(int)clean;
|
||||
- (void)notifyRemoteExit;
|
||||
- (Terminal *)term;
|
||||
@end
|
||||
|
||||
/*
|
||||
* The ConfigWindow class, defined in osxdlg.m.
|
||||
*/
|
||||
|
||||
@class ConfigWindow;
|
||||
|
||||
@interface ConfigWindow : NSWindow
|
||||
{
|
||||
NSOutlineView *treeview;
|
||||
struct controlbox *ctrlbox;
|
||||
void *dv;
|
||||
Config cfg;
|
||||
}
|
||||
- (id)initWithConfig:(Config)cfg;
|
||||
@end
|
||||
|
||||
/*
|
||||
* Functions exported by osxctrls.m. (They have to go in this
|
||||
* header file and not osx.h, because some of them have Cocoa class
|
||||
* types in their prototypes.)
|
||||
*/
|
||||
#define HSPACING 12 /* needed in osxdlg.m and osxctrls.m */
|
||||
#define VSPACING 8
|
||||
|
||||
void *fe_dlg_init(void *data, NSWindow *window, NSObject *target, SEL action);
|
||||
void fe_dlg_free(void *dv);
|
||||
void create_ctrls(void *dv, NSView *parent, struct controlset *s,
|
||||
int *minw, int *minh);
|
||||
int place_ctrls(void *dv, struct controlset *s, int leftx, int topy,
|
||||
int width); /* returns height used */
|
||||
void select_panel(void *dv, struct controlbox *b, const char *name);
|
||||
|
||||
#endif /* PUTTY_OSXCLASS_H */
|
1781
macosx/osxctrls.m
1781
macosx/osxctrls.m
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
509
macosx/osxdlg.m
509
macosx/osxdlg.m
|
@ -1,509 +0,0 @@
|
|||
/*
|
||||
* osxdlg.m: various PuTTY dialog boxes for OS X.
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include "putty.h"
|
||||
#include "storage.h"
|
||||
#include "dialog.h"
|
||||
#include "osxclass.h"
|
||||
|
||||
/*
|
||||
* The `ConfigWindow' class is used to start up a new PuTTY
|
||||
* session.
|
||||
*/
|
||||
|
||||
@class ConfigTree;
|
||||
@interface ConfigTree : NSObject
|
||||
{
|
||||
NSString **paths;
|
||||
int *levels;
|
||||
int nitems, itemsize;
|
||||
}
|
||||
- (void)addPath:(char *)path;
|
||||
@end
|
||||
|
||||
@implementation ConfigTree
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
paths = NULL;
|
||||
levels = NULL;
|
||||
nitems = itemsize = 0;
|
||||
return self;
|
||||
}
|
||||
- (void)addPath:(char *)path
|
||||
{
|
||||
if (nitems >= itemsize) {
|
||||
itemsize += 32;
|
||||
paths = sresize(paths, itemsize, NSString *);
|
||||
levels = sresize(levels, itemsize, int);
|
||||
}
|
||||
paths[nitems] = [[NSString stringWithCString:path] retain];
|
||||
levels[nitems] = ctrl_path_elements(path) - 1;
|
||||
nitems++;
|
||||
}
|
||||
- (void)dealloc
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nitems; i++)
|
||||
[paths[i] release];
|
||||
|
||||
sfree(paths);
|
||||
sfree(levels);
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
- (id)iterateChildren:(int)index ofItem:(id)item count:(int *)count
|
||||
{
|
||||
int i, plevel;
|
||||
|
||||
if (item) {
|
||||
for (i = 0; i < nitems; i++)
|
||||
if (paths[i] == item)
|
||||
break;
|
||||
assert(i < nitems);
|
||||
plevel = levels[i];
|
||||
i++;
|
||||
} else {
|
||||
i = 0;
|
||||
plevel = -1;
|
||||
}
|
||||
|
||||
if (count)
|
||||
*count = 0;
|
||||
|
||||
while (index > 0) {
|
||||
if (i >= nitems || levels[i] != plevel+1)
|
||||
return nil;
|
||||
if (count)
|
||||
(*count)++;
|
||||
do {
|
||||
i++;
|
||||
} while (i < nitems && levels[i] > plevel+1);
|
||||
index--;
|
||||
}
|
||||
|
||||
return paths[i];
|
||||
}
|
||||
- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
|
||||
{
|
||||
return [self iterateChildren:index ofItem:item count:NULL];
|
||||
}
|
||||
- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
|
||||
{
|
||||
int count = 0;
|
||||
/* pass nitems+1 to ensure we run off the end */
|
||||
[self iterateChildren:nitems+1 ofItem:item count:&count];
|
||||
return count;
|
||||
}
|
||||
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
|
||||
{
|
||||
return [self outlineView:outlineView numberOfChildrenOfItem:item] > 0;
|
||||
}
|
||||
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
|
||||
{
|
||||
/*
|
||||
* Trim off all path elements except the last one.
|
||||
*/
|
||||
NSArray *components = [item componentsSeparatedByString:@"/"];
|
||||
return [components objectAtIndex:[components count]-1];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation ConfigWindow
|
||||
- (id)initWithConfig:(Config)aCfg
|
||||
{
|
||||
NSScrollView *scrollview;
|
||||
NSTableColumn *col;
|
||||
ConfigTree *treedata;
|
||||
int by = 0, mby = 0;
|
||||
int wmin = 0;
|
||||
int hmin = 0;
|
||||
int panelht = 0;
|
||||
|
||||
ctrlbox = ctrl_new_box();
|
||||
setup_config_box(ctrlbox, FALSE /*midsession*/, aCfg.protocol,
|
||||
0 /* protcfginfo */);
|
||||
unix_setup_config_box(ctrlbox, FALSE /*midsession*/, aCfg.protocol);
|
||||
|
||||
cfg = aCfg; /* structure copy */
|
||||
|
||||
self = [super initWithContentRect:NSMakeRect(0,0,300,300)
|
||||
styleMask:(NSTitledWindowMask | NSMiniaturizableWindowMask |
|
||||
NSClosableWindowMask)
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:YES];
|
||||
[self setTitle:@"PuTTY Configuration"];
|
||||
|
||||
[self setIgnoresMouseEvents:NO];
|
||||
|
||||
dv = fe_dlg_init(&cfg, self, self, @selector(configBoxFinished:));
|
||||
|
||||
scrollview = [[NSScrollView alloc] initWithFrame:NSMakeRect(20,20,10,10)];
|
||||
treeview = [[NSOutlineView alloc] initWithFrame:[scrollview frame]];
|
||||
[scrollview setBorderType:NSLineBorder];
|
||||
[scrollview setDocumentView:treeview];
|
||||
[[self contentView] addSubview:scrollview];
|
||||
[scrollview setHasVerticalScroller:YES];
|
||||
[scrollview setAutohidesScrollers:YES];
|
||||
/* FIXME: the below is untested. Test it then remove this notice. */
|
||||
[treeview setAllowsColumnReordering:NO];
|
||||
[treeview setAllowsColumnResizing:NO];
|
||||
[treeview setAllowsMultipleSelection:NO];
|
||||
[treeview setAllowsEmptySelection:NO];
|
||||
[treeview setAllowsColumnSelection:YES];
|
||||
|
||||
treedata = [[[ConfigTree alloc] init] retain];
|
||||
|
||||
col = [[NSTableColumn alloc] initWithIdentifier:nil];
|
||||
[treeview addTableColumn:col];
|
||||
[treeview setOutlineTableColumn:col];
|
||||
|
||||
[[treeview headerView] setFrame:NSMakeRect(0,0,0,0)];
|
||||
|
||||
/*
|
||||
* Create the controls.
|
||||
*/
|
||||
{
|
||||
int i;
|
||||
char *path = NULL;
|
||||
|
||||
for (i = 0; i < ctrlbox->nctrlsets; i++) {
|
||||
struct controlset *s = ctrlbox->ctrlsets[i];
|
||||
int mw, mh;
|
||||
|
||||
if (!*s->pathname) {
|
||||
|
||||
create_ctrls(dv, [self contentView], s, &mw, &mh);
|
||||
|
||||
by += 20 + mh;
|
||||
|
||||
if (wmin < mw + 40)
|
||||
wmin = mw + 40;
|
||||
} else {
|
||||
int j = path ? ctrl_path_compare(s->pathname, path) : 0;
|
||||
|
||||
if (j != INT_MAX) { /* add to treeview, start new panel */
|
||||
char *c;
|
||||
|
||||
/*
|
||||
* We expect never to find an implicit path
|
||||
* component. For example, we expect never to
|
||||
* see A/B/C followed by A/D/E, because that
|
||||
* would _implicitly_ create A/D. All our path
|
||||
* prefixes are expected to contain actual
|
||||
* controls and be selectable in the treeview;
|
||||
* so we would expect to see A/D _explicitly_
|
||||
* before encountering A/D/E.
|
||||
*/
|
||||
assert(j == ctrl_path_elements(s->pathname) - 1);
|
||||
|
||||
c = strrchr(s->pathname, '/');
|
||||
if (!c)
|
||||
c = s->pathname;
|
||||
else
|
||||
c++;
|
||||
|
||||
[treedata addPath:s->pathname];
|
||||
path = s->pathname;
|
||||
|
||||
panelht = 0;
|
||||
}
|
||||
|
||||
create_ctrls(dv, [self contentView], s, &mw, &mh);
|
||||
if (wmin < mw + 3*20+150)
|
||||
wmin = mw + 3*20+150;
|
||||
panelht += mh + 20;
|
||||
if (hmin < panelht - 20)
|
||||
hmin = panelht - 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int i;
|
||||
NSRect r;
|
||||
|
||||
[treeview setDataSource:treedata];
|
||||
for (i = [treeview numberOfRows]; i-- ;)
|
||||
[treeview expandItem:[treeview itemAtRow:i] expandChildren:YES];
|
||||
|
||||
[treeview sizeToFit];
|
||||
r = [treeview frame];
|
||||
if (hmin < r.size.height)
|
||||
hmin = r.size.height;
|
||||
}
|
||||
|
||||
[self setContentSize:NSMakeSize(wmin, hmin+60+by)];
|
||||
[scrollview setFrame:NSMakeRect(20, 40+by, 150, hmin)];
|
||||
[treeview setDelegate:self];
|
||||
mby = by;
|
||||
|
||||
/*
|
||||
* Now place the controls.
|
||||
*/
|
||||
{
|
||||
int i;
|
||||
char *path = NULL;
|
||||
panelht = 0;
|
||||
|
||||
for (i = 0; i < ctrlbox->nctrlsets; i++) {
|
||||
struct controlset *s = ctrlbox->ctrlsets[i];
|
||||
|
||||
if (!*s->pathname) {
|
||||
by -= VSPACING + place_ctrls(dv, s, 20, by, wmin-40);
|
||||
} else {
|
||||
if (!path || strcmp(s->pathname, path))
|
||||
panelht = 0;
|
||||
|
||||
panelht += VSPACING + place_ctrls(dv, s, 2*20+150,
|
||||
40+mby+hmin-panelht,
|
||||
wmin - (3*20+150));
|
||||
|
||||
path = s->pathname;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
select_panel(dv, ctrlbox, [[treeview itemAtRow:0] cString]);
|
||||
|
||||
[treeview reloadData];
|
||||
|
||||
dlg_refresh(NULL, dv);
|
||||
|
||||
[self center]; /* :-) */
|
||||
|
||||
return self;
|
||||
}
|
||||
- (void)configBoxFinished:(id)object
|
||||
{
|
||||
int ret = [object intValue]; /* it'll be an NSNumber */
|
||||
if (ret) {
|
||||
[controller performSelectorOnMainThread:
|
||||
@selector(newSessionWithConfig:)
|
||||
withObject:[NSData dataWithBytes:&cfg length:sizeof(cfg)]
|
||||
waitUntilDone:NO];
|
||||
}
|
||||
[self close];
|
||||
}
|
||||
- (void)outlineViewSelectionDidChange:(NSNotification *)notification
|
||||
{
|
||||
const char *path = [[treeview itemAtRow:[treeview selectedRow]] cString];
|
||||
select_panel(dv, ctrlbox, path);
|
||||
}
|
||||
- (BOOL)outlineView:(NSOutlineView *)outlineView
|
||||
shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
|
||||
{
|
||||
return NO; /* no editing! */
|
||||
}
|
||||
@end
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Various special-purpose dialog boxes.
|
||||
*/
|
||||
|
||||
struct appendstate {
|
||||
void (*callback)(void *ctx, int result);
|
||||
void *ctx;
|
||||
};
|
||||
|
||||
static void askappend_callback(void *ctx, int result)
|
||||
{
|
||||
struct appendstate *state = (struct appendstate *)ctx;
|
||||
|
||||
state->callback(state->ctx, (result == NSAlertFirstButtonReturn ? 2 :
|
||||
result == NSAlertSecondButtonReturn ? 1 : 0));
|
||||
sfree(state);
|
||||
}
|
||||
|
||||
int askappend(void *frontend, Filename filename,
|
||||
void (*callback)(void *ctx, int result), void *ctx)
|
||||
{
|
||||
static const char msgtemplate[] =
|
||||
"The session log file \"%s\" already exists. "
|
||||
"You can overwrite it with a new session log, "
|
||||
"append your session log to the end of it, "
|
||||
"or disable session logging for this session.";
|
||||
|
||||
char *text;
|
||||
SessionWindow *win = (SessionWindow *)frontend;
|
||||
struct appendstate *state;
|
||||
NSAlert *alert;
|
||||
|
||||
text = dupprintf(msgtemplate, filename.path);
|
||||
|
||||
state = snew(struct appendstate);
|
||||
state->callback = callback;
|
||||
state->ctx = ctx;
|
||||
|
||||
alert = [[NSAlert alloc] init];
|
||||
[alert setInformativeText:[NSString stringWithCString:text]];
|
||||
[alert addButtonWithTitle:@"Overwrite"];
|
||||
[alert addButtonWithTitle:@"Append"];
|
||||
[alert addButtonWithTitle:@"Disable"];
|
||||
[win startAlert:alert withCallback:askappend_callback andCtx:state];
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct algstate {
|
||||
void (*callback)(void *ctx, int result);
|
||||
void *ctx;
|
||||
};
|
||||
|
||||
static void askalg_callback(void *ctx, int result)
|
||||
{
|
||||
struct algstate *state = (struct algstate *)ctx;
|
||||
|
||||
state->callback(state->ctx, result == NSAlertFirstButtonReturn);
|
||||
sfree(state);
|
||||
}
|
||||
|
||||
int askalg(void *frontend, const char *algtype, const char *algname,
|
||||
void (*callback)(void *ctx, int result), void *ctx)
|
||||
{
|
||||
static const char msg[] =
|
||||
"The first %s supported by the server is "
|
||||
"%s, which is below the configured warning threshold.\n"
|
||||
"Continue with connection?";
|
||||
|
||||
char *text;
|
||||
SessionWindow *win = (SessionWindow *)frontend;
|
||||
struct algstate *state;
|
||||
NSAlert *alert;
|
||||
|
||||
text = dupprintf(msg, algtype, algname);
|
||||
|
||||
state = snew(struct algstate);
|
||||
state->callback = callback;
|
||||
state->ctx = ctx;
|
||||
|
||||
alert = [[NSAlert alloc] init];
|
||||
[alert setInformativeText:[NSString stringWithCString:text]];
|
||||
[alert addButtonWithTitle:@"Yes"];
|
||||
[alert addButtonWithTitle:@"No"];
|
||||
[win startAlert:alert withCallback:askalg_callback andCtx:state];
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct hostkeystate {
|
||||
char *host, *keytype, *keystr;
|
||||
int port;
|
||||
void (*callback)(void *ctx, int result);
|
||||
void *ctx;
|
||||
};
|
||||
|
||||
static void verify_ssh_host_key_callback(void *ctx, int result)
|
||||
{
|
||||
struct hostkeystate *state = (struct hostkeystate *)ctx;
|
||||
|
||||
if (result == NSAlertThirdButtonReturn) /* `Accept' */
|
||||
store_host_key(state->host, state->port,
|
||||
state->keytype, state->keystr);
|
||||
state->callback(state->ctx, result != NSAlertFirstButtonReturn);
|
||||
sfree(state->host);
|
||||
sfree(state->keytype);
|
||||
sfree(state->keystr);
|
||||
sfree(state);
|
||||
}
|
||||
|
||||
int verify_ssh_host_key(void *frontend, char *host, int port,
|
||||
const char *keytype, char *keystr, char *fingerprint,
|
||||
void (*callback)(void *ctx, int result), void *ctx)
|
||||
{
|
||||
static const char absenttxt[] =
|
||||
"The server's host key is not cached. You have no guarantee "
|
||||
"that the server is the computer you think it is.\n"
|
||||
"The server's %s key fingerprint is:\n"
|
||||
"%s\n"
|
||||
"If you trust this host, press \"Accept\" to add the key to "
|
||||
"PuTTY's cache and carry on connecting.\n"
|
||||
"If you want to carry on connecting just once, without "
|
||||
"adding the key to the cache, press \"Connect Once\".\n"
|
||||
"If you do not trust this host, press \"Cancel\" to abandon the "
|
||||
"connection.";
|
||||
static const char wrongtxt[] =
|
||||
"WARNING - POTENTIAL SECURITY BREACH!\n"
|
||||
"The server's host key does not match the one PuTTY has "
|
||||
"cached. This means that either the server administrator "
|
||||
"has changed the host key, or you have actually connected "
|
||||
"to another computer pretending to be the server.\n"
|
||||
"The new %s key fingerprint is:\n"
|
||||
"%s\n"
|
||||
"If you were expecting this change and trust the new key, "
|
||||
"press \"Accept\" to update PuTTY's cache and continue connecting.\n"
|
||||
"If you want to carry on connecting but without updating "
|
||||
"the cache, press \"Connect Once\".\n"
|
||||
"If you want to abandon the connection completely, press "
|
||||
"\"Cancel\" to cancel. Pressing \"Cancel\" is the ONLY guaranteed "
|
||||
"safe choice.";
|
||||
|
||||
int ret;
|
||||
char *text;
|
||||
SessionWindow *win = (SessionWindow *)frontend;
|
||||
struct hostkeystate *state;
|
||||
NSAlert *alert;
|
||||
|
||||
/*
|
||||
* Verify the key.
|
||||
*/
|
||||
ret = verify_host_key(host, port, keytype, keystr);
|
||||
|
||||
if (ret == 0)
|
||||
return 1;
|
||||
|
||||
text = dupprintf((ret == 2 ? wrongtxt : absenttxt), keytype, fingerprint);
|
||||
|
||||
state = snew(struct hostkeystate);
|
||||
state->callback = callback;
|
||||
state->ctx = ctx;
|
||||
state->host = dupstr(host);
|
||||
state->port = port;
|
||||
state->keytype = dupstr(keytype);
|
||||
state->keystr = dupstr(keystr);
|
||||
|
||||
alert = [[NSAlert alloc] init];
|
||||
[alert setInformativeText:[NSString stringWithCString:text]];
|
||||
[alert addButtonWithTitle:@"Cancel"];
|
||||
[alert addButtonWithTitle:@"Connect Once"];
|
||||
[alert addButtonWithTitle:@"Accept"];
|
||||
[win startAlert:alert withCallback:verify_ssh_host_key_callback
|
||||
andCtx:state];
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void old_keyfile_warning(void)
|
||||
{
|
||||
/*
|
||||
* This should never happen on OS X. We hope.
|
||||
*/
|
||||
}
|
||||
|
||||
static void connection_fatal_callback(void *ctx, int result)
|
||||
{
|
||||
SessionWindow *win = (SessionWindow *)ctx;
|
||||
|
||||
[win endSession:FALSE];
|
||||
}
|
||||
|
||||
void connection_fatal(void *frontend, const char *p, ...)
|
||||
{
|
||||
SessionWindow *win = (SessionWindow *)frontend;
|
||||
va_list ap;
|
||||
char *msg;
|
||||
NSAlert *alert;
|
||||
|
||||
va_start(ap, p);
|
||||
msg = dupvprintf(p, ap);
|
||||
va_end(ap);
|
||||
|
||||
alert = [[NSAlert alloc] init];
|
||||
[alert setInformativeText:[NSString stringWithCString:msg]];
|
||||
[alert addButtonWithTitle:@"Proceed"];
|
||||
[win startAlert:alert withCallback:connection_fatal_callback
|
||||
andCtx:win];
|
||||
}
|
426
macosx/osxmain.m
426
macosx/osxmain.m
|
@ -1,426 +0,0 @@
|
|||
/*
|
||||
* osxmain.m: main-program file of Mac OS X PuTTY.
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#define PUTTY_DO_GLOBALS /* actually _define_ globals */
|
||||
|
||||
#include "putty.h"
|
||||
#include "osxclass.h"
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Global variables.
|
||||
*/
|
||||
|
||||
AppController *controller;
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Miscellaneous elements of the interface to the cross-platform
|
||||
* and Unix PuTTY code.
|
||||
*/
|
||||
|
||||
char *platform_get_x_display(void) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FontSpec platform_default_fontspec(const char *name)
|
||||
{
|
||||
FontSpec ret;
|
||||
/* FIXME */
|
||||
return ret;
|
||||
}
|
||||
|
||||
Filename platform_default_filename(const char *name)
|
||||
{
|
||||
Filename ret;
|
||||
if (!strcmp(name, "LogFileName"))
|
||||
strcpy(ret.path, "putty.log");
|
||||
else
|
||||
*ret.path = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *platform_default_s(const char *name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int platform_default_i(const char *name, int def)
|
||||
{
|
||||
if (!strcmp(name, "CloseOnExit"))
|
||||
return 2; /* maps to FORCE_ON after painful rearrangement :-( */
|
||||
return def;
|
||||
}
|
||||
|
||||
char *x_get_default(const char *key)
|
||||
{
|
||||
return NULL; /* this is a stub */
|
||||
}
|
||||
|
||||
static void commonfatalbox(const char *p, va_list ap)
|
||||
{
|
||||
char errorbuf[2048];
|
||||
NSAlert *alert;
|
||||
|
||||
/*
|
||||
* We may have come here because we ran out of memory, in which
|
||||
* case it's entirely likely that that further memory
|
||||
* allocations will fail. So (a) we use vsnprintf to format the
|
||||
* error message rather than the usual dupvprintf; and (b) we
|
||||
* have a fallback way to get the message out via stderr if
|
||||
* even creating an NSAlert fails.
|
||||
*/
|
||||
vsnprintf(errorbuf, lenof(errorbuf), p, ap);
|
||||
|
||||
alert = [NSAlert alloc];
|
||||
if (!alert) {
|
||||
fprintf(stderr, "fatal error (and NSAlert failed): %s\n", errorbuf);
|
||||
} else {
|
||||
alert = [[alert init] autorelease];
|
||||
[alert addButtonWithTitle:@"Terminate"];
|
||||
[alert setInformativeText:[NSString stringWithCString:errorbuf]];
|
||||
[alert runModal];
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void nonfatal(void *frontend, const char *p, ...)
|
||||
{
|
||||
char *errorbuf;
|
||||
NSAlert *alert;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, p);
|
||||
errorbuf = dupvprintf(p, ap);
|
||||
va_end(ap);
|
||||
|
||||
alert = [[[NSAlert alloc] init] autorelease];
|
||||
[alert addButtonWithTitle:@"Error"];
|
||||
[alert setInformativeText:[NSString stringWithCString:errorbuf]];
|
||||
[alert runModal];
|
||||
|
||||
sfree(errorbuf);
|
||||
}
|
||||
|
||||
void fatalbox(const char *p, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, p);
|
||||
commonfatalbox(p, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void modalfatalbox(const char *p, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, p);
|
||||
commonfatalbox(p, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void cmdline_error(const char *p, ...)
|
||||
{
|
||||
va_list ap;
|
||||
fprintf(stderr, "%s: ", appname);
|
||||
va_start(ap, p);
|
||||
vfprintf(stderr, p, ap);
|
||||
va_end(ap);
|
||||
fputc('\n', stderr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up and exit.
|
||||
*/
|
||||
void cleanup_exit(int code)
|
||||
{
|
||||
/*
|
||||
* Clean up.
|
||||
*/
|
||||
sk_cleanup();
|
||||
random_save_seed();
|
||||
exit(code);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Tiny extension to NSMenuItem which carries a payload of a `void
|
||||
* *', allowing several menu items to invoke the same message but
|
||||
* pass different data through it.
|
||||
*/
|
||||
@interface DataMenuItem : NSMenuItem
|
||||
{
|
||||
void *payload;
|
||||
}
|
||||
- (void)setPayload:(void *)d;
|
||||
- (void *)getPayload;
|
||||
@end
|
||||
@implementation DataMenuItem
|
||||
- (void)setPayload:(void *)d
|
||||
{
|
||||
payload = d;
|
||||
}
|
||||
- (void *)getPayload
|
||||
{
|
||||
return payload;
|
||||
}
|
||||
@end
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Utility routines for constructing OS X menus.
|
||||
*/
|
||||
|
||||
NSMenu *newmenu(const char *title)
|
||||
{
|
||||
return [[[NSMenu allocWithZone:[NSMenu menuZone]]
|
||||
initWithTitle:[NSString stringWithCString:title]]
|
||||
autorelease];
|
||||
}
|
||||
|
||||
NSMenu *newsubmenu(NSMenu *parent, const char *title)
|
||||
{
|
||||
NSMenuItem *item;
|
||||
NSMenu *child;
|
||||
|
||||
item = [[[NSMenuItem allocWithZone:[NSMenu menuZone]]
|
||||
initWithTitle:[NSString stringWithCString:title]
|
||||
action:NULL
|
||||
keyEquivalent:@""]
|
||||
autorelease];
|
||||
child = newmenu(title);
|
||||
[item setEnabled:YES];
|
||||
[item setSubmenu:child];
|
||||
[parent addItem:item];
|
||||
return child;
|
||||
}
|
||||
|
||||
id initnewitem(NSMenuItem *item, NSMenu *parent, const char *title,
|
||||
const char *key, id target, SEL action)
|
||||
{
|
||||
unsigned mask = NSCommandKeyMask;
|
||||
|
||||
if (key[strcspn(key, "-")]) {
|
||||
while (*key && *key != '-') {
|
||||
int c = tolower((unsigned char)*key);
|
||||
if (c == 's') {
|
||||
mask |= NSShiftKeyMask;
|
||||
} else if (c == 'o' || c == 'a') {
|
||||
mask |= NSAlternateKeyMask;
|
||||
}
|
||||
key++;
|
||||
}
|
||||
if (*key)
|
||||
key++;
|
||||
}
|
||||
|
||||
item = [[item initWithTitle:[NSString stringWithCString:title]
|
||||
action:NULL
|
||||
keyEquivalent:[NSString stringWithCString:key]]
|
||||
autorelease];
|
||||
|
||||
if (*key)
|
||||
[item setKeyEquivalentModifierMask: mask];
|
||||
|
||||
[item setEnabled:YES];
|
||||
[item setTarget:target];
|
||||
[item setAction:action];
|
||||
|
||||
[parent addItem:item];
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
NSMenuItem *newitem(NSMenu *parent, char *title, char *key,
|
||||
id target, SEL action)
|
||||
{
|
||||
return initnewitem([NSMenuItem allocWithZone:[NSMenu menuZone]],
|
||||
parent, title, key, target, action);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* AppController: the object which receives the messages from all
|
||||
* menu selections that aren't standard OS X functions.
|
||||
*/
|
||||
@implementation AppController
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
timer = NULL;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)newTerminal:(id)sender
|
||||
{
|
||||
id win;
|
||||
Config cfg;
|
||||
|
||||
do_defaults(NULL, &cfg);
|
||||
|
||||
cfg.protocol = -1; /* PROT_TERMINAL */
|
||||
|
||||
win = [[SessionWindow alloc] initWithConfig:cfg];
|
||||
[win makeKeyAndOrderFront:self];
|
||||
}
|
||||
|
||||
- (void)newSessionConfig:(id)sender
|
||||
{
|
||||
id win;
|
||||
Config cfg;
|
||||
|
||||
do_defaults(NULL, &cfg);
|
||||
|
||||
win = [[ConfigWindow alloc] initWithConfig:cfg];
|
||||
[win makeKeyAndOrderFront:self];
|
||||
}
|
||||
|
||||
- (void)newSessionWithConfig:(id)vdata
|
||||
{
|
||||
id win;
|
||||
Config cfg;
|
||||
NSData *data = (NSData *)vdata;
|
||||
|
||||
assert([data length] == sizeof(cfg));
|
||||
[data getBytes:&cfg];
|
||||
|
||||
win = [[SessionWindow alloc] initWithConfig:cfg];
|
||||
[win makeKeyAndOrderFront:self];
|
||||
}
|
||||
|
||||
- (NSMenu *)applicationDockMenu:(NSApplication *)sender
|
||||
{
|
||||
NSMenu *menu = newmenu("Dock Menu");
|
||||
/*
|
||||
* FIXME: Add some useful things to this, probably including
|
||||
* the saved session list.
|
||||
*/
|
||||
return menu;
|
||||
}
|
||||
|
||||
- (void)timerFired:(id)sender
|
||||
{
|
||||
long now, next;
|
||||
|
||||
assert(sender == timer);
|
||||
|
||||
/* `sender' is the timer itself, so its userInfo is an NSNumber. */
|
||||
now = [(NSNumber *)[sender userInfo] longValue];
|
||||
|
||||
[sender invalidate];
|
||||
|
||||
timer = NULL;
|
||||
|
||||
if (run_timers(now, &next))
|
||||
[self setTimer:next];
|
||||
}
|
||||
|
||||
- (void)setTimer:(long)next
|
||||
{
|
||||
long interval = next - GETTICKCOUNT();
|
||||
float finterval;
|
||||
|
||||
if (interval <= 0)
|
||||
interval = 1; /* just in case */
|
||||
|
||||
finterval = interval / (float)TICKSPERSEC;
|
||||
|
||||
if (timer) {
|
||||
[timer invalidate];
|
||||
}
|
||||
|
||||
timer = [NSTimer scheduledTimerWithTimeInterval:finterval
|
||||
target:self selector:@selector(timerFired:)
|
||||
userInfo:[NSNumber numberWithLong:next] repeats:NO];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
void timer_change_notify(long next)
|
||||
{
|
||||
[controller setTimer:next];
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Annoyingly, it looks as if I have to actually subclass
|
||||
* NSApplication if I want to catch NSApplicationDefined events. So
|
||||
* here goes.
|
||||
*/
|
||||
@interface MyApplication : NSApplication
|
||||
{
|
||||
}
|
||||
@end
|
||||
@implementation MyApplication
|
||||
- (void)sendEvent:(NSEvent *)ev
|
||||
{
|
||||
if ([ev type] == NSApplicationDefined)
|
||||
osxsel_process_results();
|
||||
|
||||
[super sendEvent:ev];
|
||||
}
|
||||
@end
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Main program. Constructs the menus and runs the application.
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
NSAutoreleasePool *pool;
|
||||
NSMenu *menu;
|
||||
NSMenuItem *item;
|
||||
NSImage *icon;
|
||||
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
icon = [NSImage imageNamed:@"NSApplicationIcon"];
|
||||
[MyApplication sharedApplication];
|
||||
[NSApp setApplicationIconImage:icon];
|
||||
|
||||
controller = [[[AppController alloc] init] autorelease];
|
||||
[NSApp setDelegate:controller];
|
||||
|
||||
[NSApp setMainMenu: newmenu("Main Menu")];
|
||||
|
||||
menu = newsubmenu([NSApp mainMenu], "Apple Menu");
|
||||
[NSApp setServicesMenu:newsubmenu(menu, "Services")];
|
||||
[menu addItem:[NSMenuItem separatorItem]];
|
||||
item = newitem(menu, "Hide PuTTY", "h", NSApp, @selector(hide:));
|
||||
item = newitem(menu, "Hide Others", "o-h", NSApp, @selector(hideOtherApplications:));
|
||||
item = newitem(menu, "Show All", "", NSApp, @selector(unhideAllApplications:));
|
||||
[menu addItem:[NSMenuItem separatorItem]];
|
||||
item = newitem(menu, "Quit", "q", NSApp, @selector(terminate:));
|
||||
[NSApp setAppleMenu: menu];
|
||||
|
||||
menu = newsubmenu([NSApp mainMenu], "File");
|
||||
item = newitem(menu, "New", "n", NULL, @selector(newSessionConfig:));
|
||||
item = newitem(menu, "New Terminal", "t", NULL, @selector(newTerminal:));
|
||||
item = newitem(menu, "Close", "w", NULL, @selector(performClose:));
|
||||
|
||||
menu = newsubmenu([NSApp mainMenu], "Window");
|
||||
[NSApp setWindowsMenu: menu];
|
||||
item = newitem(menu, "Minimise Window", "m", NULL, @selector(performMiniaturize:));
|
||||
|
||||
// menu = newsubmenu([NSApp mainMenu], "Help");
|
||||
// item = newitem(menu, "PuTTY Help", "?", NSApp, @selector(showHelp:));
|
||||
|
||||
/*
|
||||
* Start up the sub-thread doing select().
|
||||
*/
|
||||
osxsel_init();
|
||||
|
||||
/*
|
||||
* Start up networking.
|
||||
*/
|
||||
sk_init();
|
||||
|
||||
/*
|
||||
* FIXME: To make initial debugging more convenient I'm going
|
||||
* to start by opening a session window unconditionally. This
|
||||
* will probably change later on.
|
||||
*/
|
||||
[controller newSessionConfig:nil];
|
||||
|
||||
[NSApp run];
|
||||
[pool release];
|
||||
|
||||
return 0;
|
||||
}
|
308
macosx/osxsel.m
308
macosx/osxsel.m
|
@ -1,308 +0,0 @@
|
|||
/*
|
||||
* osxsel.m: OS X implementation of the front end interface to uxsel.
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include <unistd.h>
|
||||
#include "putty.h"
|
||||
#include "osxclass.h"
|
||||
|
||||
/*
|
||||
* The unofficial Cocoa FAQ at
|
||||
*
|
||||
* http://www.alastairs-place.net/cocoa/faq.txt
|
||||
*
|
||||
* says that Cocoa has the native ability to be given an fd and
|
||||
* tell you when it becomes readable, but cannot tell you when it
|
||||
* becomes _writable_. This is unacceptable to PuTTY, which depends
|
||||
* for correct functioning on being told both. Therefore, I can't
|
||||
* use the Cocoa native mechanism.
|
||||
*
|
||||
* Instead, I'm going to resort to threads. I start a second thread
|
||||
* whose job is to do selects. At the termination of every select,
|
||||
* it posts a Cocoa event into the main thread's event queue, so
|
||||
* that the main thread gets select results interleaved with other
|
||||
* GUI operations. Communication from the main thread _to_ the
|
||||
* select thread is performed by writing to a pipe whose other end
|
||||
* is one of the file descriptors being selected on. (This is the
|
||||
* only sensible way, because we have to be able to interrupt a
|
||||
* select in order to provide a new fd list.)
|
||||
*/
|
||||
|
||||
/*
|
||||
* In more detail, the select thread must:
|
||||
*
|
||||
* - start off by listening to _just_ the pipe, waiting to be told
|
||||
* to begin a select.
|
||||
*
|
||||
* - when it receives the `start' command, it should read the
|
||||
* shared uxsel data (which is protected by a mutex), set up its
|
||||
* select, and begin it.
|
||||
*
|
||||
* - when the select terminates, it should write the results
|
||||
* (perhaps minus the inter-thread pipe if it's there) into
|
||||
* shared memory and dispatch a GUI event to let the main thread
|
||||
* know.
|
||||
*
|
||||
* - the main thread will then think about it, do some processing,
|
||||
* and _then_ send a command saying `now restart select'. Before
|
||||
* sending that command it might easily have tinkered with the
|
||||
* uxsel structures, which is why it waited before sending it.
|
||||
*
|
||||
* - EOF on the inter-thread pipe, of course, means the process
|
||||
* has finished completely, so the select thread terminates.
|
||||
*
|
||||
* - The main thread may wish to adjust the uxsel settings in the
|
||||
* middle of a select. In this situation it first writes the new
|
||||
* data to the shared memory area, then notifies the select
|
||||
* thread by writing to the inter-thread pipe.
|
||||
*
|
||||
* So the upshot is that the sequence of operations performed in
|
||||
* the select thread must be:
|
||||
*
|
||||
* - read a byte from the pipe (which may block)
|
||||
*
|
||||
* - read the shared uxsel data and perform a select
|
||||
*
|
||||
* - notify the main thread of interesting select results (if any)
|
||||
*
|
||||
* - loop round again from the top.
|
||||
*
|
||||
* This is sufficient. Notifying the select thread asynchronously
|
||||
* by writing to the pipe will cause its select to terminate and
|
||||
* another to begin immediately without blocking. If the select
|
||||
* thread's select terminates due to network data, its subsequent
|
||||
* pipe read will block until the main thread is ready to let it
|
||||
* loose again.
|
||||
*/
|
||||
|
||||
static int osxsel_pipe[2];
|
||||
|
||||
static NSLock *osxsel_inlock;
|
||||
static fd_set osxsel_rfds_in;
|
||||
static fd_set osxsel_wfds_in;
|
||||
static fd_set osxsel_xfds_in;
|
||||
static int osxsel_inmax;
|
||||
|
||||
static NSLock *osxsel_outlock;
|
||||
static fd_set osxsel_rfds_out;
|
||||
static fd_set osxsel_wfds_out;
|
||||
static fd_set osxsel_xfds_out;
|
||||
static int osxsel_outmax;
|
||||
|
||||
static int inhibit_start_select;
|
||||
|
||||
/*
|
||||
* NSThread requires an object method as its thread procedure, so
|
||||
* here I define a trivial holding class.
|
||||
*/
|
||||
@class OSXSel;
|
||||
@interface OSXSel : NSObject
|
||||
{
|
||||
}
|
||||
- (void)runThread:(id)arg;
|
||||
@end
|
||||
@implementation OSXSel
|
||||
- (void)runThread:(id)arg
|
||||
{
|
||||
char c;
|
||||
fd_set r, w, x;
|
||||
int n, ret;
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
while (1) {
|
||||
/*
|
||||
* Read one byte from the pipe.
|
||||
*/
|
||||
ret = read(osxsel_pipe[0], &c, 1);
|
||||
|
||||
if (ret <= 0)
|
||||
return; /* terminate the thread */
|
||||
|
||||
/*
|
||||
* Now set up the select data.
|
||||
*/
|
||||
[osxsel_inlock lock];
|
||||
memcpy(&r, &osxsel_rfds_in, sizeof(fd_set));
|
||||
memcpy(&w, &osxsel_wfds_in, sizeof(fd_set));
|
||||
memcpy(&x, &osxsel_xfds_in, sizeof(fd_set));
|
||||
n = osxsel_inmax;
|
||||
[osxsel_inlock unlock];
|
||||
FD_SET(osxsel_pipe[0], &r);
|
||||
if (n < osxsel_pipe[0]+1)
|
||||
n = osxsel_pipe[0]+1;
|
||||
|
||||
/*
|
||||
* Perform the select.
|
||||
*/
|
||||
ret = select(n, &r, &w, &x, NULL);
|
||||
|
||||
/*
|
||||
* Detect the one special case in which the only
|
||||
* interesting fd was the inter-thread pipe. In that
|
||||
* situation only we are interested - the main thread will
|
||||
* not be!
|
||||
*/
|
||||
if (ret == 1 && FD_ISSET(osxsel_pipe[0], &r))
|
||||
continue; /* just loop round again */
|
||||
|
||||
/*
|
||||
* Write the select results to shared data.
|
||||
*
|
||||
* I _think_ we don't need this data to be lock-protected:
|
||||
* it won't be read by the main thread until after we send
|
||||
* a message indicating that we've finished writing it, and
|
||||
* we won't start another select (hence potentially writing
|
||||
* it again) until the main thread notifies us in return.
|
||||
*
|
||||
* However, I'm scared of multithreading and not totally
|
||||
* convinced of my reasoning, so I'm going to lock it
|
||||
* anyway.
|
||||
*/
|
||||
[osxsel_outlock lock];
|
||||
memcpy(&osxsel_rfds_out, &r, sizeof(fd_set));
|
||||
memcpy(&osxsel_wfds_out, &w, sizeof(fd_set));
|
||||
memcpy(&osxsel_xfds_out, &x, sizeof(fd_set));
|
||||
osxsel_outmax = n;
|
||||
[osxsel_outlock unlock];
|
||||
|
||||
/*
|
||||
* Post a message to the main thread's message queue
|
||||
* telling it that select data is available.
|
||||
*/
|
||||
[NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
|
||||
location:NSMakePoint(0,0)
|
||||
modifierFlags:0
|
||||
timestamp:0
|
||||
windowNumber:0
|
||||
context:nil
|
||||
subtype:0
|
||||
data1:0
|
||||
data2:0]
|
||||
atStart:NO];
|
||||
}
|
||||
|
||||
[pool release];
|
||||
}
|
||||
@end
|
||||
|
||||
void osxsel_init(void)
|
||||
{
|
||||
uxsel_init();
|
||||
|
||||
if (pipe(osxsel_pipe) < 0) {
|
||||
fatalbox("Unable to set up inter-thread pipe for select");
|
||||
}
|
||||
[NSThread detachNewThreadSelector:@selector(runThread:)
|
||||
toTarget:[[[OSXSel alloc] init] retain] withObject:nil];
|
||||
/*
|
||||
* Also initialise (i.e. clear) the input fd_sets. Need not
|
||||
* start a select just yet - the select thread will block until
|
||||
* we have at least one fd for it!
|
||||
*/
|
||||
FD_ZERO(&osxsel_rfds_in);
|
||||
FD_ZERO(&osxsel_wfds_in);
|
||||
FD_ZERO(&osxsel_xfds_in);
|
||||
osxsel_inmax = 0;
|
||||
/*
|
||||
* Initialise the mutex locks used to protect the data passed
|
||||
* between threads.
|
||||
*/
|
||||
osxsel_inlock = [[[NSLock alloc] init] retain];
|
||||
osxsel_outlock = [[[NSLock alloc] init] retain];
|
||||
}
|
||||
|
||||
static void osxsel_start_select(void)
|
||||
{
|
||||
char c = 'g'; /* for `Go!' :-) but it's never used */
|
||||
|
||||
if (!inhibit_start_select)
|
||||
write(osxsel_pipe[1], &c, 1);
|
||||
}
|
||||
|
||||
int uxsel_input_add(int fd, int rwx)
|
||||
{
|
||||
/*
|
||||
* Add the new fd to the appropriate input fd_sets, then write
|
||||
* to the inter-thread pipe.
|
||||
*/
|
||||
[osxsel_inlock lock];
|
||||
if (rwx & 1)
|
||||
FD_SET(fd, &osxsel_rfds_in);
|
||||
else
|
||||
FD_CLR(fd, &osxsel_rfds_in);
|
||||
if (rwx & 2)
|
||||
FD_SET(fd, &osxsel_wfds_in);
|
||||
else
|
||||
FD_CLR(fd, &osxsel_wfds_in);
|
||||
if (rwx & 4)
|
||||
FD_SET(fd, &osxsel_xfds_in);
|
||||
else
|
||||
FD_CLR(fd, &osxsel_xfds_in);
|
||||
if (osxsel_inmax < fd+1)
|
||||
osxsel_inmax = fd+1;
|
||||
[osxsel_inlock unlock];
|
||||
osxsel_start_select();
|
||||
|
||||
/*
|
||||
* We must return an `id' which will be passed back to us at
|
||||
* the time of uxsel_input_remove. Since we have no need to
|
||||
* store ids in that sense, we might as well go with the fd
|
||||
* itself.
|
||||
*/
|
||||
return fd;
|
||||
}
|
||||
|
||||
void uxsel_input_remove(int id)
|
||||
{
|
||||
/*
|
||||
* Remove the fd from all the input fd_sets. In this
|
||||
* implementation, the simplest way to do that is to call
|
||||
* uxsel_input_add with rwx==0!
|
||||
*/
|
||||
uxsel_input_add(id, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function called in the main thread to process results. It will
|
||||
* have to read the output fd_sets, go through them, call back to
|
||||
* uxsel with the results, and then write to the inter-thread pipe.
|
||||
*
|
||||
* This function will have to be called from an event handler in
|
||||
* osxmain.m, which will therefore necessarily contain a small part
|
||||
* of this mechanism (along with calling osxsel_init).
|
||||
*/
|
||||
void osxsel_process_results(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* We must write to the pipe to start a fresh select _even if_
|
||||
* there were no changes. So for efficiency, we set a flag here
|
||||
* which inhibits uxsel_input_{add,remove} from writing to the
|
||||
* pipe; then once we finish processing, we clear the flag
|
||||
* again and write a single byte ourselves. It's cleaner,
|
||||
* because it wakes up the select thread fewer times.
|
||||
*/
|
||||
inhibit_start_select = TRUE;
|
||||
|
||||
[osxsel_outlock lock];
|
||||
|
||||
for (i = 0; i < osxsel_outmax; i++) {
|
||||
if (FD_ISSET(i, &osxsel_xfds_out))
|
||||
select_result(i, 4);
|
||||
}
|
||||
for (i = 0; i < osxsel_outmax; i++) {
|
||||
if (FD_ISSET(i, &osxsel_rfds_out))
|
||||
select_result(i, 1);
|
||||
}
|
||||
for (i = 0; i < osxsel_outmax; i++) {
|
||||
if (FD_ISSET(i, &osxsel_wfds_out))
|
||||
select_result(i, 2);
|
||||
}
|
||||
|
||||
[osxsel_outlock unlock];
|
||||
|
||||
inhibit_start_select = FALSE;
|
||||
osxsel_start_select();
|
||||
}
|
1227
macosx/osxwin.m
1227
macosx/osxwin.m
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -9,10 +9,6 @@
|
|||
|
||||
#include "winstuff.h"
|
||||
|
||||
#elif defined(MACOSX)
|
||||
|
||||
#include "osx.h"
|
||||
|
||||
#else
|
||||
|
||||
#include "unix.h"
|
||||
|
|
Загрузка…
Ссылка в новой задаче