]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - macosx/osxmain.m
Update version number for 0.66 release.
[PuTTY.git] / macosx / osxmain.m
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
16 AppController *controller;
17
18 /* ----------------------------------------------------------------------
19  * Miscellaneous elements of the interface to the cross-platform
20  * and Unix PuTTY code.
21  */
22
23 char *platform_get_x_display(void) {
24     return NULL;
25 }
26
27 FontSpec platform_default_fontspec(const char *name)
28 {
29     FontSpec ret;
30     /* FIXME */
31     return ret;
32 }
33
34 Filename 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
44 char *platform_default_s(const char *name)
45 {
46     return NULL;
47 }
48
49 int 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
56 char *x_get_default(const char *key)
57 {
58     return NULL;                       /* this is a stub */
59 }
60
61 static 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
88 void 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
106 void fatalbox(char *p, ...)
107 {
108     va_list ap;
109     va_start(ap, p);
110     commonfatalbox(p, ap);
111     va_end(ap);
112 }
113
114 void modalfatalbox(char *p, ...)
115 {
116     va_list ap;
117     va_start(ap, p);
118     commonfatalbox(p, ap);
119     va_end(ap);
120 }
121
122 void 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  */
136 void 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
173 NSMenu *newmenu(const char *title)
174 {
175     return [[[NSMenu allocWithZone:[NSMenu menuZone]]
176              initWithTitle:[NSString stringWithCString:title]]
177             autorelease];
178 }
179
180 NSMenu *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
197 id 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
233 NSMenuItem *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
338 void 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  */
365 int 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 }