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