]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - macosx/osxmain.m
Add some appropriate bignum typedefs for generic 64-bit systems,
[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 fatalbox(char *p, ...)
89 {
90     va_list ap;
91     va_start(ap, p);
92     commonfatalbox(p, ap);
93     va_end(ap);
94 }
95
96 void modalfatalbox(char *p, ...)
97 {
98     va_list ap;
99     va_start(ap, p);
100     commonfatalbox(p, ap);
101     va_end(ap);
102 }
103
104 void 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  */
118 void 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
155 NSMenu *newmenu(const char *title)
156 {
157     return [[[NSMenu allocWithZone:[NSMenu menuZone]]
158              initWithTitle:[NSString stringWithCString:title]]
159             autorelease];
160 }
161
162 NSMenu *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
179 id 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
215 NSMenuItem *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
320 void 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  */
347 int 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 }