Add a nonfatal() function everywhere, to be used for reporting things
[sgt/putty] / macosx / osxmain.m
CommitLineData
1ddda1ca 1/*
2 * osxmain.m: main-program file of Mac OS X PuTTY.
3 */
4
5#import <Cocoa/Cocoa.h>
6
7#define PUTTY_DO_GLOBALS /* actually _define_ globals */
8
9#include "putty.h"
10#include "osxclass.h"
11
12/* ----------------------------------------------------------------------
13 * Global variables.
14 */
15
16AppController *controller;
17
18/* ----------------------------------------------------------------------
19 * Miscellaneous elements of the interface to the cross-platform
20 * and Unix PuTTY code.
21 */
22
1ddda1ca 23char *platform_get_x_display(void) {
24 return NULL;
25}
26
27FontSpec platform_default_fontspec(const char *name)
28{
29 FontSpec ret;
30 /* FIXME */
31 return ret;
32}
33
34Filename platform_default_filename(const char *name)
35{
36 Filename ret;
37 if (!strcmp(name, "LogFileName"))
38 strcpy(ret.path, "putty.log");
39 else
40 *ret.path = '\0';
41 return ret;
42}
43
44char *platform_default_s(const char *name)
45{
46 return NULL;
47}
48
49int platform_default_i(const char *name, int def)
50{
51 if (!strcmp(name, "CloseOnExit"))
52 return 2; /* maps to FORCE_ON after painful rearrangement :-( */
53 return def;
54}
55
56char *x_get_default(const char *key)
57{
58 return NULL; /* this is a stub */
59}
60
ec5da310 61static void commonfatalbox(char *p, va_list ap)
62{
63 char errorbuf[2048];
64 NSAlert *alert;
65
66 /*
67 * We may have come here because we ran out of memory, in which
68 * case it's entirely likely that that further memory
69 * allocations will fail. So (a) we use vsnprintf to format the
70 * error message rather than the usual dupvprintf; and (b) we
71 * have a fallback way to get the message out via stderr if
72 * even creating an NSAlert fails.
73 */
74 vsnprintf(errorbuf, lenof(errorbuf), p, ap);
75
76 alert = [NSAlert alloc];
77 if (!alert) {
78 fprintf(stderr, "fatal error (and NSAlert failed): %s\n", errorbuf);
79 } else {
80 alert = [[alert init] autorelease];
81 [alert addButtonWithTitle:@"Terminate"];
82 [alert setInformativeText:[NSString stringWithCString:errorbuf]];
83 [alert runModal];
84 }
85 exit(1);
86}
87
33f4bde2 88void nonfatal(void *frontend, char *p, ...)
89{
90 char *errorbuf;
91 NSAlert *alert;
92 va_list ap;
93
94 va_start(ap, p);
95 errorbuf = dupvprintf(p, ap);
96 va_end(ap);
97
98 alert = [[[NSAlert alloc] init] autorelease];
99 [alert addButtonWithTitle:@"Error"];
100 [alert setInformativeText:[NSString stringWithCString:errorbuf]];
101 [alert runModal];
102
103 sfree(errorbuf);
104}
105
ec5da310 106void fatalbox(char *p, ...)
1ddda1ca 107{
1ddda1ca 108 va_list ap;
1ddda1ca 109 va_start(ap, p);
ec5da310 110 commonfatalbox(p, ap);
1ddda1ca 111 va_end(ap);
1ddda1ca 112}
113
ec5da310 114void modalfatalbox(char *p, ...)
1ddda1ca 115{
1ddda1ca 116 va_list ap;
1ddda1ca 117 va_start(ap, p);
ec5da310 118 commonfatalbox(p, ap);
1ddda1ca 119 va_end(ap);
1ddda1ca 120}
121
122void cmdline_error(char *p, ...)
123{
124 va_list ap;
125 fprintf(stderr, "%s: ", appname);
126 va_start(ap, p);
127 vfprintf(stderr, p, ap);
128 va_end(ap);
129 fputc('\n', stderr);
130 exit(1);
131}
132
133/*
134 * Clean up and exit.
135 */
136void cleanup_exit(int code)
137{
138 /*
139 * Clean up.
140 */
141 sk_cleanup();
142 random_save_seed();
143 exit(code);
144}
145
146/* ----------------------------------------------------------------------
147 * Tiny extension to NSMenuItem which carries a payload of a `void
148 * *', allowing several menu items to invoke the same message but
149 * pass different data through it.
150 */
151@interface DataMenuItem : NSMenuItem
152{
153 void *payload;
154}
155- (void)setPayload:(void *)d;
156- (void *)getPayload;
157@end
158@implementation DataMenuItem
159- (void)setPayload:(void *)d
160{
161 payload = d;
162}
163- (void *)getPayload
164{
165 return payload;
166}
167@end
168
169/* ----------------------------------------------------------------------
170 * Utility routines for constructing OS X menus.
171 */
172
173NSMenu *newmenu(const char *title)
174{
175 return [[[NSMenu allocWithZone:[NSMenu menuZone]]
176 initWithTitle:[NSString stringWithCString:title]]
177 autorelease];
178}
179
180NSMenu *newsubmenu(NSMenu *parent, const char *title)
181{
182 NSMenuItem *item;
183 NSMenu *child;
184
185 item = [[[NSMenuItem allocWithZone:[NSMenu menuZone]]
186 initWithTitle:[NSString stringWithCString:title]
187 action:NULL
188 keyEquivalent:@""]
189 autorelease];
190 child = newmenu(title);
191 [item setEnabled:YES];
192 [item setSubmenu:child];
193 [parent addItem:item];
194 return child;
195}
196
197id initnewitem(NSMenuItem *item, NSMenu *parent, const char *title,
198 const char *key, id target, SEL action)
199{
200 unsigned mask = NSCommandKeyMask;
201
202 if (key[strcspn(key, "-")]) {
203 while (*key && *key != '-') {
204 int c = tolower((unsigned char)*key);
205 if (c == 's') {
206 mask |= NSShiftKeyMask;
207 } else if (c == 'o' || c == 'a') {
208 mask |= NSAlternateKeyMask;
209 }
210 key++;
211 }
212 if (*key)
213 key++;
214 }
215
216 item = [[item initWithTitle:[NSString stringWithCString:title]
217 action:NULL
218 keyEquivalent:[NSString stringWithCString:key]]
219 autorelease];
220
221 if (*key)
222 [item setKeyEquivalentModifierMask: mask];
223
224 [item setEnabled:YES];
225 [item setTarget:target];
226 [item setAction:action];
227
228 [parent addItem:item];
229
230 return item;
231}
232
233NSMenuItem *newitem(NSMenu *parent, char *title, char *key,
234 id target, SEL action)
235{
236 return initnewitem([NSMenuItem allocWithZone:[NSMenu menuZone]],
237 parent, title, key, target, action);
238}
239
240/* ----------------------------------------------------------------------
241 * AppController: the object which receives the messages from all
242 * menu selections that aren't standard OS X functions.
243 */
244@implementation AppController
245
246- (id)init
247{
248 self = [super init];
249 timer = NULL;
250 return self;
251}
252
253- (void)newTerminal:(id)sender
254{
255 id win;
256 Config cfg;
257
258 do_defaults(NULL, &cfg);
259
260 cfg.protocol = -1; /* PROT_TERMINAL */
261
262 win = [[SessionWindow alloc] initWithConfig:cfg];
263 [win makeKeyAndOrderFront:self];
264}
265
266- (void)newSessionConfig:(id)sender
267{
268 id win;
269 Config cfg;
270
271 do_defaults(NULL, &cfg);
272
273 win = [[ConfigWindow alloc] initWithConfig:cfg];
274 [win makeKeyAndOrderFront:self];
275}
276
277- (void)newSessionWithConfig:(id)vdata
278{
279 id win;
280 Config cfg;
281 NSData *data = (NSData *)vdata;
282
283 assert([data length] == sizeof(cfg));
284 [data getBytes:&cfg];
285
286 win = [[SessionWindow alloc] initWithConfig:cfg];
287 [win makeKeyAndOrderFront:self];
288}
289
290- (NSMenu *)applicationDockMenu:(NSApplication *)sender
291{
292 NSMenu *menu = newmenu("Dock Menu");
293 /*
294 * FIXME: Add some useful things to this, probably including
295 * the saved session list.
296 */
297 return menu;
298}
299
300- (void)timerFired:(id)sender
301{
302 long now, next;
303
304 assert(sender == timer);
305
306 /* `sender' is the timer itself, so its userInfo is an NSNumber. */
307 now = [(NSNumber *)[sender userInfo] longValue];
308
309 [sender invalidate];
310
311 timer = NULL;
312
313 if (run_timers(now, &next))
314 [self setTimer:next];
315}
316
317- (void)setTimer:(long)next
318{
319 long interval = next - GETTICKCOUNT();
320 float finterval;
321
322 if (interval <= 0)
323 interval = 1; /* just in case */
324
325 finterval = interval / (float)TICKSPERSEC;
326
327 if (timer) {
328 [timer invalidate];
329 }
330
331 timer = [NSTimer scheduledTimerWithTimeInterval:finterval
332 target:self selector:@selector(timerFired:)
333 userInfo:[NSNumber numberWithLong:next] repeats:NO];
334}
335
336@end
337
338void timer_change_notify(long next)
339{
340 [controller setTimer:next];
341}
342
343/* ----------------------------------------------------------------------
344 * Annoyingly, it looks as if I have to actually subclass
345 * NSApplication if I want to catch NSApplicationDefined events. So
346 * here goes.
347 */
348@interface MyApplication : NSApplication
349{
350}
351@end
352@implementation MyApplication
353- (void)sendEvent:(NSEvent *)ev
354{
355 if ([ev type] == NSApplicationDefined)
356 osxsel_process_results();
357
358 [super sendEvent:ev];
359}
360@end
361
362/* ----------------------------------------------------------------------
363 * Main program. Constructs the menus and runs the application.
364 */
365int main(int argc, char **argv)
366{
367 NSAutoreleasePool *pool;
368 NSMenu *menu;
369 NSMenuItem *item;
370 NSImage *icon;
371
372 pool = [[NSAutoreleasePool alloc] init];
373
374 icon = [NSImage imageNamed:@"NSApplicationIcon"];
375 [MyApplication sharedApplication];
376 [NSApp setApplicationIconImage:icon];
377
378 controller = [[[AppController alloc] init] autorelease];
379 [NSApp setDelegate:controller];
380
381 [NSApp setMainMenu: newmenu("Main Menu")];
382
383 menu = newsubmenu([NSApp mainMenu], "Apple Menu");
384 [NSApp setServicesMenu:newsubmenu(menu, "Services")];
385 [menu addItem:[NSMenuItem separatorItem]];
386 item = newitem(menu, "Hide PuTTY", "h", NSApp, @selector(hide:));
387 item = newitem(menu, "Hide Others", "o-h", NSApp, @selector(hideOtherApplications:));
388 item = newitem(menu, "Show All", "", NSApp, @selector(unhideAllApplications:));
389 [menu addItem:[NSMenuItem separatorItem]];
390 item = newitem(menu, "Quit", "q", NSApp, @selector(terminate:));
391 [NSApp setAppleMenu: menu];
392
393 menu = newsubmenu([NSApp mainMenu], "File");
394 item = newitem(menu, "New", "n", NULL, @selector(newSessionConfig:));
395 item = newitem(menu, "New Terminal", "t", NULL, @selector(newTerminal:));
396 item = newitem(menu, "Close", "w", NULL, @selector(performClose:));
397
398 menu = newsubmenu([NSApp mainMenu], "Window");
399 [NSApp setWindowsMenu: menu];
400 item = newitem(menu, "Minimise Window", "m", NULL, @selector(performMiniaturize:));
401
402// menu = newsubmenu([NSApp mainMenu], "Help");
403// item = newitem(menu, "PuTTY Help", "?", NSApp, @selector(showHelp:));
404
405 /*
406 * Start up the sub-thread doing select().
407 */
408 osxsel_init();
409
410 /*
411 * Start up networking.
412 */
413 sk_init();
414
415 /*
416 * FIXME: To make initial debugging more convenient I'm going
417 * to start by opening a session window unconditionally. This
418 * will probably change later on.
419 */
420 [controller newSessionConfig:nil];
421
422 [NSApp run];
423 [pool release];
424
425 return 0;
426}