Fix a couple of code paths on which, if fxp_readdir returned an error,
[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
88void fatalbox(char *p, ...)
1ddda1ca 89{
1ddda1ca 90 va_list ap;
1ddda1ca 91 va_start(ap, p);
ec5da310 92 commonfatalbox(p, ap);
1ddda1ca 93 va_end(ap);
1ddda1ca 94}
95
ec5da310 96void modalfatalbox(char *p, ...)
1ddda1ca 97{
1ddda1ca 98 va_list ap;
1ddda1ca 99 va_start(ap, p);
ec5da310 100 commonfatalbox(p, ap);
1ddda1ca 101 va_end(ap);
1ddda1ca 102}
103
104void cmdline_error(char *p, ...)
105{
106 va_list ap;
107 fprintf(stderr, "%s: ", appname);
108 va_start(ap, p);
109 vfprintf(stderr, p, ap);
110 va_end(ap);
111 fputc('\n', stderr);
112 exit(1);
113}
114
115/*
116 * Clean up and exit.
117 */
118void cleanup_exit(int code)
119{
120 /*
121 * Clean up.
122 */
123 sk_cleanup();
124 random_save_seed();
125 exit(code);
126}
127
128/* ----------------------------------------------------------------------
129 * Tiny extension to NSMenuItem which carries a payload of a `void
130 * *', allowing several menu items to invoke the same message but
131 * pass different data through it.
132 */
133@interface DataMenuItem : NSMenuItem
134{
135 void *payload;
136}
137- (void)setPayload:(void *)d;
138- (void *)getPayload;
139@end
140@implementation DataMenuItem
141- (void)setPayload:(void *)d
142{
143 payload = d;
144}
145- (void *)getPayload
146{
147 return payload;
148}
149@end
150
151/* ----------------------------------------------------------------------
152 * Utility routines for constructing OS X menus.
153 */
154
155NSMenu *newmenu(const char *title)
156{
157 return [[[NSMenu allocWithZone:[NSMenu menuZone]]
158 initWithTitle:[NSString stringWithCString:title]]
159 autorelease];
160}
161
162NSMenu *newsubmenu(NSMenu *parent, const char *title)
163{
164 NSMenuItem *item;
165 NSMenu *child;
166
167 item = [[[NSMenuItem allocWithZone:[NSMenu menuZone]]
168 initWithTitle:[NSString stringWithCString:title]
169 action:NULL
170 keyEquivalent:@""]
171 autorelease];
172 child = newmenu(title);
173 [item setEnabled:YES];
174 [item setSubmenu:child];
175 [parent addItem:item];
176 return child;
177}
178
179id initnewitem(NSMenuItem *item, NSMenu *parent, const char *title,
180 const char *key, id target, SEL action)
181{
182 unsigned mask = NSCommandKeyMask;
183
184 if (key[strcspn(key, "-")]) {
185 while (*key && *key != '-') {
186 int c = tolower((unsigned char)*key);
187 if (c == 's') {
188 mask |= NSShiftKeyMask;
189 } else if (c == 'o' || c == 'a') {
190 mask |= NSAlternateKeyMask;
191 }
192 key++;
193 }
194 if (*key)
195 key++;
196 }
197
198 item = [[item initWithTitle:[NSString stringWithCString:title]
199 action:NULL
200 keyEquivalent:[NSString stringWithCString:key]]
201 autorelease];
202
203 if (*key)
204 [item setKeyEquivalentModifierMask: mask];
205
206 [item setEnabled:YES];
207 [item setTarget:target];
208 [item setAction:action];
209
210 [parent addItem:item];
211
212 return item;
213}
214
215NSMenuItem *newitem(NSMenu *parent, char *title, char *key,
216 id target, SEL action)
217{
218 return initnewitem([NSMenuItem allocWithZone:[NSMenu menuZone]],
219 parent, title, key, target, action);
220}
221
222/* ----------------------------------------------------------------------
223 * AppController: the object which receives the messages from all
224 * menu selections that aren't standard OS X functions.
225 */
226@implementation AppController
227
228- (id)init
229{
230 self = [super init];
231 timer = NULL;
232 return self;
233}
234
235- (void)newTerminal:(id)sender
236{
237 id win;
238 Config cfg;
239
240 do_defaults(NULL, &cfg);
241
242 cfg.protocol = -1; /* PROT_TERMINAL */
243
244 win = [[SessionWindow alloc] initWithConfig:cfg];
245 [win makeKeyAndOrderFront:self];
246}
247
248- (void)newSessionConfig:(id)sender
249{
250 id win;
251 Config cfg;
252
253 do_defaults(NULL, &cfg);
254
255 win = [[ConfigWindow alloc] initWithConfig:cfg];
256 [win makeKeyAndOrderFront:self];
257}
258
259- (void)newSessionWithConfig:(id)vdata
260{
261 id win;
262 Config cfg;
263 NSData *data = (NSData *)vdata;
264
265 assert([data length] == sizeof(cfg));
266 [data getBytes:&cfg];
267
268 win = [[SessionWindow alloc] initWithConfig:cfg];
269 [win makeKeyAndOrderFront:self];
270}
271
272- (NSMenu *)applicationDockMenu:(NSApplication *)sender
273{
274 NSMenu *menu = newmenu("Dock Menu");
275 /*
276 * FIXME: Add some useful things to this, probably including
277 * the saved session list.
278 */
279 return menu;
280}
281
282- (void)timerFired:(id)sender
283{
284 long now, next;
285
286 assert(sender == timer);
287
288 /* `sender' is the timer itself, so its userInfo is an NSNumber. */
289 now = [(NSNumber *)[sender userInfo] longValue];
290
291 [sender invalidate];
292
293 timer = NULL;
294
295 if (run_timers(now, &next))
296 [self setTimer:next];
297}
298
299- (void)setTimer:(long)next
300{
301 long interval = next - GETTICKCOUNT();
302 float finterval;
303
304 if (interval <= 0)
305 interval = 1; /* just in case */
306
307 finterval = interval / (float)TICKSPERSEC;
308
309 if (timer) {
310 [timer invalidate];
311 }
312
313 timer = [NSTimer scheduledTimerWithTimeInterval:finterval
314 target:self selector:@selector(timerFired:)
315 userInfo:[NSNumber numberWithLong:next] repeats:NO];
316}
317
318@end
319
320void timer_change_notify(long next)
321{
322 [controller setTimer:next];
323}
324
325/* ----------------------------------------------------------------------
326 * Annoyingly, it looks as if I have to actually subclass
327 * NSApplication if I want to catch NSApplicationDefined events. So
328 * here goes.
329 */
330@interface MyApplication : NSApplication
331{
332}
333@end
334@implementation MyApplication
335- (void)sendEvent:(NSEvent *)ev
336{
337 if ([ev type] == NSApplicationDefined)
338 osxsel_process_results();
339
340 [super sendEvent:ev];
341}
342@end
343
344/* ----------------------------------------------------------------------
345 * Main program. Constructs the menus and runs the application.
346 */
347int main(int argc, char **argv)
348{
349 NSAutoreleasePool *pool;
350 NSMenu *menu;
351 NSMenuItem *item;
352 NSImage *icon;
353
354 pool = [[NSAutoreleasePool alloc] init];
355
356 icon = [NSImage imageNamed:@"NSApplicationIcon"];
357 [MyApplication sharedApplication];
358 [NSApp setApplicationIconImage:icon];
359
360 controller = [[[AppController alloc] init] autorelease];
361 [NSApp setDelegate:controller];
362
363 [NSApp setMainMenu: newmenu("Main Menu")];
364
365 menu = newsubmenu([NSApp mainMenu], "Apple Menu");
366 [NSApp setServicesMenu:newsubmenu(menu, "Services")];
367 [menu addItem:[NSMenuItem separatorItem]];
368 item = newitem(menu, "Hide PuTTY", "h", NSApp, @selector(hide:));
369 item = newitem(menu, "Hide Others", "o-h", NSApp, @selector(hideOtherApplications:));
370 item = newitem(menu, "Show All", "", NSApp, @selector(unhideAllApplications:));
371 [menu addItem:[NSMenuItem separatorItem]];
372 item = newitem(menu, "Quit", "q", NSApp, @selector(terminate:));
373 [NSApp setAppleMenu: menu];
374
375 menu = newsubmenu([NSApp mainMenu], "File");
376 item = newitem(menu, "New", "n", NULL, @selector(newSessionConfig:));
377 item = newitem(menu, "New Terminal", "t", NULL, @selector(newTerminal:));
378 item = newitem(menu, "Close", "w", NULL, @selector(performClose:));
379
380 menu = newsubmenu([NSApp mainMenu], "Window");
381 [NSApp setWindowsMenu: menu];
382 item = newitem(menu, "Minimise Window", "m", NULL, @selector(performMiniaturize:));
383
384// menu = newsubmenu([NSApp mainMenu], "Help");
385// item = newitem(menu, "PuTTY Help", "?", NSApp, @selector(showHelp:));
386
387 /*
388 * Start up the sub-thread doing select().
389 */
390 osxsel_init();
391
392 /*
393 * Start up networking.
394 */
395 sk_init();
396
397 /*
398 * FIXME: To make initial debugging more convenient I'm going
399 * to start by opening a session window unconditionally. This
400 * will probably change later on.
401 */
402 [controller newSessionConfig:nil];
403
404 [NSApp run];
405 [pool release];
406
407 return 0;
408}