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