mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-10 18:07:59 +00:00
46bfde32e8
with the Unix port and layering a Cocoa GUI on top. The basics all work: there's a configuration panel and a terminal window, the timing interface works and the select interface functions. The same application can run both SSH (or other network) connections and local pty sessions, and multiple sessions in the same process are fully supported. However, it's horribly unfinished in a wide variety of other ways; anyone interested is invited to read README.OSX and wince at the length and content of its `unfinished' list. [originally from svn r5308]
392 lines
8.7 KiB
Objective-C
392 lines
8.7 KiB
Objective-C
/*
|
|
* 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.
|
|
*/
|
|
|
|
const char platform_x11_best_transport[] = "unix";
|
|
|
|
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 */
|
|
}
|
|
|
|
void modalfatalbox(char *p, ...)
|
|
{
|
|
/* FIXME: proper OS X GUI stuff */
|
|
va_list ap;
|
|
fprintf(stderr, "FATAL ERROR: ");
|
|
va_start(ap, p);
|
|
vfprintf(stderr, p, ap);
|
|
va_end(ap);
|
|
fputc('\n', stderr);
|
|
exit(1);
|
|
}
|
|
|
|
void fatalbox(char *p, ...)
|
|
{
|
|
/* FIXME: proper OS X GUI stuff */
|
|
va_list ap;
|
|
fprintf(stderr, "FATAL ERROR: ");
|
|
va_start(ap, p);
|
|
vfprintf(stderr, p, ap);
|
|
va_end(ap);
|
|
fputc('\n', stderr);
|
|
exit(1);
|
|
}
|
|
|
|
void cmdline_error(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;
|
|
}
|