2 * osxmain.m: main-program file of Mac OS X PuTTY.
\r
5 #import <Cocoa/Cocoa.h>
\r
7 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
\r
10 #include "osxclass.h"
\r
12 /* ----------------------------------------------------------------------
\r
16 AppController *controller;
\r
18 /* ----------------------------------------------------------------------
\r
19 * Miscellaneous elements of the interface to the cross-platform
\r
20 * and Unix PuTTY code.
\r
23 char *platform_get_x_display(void) {
\r
27 FontSpec platform_default_fontspec(const char *name)
\r
34 Filename platform_default_filename(const char *name)
\r
37 if (!strcmp(name, "LogFileName"))
\r
38 strcpy(ret.path, "putty.log");
\r
44 char *platform_default_s(const char *name)
\r
49 int platform_default_i(const char *name, int def)
\r
51 if (!strcmp(name, "CloseOnExit"))
\r
52 return 2; /* maps to FORCE_ON after painful rearrangement :-( */
\r
56 char *x_get_default(const char *key)
\r
58 return NULL; /* this is a stub */
\r
61 static void commonfatalbox(char *p, va_list ap)
\r
63 char errorbuf[2048];
\r
67 * We may have come here because we ran out of memory, in which
\r
68 * case it's entirely likely that that further memory
\r
69 * allocations will fail. So (a) we use vsnprintf to format the
\r
70 * error message rather than the usual dupvprintf; and (b) we
\r
71 * have a fallback way to get the message out via stderr if
\r
72 * even creating an NSAlert fails.
\r
74 vsnprintf(errorbuf, lenof(errorbuf), p, ap);
\r
76 alert = [NSAlert alloc];
\r
78 fprintf(stderr, "fatal error (and NSAlert failed): %s\n", errorbuf);
\r
80 alert = [[alert init] autorelease];
\r
81 [alert addButtonWithTitle:@"Terminate"];
\r
82 [alert setInformativeText:[NSString stringWithCString:errorbuf]];
\r
88 void fatalbox(char *p, ...)
\r
92 commonfatalbox(p, ap);
\r
96 void modalfatalbox(char *p, ...)
\r
100 commonfatalbox(p, ap);
\r
104 void cmdline_error(char *p, ...)
\r
107 fprintf(stderr, "%s: ", appname);
\r
109 vfprintf(stderr, p, ap);
\r
111 fputc('\n', stderr);
\r
116 * Clean up and exit.
\r
118 void cleanup_exit(int code)
\r
124 random_save_seed();
\r
128 /* ----------------------------------------------------------------------
\r
129 * Tiny extension to NSMenuItem which carries a payload of a `void
\r
130 * *', allowing several menu items to invoke the same message but
\r
131 * pass different data through it.
\r
133 @interface DataMenuItem : NSMenuItem
\r
137 - (void)setPayload:(void *)d;
\r
138 - (void *)getPayload;
\r
140 @implementation DataMenuItem
\r
141 - (void)setPayload:(void *)d
\r
145 - (void *)getPayload
\r
151 /* ----------------------------------------------------------------------
\r
152 * Utility routines for constructing OS X menus.
\r
155 NSMenu *newmenu(const char *title)
\r
157 return [[[NSMenu allocWithZone:[NSMenu menuZone]]
\r
158 initWithTitle:[NSString stringWithCString:title]]
\r
162 NSMenu *newsubmenu(NSMenu *parent, const char *title)
\r
167 item = [[[NSMenuItem allocWithZone:[NSMenu menuZone]]
\r
168 initWithTitle:[NSString stringWithCString:title]
\r
172 child = newmenu(title);
\r
173 [item setEnabled:YES];
\r
174 [item setSubmenu:child];
\r
175 [parent addItem:item];
\r
179 id initnewitem(NSMenuItem *item, NSMenu *parent, const char *title,
\r
180 const char *key, id target, SEL action)
\r
182 unsigned mask = NSCommandKeyMask;
\r
184 if (key[strcspn(key, "-")]) {
\r
185 while (*key && *key != '-') {
\r
186 int c = tolower((unsigned char)*key);
\r
188 mask |= NSShiftKeyMask;
\r
189 } else if (c == 'o' || c == 'a') {
\r
190 mask |= NSAlternateKeyMask;
\r
198 item = [[item initWithTitle:[NSString stringWithCString:title]
\r
200 keyEquivalent:[NSString stringWithCString:key]]
\r
204 [item setKeyEquivalentModifierMask: mask];
\r
206 [item setEnabled:YES];
\r
207 [item setTarget:target];
\r
208 [item setAction:action];
\r
210 [parent addItem:item];
\r
215 NSMenuItem *newitem(NSMenu *parent, char *title, char *key,
\r
216 id target, SEL action)
\r
218 return initnewitem([NSMenuItem allocWithZone:[NSMenu menuZone]],
\r
219 parent, title, key, target, action);
\r
222 /* ----------------------------------------------------------------------
\r
223 * AppController: the object which receives the messages from all
\r
224 * menu selections that aren't standard OS X functions.
\r
226 @implementation AppController
\r
230 self = [super init];
\r
235 - (void)newTerminal:(id)sender
\r
240 do_defaults(NULL, &cfg);
\r
242 cfg.protocol = -1; /* PROT_TERMINAL */
\r
244 win = [[SessionWindow alloc] initWithConfig:cfg];
\r
245 [win makeKeyAndOrderFront:self];
\r
248 - (void)newSessionConfig:(id)sender
\r
253 do_defaults(NULL, &cfg);
\r
255 win = [[ConfigWindow alloc] initWithConfig:cfg];
\r
256 [win makeKeyAndOrderFront:self];
\r
259 - (void)newSessionWithConfig:(id)vdata
\r
263 NSData *data = (NSData *)vdata;
\r
265 assert([data length] == sizeof(cfg));
\r
266 [data getBytes:&cfg];
\r
268 win = [[SessionWindow alloc] initWithConfig:cfg];
\r
269 [win makeKeyAndOrderFront:self];
\r
272 - (NSMenu *)applicationDockMenu:(NSApplication *)sender
\r
274 NSMenu *menu = newmenu("Dock Menu");
\r
276 * FIXME: Add some useful things to this, probably including
\r
277 * the saved session list.
\r
282 - (void)timerFired:(id)sender
\r
286 assert(sender == timer);
\r
288 /* `sender' is the timer itself, so its userInfo is an NSNumber. */
\r
289 now = [(NSNumber *)[sender userInfo] longValue];
\r
291 [sender invalidate];
\r
295 if (run_timers(now, &next))
\r
296 [self setTimer:next];
\r
299 - (void)setTimer:(long)next
\r
301 long interval = next - GETTICKCOUNT();
\r
305 interval = 1; /* just in case */
\r
307 finterval = interval / (float)TICKSPERSEC;
\r
310 [timer invalidate];
\r
313 timer = [NSTimer scheduledTimerWithTimeInterval:finterval
\r
314 target:self selector:@selector(timerFired:)
\r
315 userInfo:[NSNumber numberWithLong:next] repeats:NO];
\r
320 void timer_change_notify(long next)
\r
322 [controller setTimer:next];
\r
325 /* ----------------------------------------------------------------------
\r
326 * Annoyingly, it looks as if I have to actually subclass
\r
327 * NSApplication if I want to catch NSApplicationDefined events. So
\r
330 @interface MyApplication : NSApplication
\r
334 @implementation MyApplication
\r
335 - (void)sendEvent:(NSEvent *)ev
\r
337 if ([ev type] == NSApplicationDefined)
\r
338 osxsel_process_results();
\r
340 [super sendEvent:ev];
\r
344 /* ----------------------------------------------------------------------
\r
345 * Main program. Constructs the menus and runs the application.
\r
347 int main(int argc, char **argv)
\r
349 NSAutoreleasePool *pool;
\r
354 pool = [[NSAutoreleasePool alloc] init];
\r
356 icon = [NSImage imageNamed:@"NSApplicationIcon"];
\r
357 [MyApplication sharedApplication];
\r
358 [NSApp setApplicationIconImage:icon];
\r
360 controller = [[[AppController alloc] init] autorelease];
\r
361 [NSApp setDelegate:controller];
\r
363 [NSApp setMainMenu: newmenu("Main Menu")];
\r
365 menu = newsubmenu([NSApp mainMenu], "Apple Menu");
\r
366 [NSApp setServicesMenu:newsubmenu(menu, "Services")];
\r
367 [menu addItem:[NSMenuItem separatorItem]];
\r
368 item = newitem(menu, "Hide PuTTY", "h", NSApp, @selector(hide:));
\r
369 item = newitem(menu, "Hide Others", "o-h", NSApp, @selector(hideOtherApplications:));
\r
370 item = newitem(menu, "Show All", "", NSApp, @selector(unhideAllApplications:));
\r
371 [menu addItem:[NSMenuItem separatorItem]];
\r
372 item = newitem(menu, "Quit", "q", NSApp, @selector(terminate:));
\r
373 [NSApp setAppleMenu: menu];
\r
375 menu = newsubmenu([NSApp mainMenu], "File");
\r
376 item = newitem(menu, "New", "n", NULL, @selector(newSessionConfig:));
\r
377 item = newitem(menu, "New Terminal", "t", NULL, @selector(newTerminal:));
\r
378 item = newitem(menu, "Close", "w", NULL, @selector(performClose:));
\r
380 menu = newsubmenu([NSApp mainMenu], "Window");
\r
381 [NSApp setWindowsMenu: menu];
\r
382 item = newitem(menu, "Minimise Window", "m", NULL, @selector(performMiniaturize:));
\r
384 // menu = newsubmenu([NSApp mainMenu], "Help");
\r
385 // item = newitem(menu, "PuTTY Help", "?", NSApp, @selector(showHelp:));
\r
388 * Start up the sub-thread doing select().
\r
393 * Start up networking.
\r
398 * FIXME: To make initial debugging more convenient I'm going
\r
399 * to start by opening a session window unconditionally. This
\r
400 * will probably change later on.
\r
402 [controller newSessionConfig:nil];
\r