Add asynchronous callback capability to the askappend() alert box.
[u/mdw/putty] / macosx / osxwin.m
CommitLineData
1ddda1ca 1/*
2 * osxwin.m: code to manage a session window in Mac OS X PuTTY.
3 */
4
5#import <Cocoa/Cocoa.h>
6#include "putty.h"
7#include "terminal.h"
8#include "osxclass.h"
9
10/* Colours come in two flavours: configurable, and xterm-extended. */
11#define NCFGCOLOURS (lenof(((Config *)0)->colours))
12#define NEXTCOLOURS 240 /* 216 colour-cube plus 24 shades of grey */
13#define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS)
14
15/*
16 * The key component of the per-session data is the SessionWindow
17 * class. A pointer to this is used as the frontend handle, to be
18 * passed to all the platform-independent subsystems that require
19 * one.
20 */
21
22@interface TerminalView : NSImageView
23{
24 NSFont *font;
25 NSImage *image;
26 Terminal *term;
27 Config cfg;
28 NSColor *colours[NALLCOLOURS];
29 float fw, fasc, fdesc, fh;
30}
31- (void)drawStartFinish:(BOOL)start;
32- (void)setColour:(int)n r:(float)r g:(float)g b:(float)b;
33- (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y
34 attr:(unsigned long)attr lattr:(int)lattr;
35@end
36
37@implementation TerminalView
38- (BOOL)isFlipped
39{
40 return YES;
41}
42- (id)initWithTerminal:(Terminal *)aTerm config:(Config)aCfg
43{
44 float w, h;
45
46 self = [self initWithFrame:NSMakeRect(0,0,100,100)];
47
48 term = aTerm;
49 cfg = aCfg;
50
51 /*
52 * Initialise the fonts we're going to use.
53 *
54 * FIXME: for the moment I'm sticking with exactly one default font.
55 */
56 font = [NSFont userFixedPitchFontOfSize:0];
57
58 /*
59 * Now determine the size of the primary font.
60 *
61 * FIXME: If we have multiple fonts, we may need to set fasc
62 * and fdesc to the _maximum_ asc and desc out of all the
63 * fonts, _before_ adding them together to get fh.
64 */
65 fw = [font widthOfString:@"A"];
66 fasc = [font ascender];
67 fdesc = -[font descender];
68 fh = fasc + fdesc;
69 fh = (int)fh + (fh > (int)fh); /* round up, ickily */
70
71 /*
72 * Use this to figure out the size of the terminal view.
73 */
74 w = fw * term->cols;
75 h = fh * term->rows;
76
77 /*
78 * And set our size and subimage.
79 */
80 image = [[NSImage alloc] initWithSize:NSMakeSize(w,h)];
81 [image setFlipped:YES];
82 [self setImage:image];
83 [self setFrame:NSMakeRect(0,0,w,h)];
84
85 term_invalidate(term);
86
87 return self;
88}
89- (void)drawStartFinish:(BOOL)start
90{
91 if (start)
92 [image lockFocus];
93 else
94 [image unlockFocus];
95}
96- (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y
97 attr:(unsigned long)attr lattr:(int)lattr
98{
99 int nfg, nbg, rlen, widefactor;
100 float ox, oy, tw, th;
101 NSDictionary *attrdict;
102
103 /* FIXME: TATTR_COMBINING */
104
105 nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
106 nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
107 if (attr & ATTR_REVERSE) {
108 int t = nfg;
109 nfg = nbg;
110 nbg = t;
111 }
112 if (cfg.bold_colour && (attr & ATTR_BOLD)) {
113 if (nfg < 16) nfg |= 8;
114 else if (nfg >= 256) nfg |= 1;
115 }
116 if (cfg.bold_colour && (attr & ATTR_BLINK)) {
117 if (nbg < 16) nbg |= 8;
118 else if (nbg >= 256) nbg |= 1;
119 }
120 if (attr & TATTR_ACTCURS) {
121 nfg = 260;
122 nbg = 261;
123 }
124
125 if (attr & ATTR_WIDE) {
126 widefactor = 2;
127 /* FIXME: what do we actually have to do about wide characters? */
128 } else {
129 widefactor = 1;
130 }
131
132 /* FIXME: ATTR_BOLD without cfg.bold_colour */
133
134 if ((lattr & LATTR_MODE) != LATTR_NORM) {
135 x *= 2;
136 if (x >= term->cols)
137 return;
138 if (x + len*2*widefactor > term->cols)
139 len = (term->cols-x)/2/widefactor;/* trim to LH half */
140 rlen = len * 2;
141 } else
142 rlen = len;
143
144 /* FIXME: how do we actually implement double-{width,height} lattrs? */
145
146 ox = x * fw;
147 oy = y * fh;
148 tw = rlen * widefactor * fw;
149 th = fh;
150
151 /*
152 * Set the clipping rectangle.
153 */
154 [[NSGraphicsContext currentContext] saveGraphicsState];
155 [NSBezierPath clipRect:NSMakeRect(ox, oy, tw, th)];
156
157 attrdict = [NSDictionary dictionaryWithObjectsAndKeys:
158 colours[nfg], NSForegroundColorAttributeName,
159 colours[nbg], NSBackgroundColorAttributeName,
160 font, NSFontAttributeName, nil];
161
162 /*
163 * Create an NSString and draw it.
164 *
165 * Annoyingly, although our input is wchar_t which is four
166 * bytes wide on OS X and terminal.c supports 32-bit Unicode,
167 * we must convert into the two-byte type `unichar' to store in
168 * NSString, so we lose display capability for extra-BMP stuff
169 * at this point.
170 */
171 {
172 NSString *string;
173 unichar *utext;
174 int i;
175
176 utext = snewn(len, unichar);
177 for (i = 0; i < len; i++)
178 utext[i] = (text[i] >= 0x10000 ? 0xFFFD : text[i]);
179
180 string = [NSString stringWithCharacters:utext length:len];
181 [string drawAtPoint:NSMakePoint(ox, oy) withAttributes:attrdict];
182
183 sfree(utext);
184 }
185
186 /*
187 * Restore the graphics state from before the clipRect: call.
188 */
189 [[NSGraphicsContext currentContext] restoreGraphicsState];
190
191 /*
192 * And flag this area as needing display.
193 */
194 [self setNeedsDisplayInRect:NSMakeRect(ox, oy, tw, th)];
195}
196
197- (void)setColour:(int)n r:(float)r g:(float)g b:(float)b
198{
199 assert(n >= 0 && n < lenof(colours));
200 colours[n] = [[NSColor colorWithDeviceRed:r green:g blue:b alpha:1.0]
201 retain];
202}
203@end
204
205@implementation SessionWindow
206- (id)initWithConfig:(Config)aCfg
207{
208 NSRect rect = { {0,0}, {0,0} };
209
3d9449a1 210 alert_ctx = NULL;
211
1ddda1ca 212 cfg = aCfg; /* structure copy */
213
214 init_ucs(&ucsdata, cfg.line_codepage, cfg.utf8_override,
215 CS_UTF8, cfg.vtmode);
216 term = term_init(&cfg, &ucsdata, self);
217 logctx = log_init(self, &cfg);
218 term_provide_logctx(term, logctx);
219 term_size(term, cfg.height, cfg.width, cfg.savelines);
220
221 termview = [[[TerminalView alloc] initWithTerminal:term config:cfg]
222 autorelease];
223
224 /*
225 * Now work out the size of the window.
226 */
227 rect = [termview frame];
228 rect.origin = NSMakePoint(0,0);
229 rect.size.width += 2 * cfg.window_border;
230 rect.size.height += 2 * cfg.window_border;
231
232 /*
233 * Set up a backend.
234 */
235 {
236 int i;
237 back = &pty_backend;
238 for (i = 0; backends[i].backend != NULL; i++)
239 if (backends[i].protocol == cfg.protocol) {
240 back = backends[i].backend;
241 break;
242 }
243 }
244
245 {
246 const char *error;
247 char *realhost = NULL;
248 error = back->init(self, &backhandle, &cfg, cfg.host, cfg.port,
249 &realhost, cfg.tcp_nodelay, cfg.tcp_keepalives);
250 if (error) {
251 fatalbox("%s\n", error); /* FIXME: connection_fatal at worst */
252 }
253
254 if (realhost)
255 sfree(realhost); /* FIXME: do something with this */
256 }
919baedb 257 back->provide_logctx(backhandle, logctx);
1ddda1ca 258
259 /*
260 * Create a line discipline. (This must be done after creating
261 * the terminal _and_ the backend, since it needs to be passed
262 * pointers to both.)
263 */
264 ldisc = ldisc_create(&cfg, term, back, backhandle, self);
265
266 /*
267 * FIXME: Set up a scrollbar.
268 */
269
270 self = [super initWithContentRect:rect
271 styleMask:(NSTitledWindowMask | NSMiniaturizableWindowMask |
272 NSClosableWindowMask)
273 backing:NSBackingStoreBuffered
274 defer:YES];
275 [self setTitle:@"PuTTY"];
276
277 [self setIgnoresMouseEvents:NO];
278
279 /*
280 * Put the terminal view in the window.
281 */
282 rect = [termview frame];
283 rect.origin = NSMakePoint(cfg.window_border, cfg.window_border);
284 [termview setFrame:rect];
285 [[self contentView] addSubview:termview];
286
287 /*
288 * Set up the colour palette.
289 */
290 palette_reset(self);
291
292 /*
293 * FIXME: Only the _first_ document window should be centred.
294 * The subsequent ones should appear down and to the right of
295 * it, probably using the cascade function provided by Cocoa.
296 * Also we're apparently required by the HIG to remember and
297 * reuse previous positions of windows, although I'm not sure
298 * how that works if the user opens more than one of the same
299 * session type.
300 */
301 [self center]; /* :-) */
302
303 return self;
304}
305
306- (void)dealloc
307{
308 /*
309 * FIXME: Here we must deallocate all sorts of stuff: the
310 * terminal, the backend, the ldisc, the logctx, you name it.
311 * Do so.
312 */
3d9449a1 313 sfree(alert_ctx);
1ddda1ca 314 [super dealloc];
315}
316
317- (void)drawStartFinish:(BOOL)start
318{
319 [termview drawStartFinish:start];
320}
321
322- (void)setColour:(int)n r:(float)r g:(float)g b:(float)b
323{
324 [termview setColour:n r:r g:g b:b];
325}
326
327- (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y
328 attr:(unsigned long)attr lattr:(int)lattr
329{
330 /* Pass this straight on to the TerminalView. */
331 [termview doText:text len:len x:x y:y attr:attr lattr:lattr];
332}
333
334- (Config *)cfg
335{
336 return &cfg;
337}
338
339- (void)keyDown:(NSEvent *)ev
340{
341 NSString *s = [ev characters];
342 int i;
343 int n = [s length], c = [s characterAtIndex:0], m = [ev modifierFlags];
344 int cm = [[ev charactersIgnoringModifiers] characterAtIndex:0];
345 wchar_t output[32];
346 char coutput[32];
347 int use_coutput = FALSE, special = FALSE, start, end;
348
349printf("n=%d c=U+%04x cm=U+%04x m=%08x\n", n, c, cm, m);
350
351 /*
352 * FIXME: Alt+numberpad codes.
353 */
354
355 /*
356 * Shift and Ctrl with PageUp/PageDown for scrollback.
357 */
358 if (n == 1 && c == NSPageUpFunctionKey && (m & NSShiftKeyMask)) {
359 term_scroll(term, 0, -term->rows/2);
360 return;
361 }
362 if (n == 1 && c == NSPageUpFunctionKey && (m & NSControlKeyMask)) {
363 term_scroll(term, 0, -1);
364 return;
365 }
366 if (n == 1 && c == NSPageDownFunctionKey && (m & NSShiftKeyMask)) {
367 term_scroll(term, 0, +term->rows/2);
368 return;
369 }
370 if (n == 1 && c == NSPageDownFunctionKey && (m & NSControlKeyMask)) {
371 term_scroll(term, 0, +1);
372 return;
373 }
374
375 /*
376 * FIXME: Shift-Ins for paste? Or is that not Maccy enough?
377 */
378
379 /*
380 * FIXME: Alt (Option? Command?) prefix in general.
381 *
382 * (Note that Alt-Shift-thing will work just by looking at
383 * charactersIgnoringModifiers; but Alt-Ctrl-thing will need
384 * processing properly, and Alt-as-in-Option won't happen at
385 * all. Hmmm.)
386 *
387 * (Note also that we need to be able to override menu key
388 * equivalents before this is particularly useful.)
389 */
390 start = 1;
391 end = start;
392
393 /*
394 * Ctrl-` is the same as Ctrl-\, unless we already have a
395 * better idea.
396 */
397 if ((m & NSControlKeyMask) && n == 1 && cm == '`' && c == '`') {
398 output[1] = '\x1c';
399 end = 2;
400 }
401
402 /* We handle Return ourselves, because it needs to be flagged as
403 * special to ldisc. */
404 if (n == 1 && c == '\015') {
405 coutput[1] = '\015';
406 use_coutput = TRUE;
407 end = 2;
408 special = TRUE;
409 }
410
411 /* Control-Shift-Space is 160 (ISO8859 nonbreaking space) */
412 if (n == 1 && (m & NSControlKeyMask) && (m & NSShiftKeyMask) &&
413 cm == ' ') {
414 output[1] = '\240';
415 end = 2;
416 }
417
418 /* Control-2, Control-Space and Control-@ are all NUL. */
419 if ((m & NSControlKeyMask) && n == 1 &&
420 (cm == '2' || cm == '@' || cm == ' ') && c == cm) {
421 output[1] = '\0';
422 end = 2;
423 }
424
425 /* We don't let MacOS tell us what Backspace is! We know better. */
426 if (cm == 0x7F && !(m & NSShiftKeyMask)) {
427 coutput[1] = cfg.bksp_is_delete ? '\x7F' : '\x08';
428 end = 2;
429 use_coutput = special = TRUE;
430 }
431 /* For Shift Backspace, do opposite of what is configured. */
432 if (cm == 0x7F && (m & NSShiftKeyMask)) {
433 coutput[1] = cfg.bksp_is_delete ? '\x08' : '\x7F';
434 end = 2;
435 use_coutput = special = TRUE;
436 }
437
438 /* Shift-Tab is ESC [ Z. Oddly, this combination generates ^Y by
439 * default on MacOS! */
440 if (cm == 0x19 && (m & NSShiftKeyMask) && !(m & NSControlKeyMask)) {
441 end = 1;
442 output[end++] = '\033';
443 output[end++] = '[';
444 output[end++] = 'Z';
445 }
446
447 /*
448 * NetHack keypad mode.
449 */
450 if (cfg.nethack_keypad && (m & NSNumericPadKeyMask)) {
451 wchar_t *keys = NULL;
452 switch (cm) {
453 case '1': keys = L"bB"; break;
454 case '2': keys = L"jJ"; break;
455 case '3': keys = L"nN"; break;
456 case '4': keys = L"hH"; break;
457 case '5': keys = L".."; break;
458 case '6': keys = L"lL"; break;
459 case '7': keys = L"yY"; break;
460 case '8': keys = L"kK"; break;
461 case '9': keys = L"uU"; break;
462 }
463 if (keys) {
464 end = 2;
465 if (m & NSShiftKeyMask)
466 output[1] = keys[1];
467 else
468 output[1] = keys[0];
469 goto done;
470 }
471 }
472
473 /*
474 * Application keypad mode.
475 */
476 if (term->app_keypad_keys && !cfg.no_applic_k &&
477 (m & NSNumericPadKeyMask)) {
478 int xkey = 0;
479 switch (cm) {
480 case NSClearLineFunctionKey: xkey = 'P'; break;
481 case '=': xkey = 'Q'; break;
482 case '/': xkey = 'R'; break;
483 case '*': xkey = 'S'; break;
484 /*
485 * FIXME: keypad - and + need to be mapped to ESC O l
486 * and ESC O k, or ESC O l and ESC O m, depending on
487 * xterm function key mode, and I can't remember which
488 * goes where.
489 */
490 case '\003': xkey = 'M'; break;
491 case '0': xkey = 'p'; break;
492 case '1': xkey = 'q'; break;
493 case '2': xkey = 'r'; break;
494 case '3': xkey = 's'; break;
495 case '4': xkey = 't'; break;
496 case '5': xkey = 'u'; break;
497 case '6': xkey = 'v'; break;
498 case '7': xkey = 'w'; break;
499 case '8': xkey = 'x'; break;
500 case '9': xkey = 'y'; break;
501 case '.': xkey = 'n'; break;
502 }
503 if (xkey) {
504 if (term->vt52_mode) {
505 if (xkey >= 'P' && xkey <= 'S') {
506 output[end++] = '\033';
507 output[end++] = xkey;
508 } else {
509 output[end++] = '\033';
510 output[end++] = '?';
511 output[end++] = xkey;
512 }
513 } else {
514 output[end++] = '\033';
515 output[end++] = 'O';
516 output[end++] = xkey;
517 }
518 goto done;
519 }
520 }
521
522 /*
523 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
524 * for integer decimal nn.)
525 *
526 * We also deal with the weird ones here. Linux VCs replace F1
527 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
528 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
529 * respectively.
530 */
531 {
532 int code = 0;
533 switch (cm) {
534 case NSF1FunctionKey:
535 code = (m & NSShiftKeyMask ? 23 : 11);
536 break;
537 case NSF2FunctionKey:
538 code = (m & NSShiftKeyMask ? 24 : 12);
539 break;
540 case NSF3FunctionKey:
541 code = (m & NSShiftKeyMask ? 25 : 13);
542 break;
543 case NSF4FunctionKey:
544 code = (m & NSShiftKeyMask ? 26 : 14);
545 break;
546 case NSF5FunctionKey:
547 code = (m & NSShiftKeyMask ? 28 : 15);
548 break;
549 case NSF6FunctionKey:
550 code = (m & NSShiftKeyMask ? 29 : 17);
551 break;
552 case NSF7FunctionKey:
553 code = (m & NSShiftKeyMask ? 31 : 18);
554 break;
555 case NSF8FunctionKey:
556 code = (m & NSShiftKeyMask ? 32 : 19);
557 break;
558 case NSF9FunctionKey:
559 code = (m & NSShiftKeyMask ? 33 : 20);
560 break;
561 case NSF10FunctionKey:
562 code = (m & NSShiftKeyMask ? 34 : 21);
563 break;
564 case NSF11FunctionKey:
565 code = 23;
566 break;
567 case NSF12FunctionKey:
568 code = 24;
569 break;
570 case NSF13FunctionKey:
571 code = 25;
572 break;
573 case NSF14FunctionKey:
574 code = 26;
575 break;
576 case NSF15FunctionKey:
577 code = 28;
578 break;
579 case NSF16FunctionKey:
580 code = 29;
581 break;
582 case NSF17FunctionKey:
583 code = 31;
584 break;
585 case NSF18FunctionKey:
586 code = 32;
587 break;
588 case NSF19FunctionKey:
589 code = 33;
590 break;
591 case NSF20FunctionKey:
592 code = 34;
593 break;
594 }
595 if (!(m & NSControlKeyMask)) switch (cm) {
596 case NSHomeFunctionKey:
597 code = 1;
598 break;
599#ifdef FIXME
600 case GDK_Insert: case GDK_KP_Insert:
601 code = 2;
602 break;
603#endif
604 case NSDeleteFunctionKey:
605 code = 3;
606 break;
607 case NSEndFunctionKey:
608 code = 4;
609 break;
610 case NSPageUpFunctionKey:
611 code = 5;
612 break;
613 case NSPageDownFunctionKey:
614 code = 6;
615 break;
616 }
617 /* Reorder edit keys to physical order */
618 if (cfg.funky_type == FUNKY_VT400 && code <= 6)
619 code = "\0\2\1\4\5\3\6"[code];
620
621 if (term->vt52_mode && code > 0 && code <= 6) {
622 output[end++] = '\033';
623 output[end++] = " HLMEIG"[code];
624 goto done;
625 }
626
627 if (cfg.funky_type == FUNKY_SCO && /* SCO function keys */
628 code >= 11 && code <= 34) {
629 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
630 int index = 0;
631 switch (cm) {
632 case NSF1FunctionKey: index = 0; break;
633 case NSF2FunctionKey: index = 1; break;
634 case NSF3FunctionKey: index = 2; break;
635 case NSF4FunctionKey: index = 3; break;
636 case NSF5FunctionKey: index = 4; break;
637 case NSF6FunctionKey: index = 5; break;
638 case NSF7FunctionKey: index = 6; break;
639 case NSF8FunctionKey: index = 7; break;
640 case NSF9FunctionKey: index = 8; break;
641 case NSF10FunctionKey: index = 9; break;
642 case NSF11FunctionKey: index = 10; break;
643 case NSF12FunctionKey: index = 11; break;
644 }
645 if (m & NSShiftKeyMask) index += 12;
646 if (m & NSControlKeyMask) index += 24;
647 output[end++] = '\033';
648 output[end++] = '[';
649 output[end++] = codes[index];
650 goto done;
651 }
652 if (cfg.funky_type == FUNKY_SCO && /* SCO small keypad */
653 code >= 1 && code <= 6) {
654 char codes[] = "HL.FIG";
655 if (code == 3) {
656 output[1] = '\x7F';
657 end = 2;
658 } else {
659 output[end++] = '\033';
660 output[end++] = '[';
661 output[end++] = codes[code-1];
662 }
663 goto done;
664 }
665 if ((term->vt52_mode || cfg.funky_type == FUNKY_VT100P) &&
666 code >= 11 && code <= 24) {
667 int offt = 0;
668 if (code > 15)
669 offt++;
670 if (code > 21)
671 offt++;
672 if (term->vt52_mode) {
673 output[end++] = '\033';
674 output[end++] = code + 'P' - 11 - offt;
675 } else {
676 output[end++] = '\033';
677 output[end++] = 'O';
678 output[end++] = code + 'P' - 11 - offt;
679 }
680 goto done;
681 }
682 if (cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) {
683 output[end++] = '\033';
684 output[end++] = '[';
685 output[end++] = '[';
686 output[end++] = code + 'A' - 11;
687 goto done;
688 }
689 if (cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) {
690 if (term->vt52_mode) {
691 output[end++] = '\033';
692 output[end++] = code + 'P' - 11;
693 } else {
694 output[end++] = '\033';
695 output[end++] = 'O';
696 output[end++] = code + 'P' - 11;
697 }
698 goto done;
699 }
700 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
701 if (code == 1) {
702 output[end++] = '\033';
703 output[end++] = '[';
704 output[end++] = 'H';
705 } else {
706 output[end++] = '\033';
707 output[end++] = 'O';
708 output[end++] = 'w';
709 }
710 goto done;
711 }
712 if (code) {
713 char buf[20];
714 sprintf(buf, "\x1B[%d~", code);
715 for (i = 0; buf[i]; i++)
716 output[end++] = buf[i];
717 goto done;
718 }
719 }
720
721 /*
722 * Cursor keys. (This includes the numberpad cursor keys,
723 * if we haven't already done them due to app keypad mode.)
724 */
725 {
726 int xkey = 0;
727 switch (cm) {
728 case NSUpArrowFunctionKey: xkey = 'A'; break;
729 case NSDownArrowFunctionKey: xkey = 'B'; break;
730 case NSRightArrowFunctionKey: xkey = 'C'; break;
731 case NSLeftArrowFunctionKey: xkey = 'D'; break;
732 }
733 if (xkey) {
734 /*
735 * The arrow keys normally do ESC [ A and so on. In
736 * app cursor keys mode they do ESC O A instead.
737 * Ctrl toggles the two modes.
738 */
739 if (term->vt52_mode) {
740 output[end++] = '\033';
741 output[end++] = xkey;
742 } else if (!term->app_cursor_keys ^ !(m & NSControlKeyMask)) {
743 output[end++] = '\033';
744 output[end++] = 'O';
745 output[end++] = xkey;
746 } else {
747 output[end++] = '\033';
748 output[end++] = '[';
749 output[end++] = xkey;
750 }
751 goto done;
752 }
753 }
754
755 done:
756
757 /*
758 * Failing everything else, send the exact Unicode we got from
759 * OS X.
760 */
761 if (end == start) {
762 if (n > lenof(output)-start)
763 n = lenof(output)-start; /* _shouldn't_ happen! */
764 for (i = 0; i < n; i++) {
765 output[i+start] = [s characterAtIndex:i];
766 }
767 end = n+start;
768 }
769
770 if (use_coutput) {
771 assert(special);
772 assert(end < lenof(coutput));
773 coutput[end] = '\0';
774 ldisc_send(ldisc, coutput+start, -2, TRUE);
775 } else {
776 luni_send(ldisc, output+start, end-start, TRUE);
777 }
778}
779
780- (int)fromBackend:(const char *)data len:(int)len isStderr:(int)is_stderr
781{
782 return term_data(term, is_stderr, data, len);
783}
784
3d9449a1 785- (void)startAlert:(NSAlert *)alert
786 withCallback:(void (*)(void *, int))callback andCtx:(void *)ctx
787{
919baedb 788 if (alert_ctx || alert_qhead) {
789 /*
790 * Queue this alert to be shown later.
791 */
792 struct alert_queue *qitem = snew(struct alert_queue);
793 qitem->next = NULL;
794 qitem->alert = alert;
795 qitem->callback = callback;
796 qitem->ctx = ctx;
797 if (alert_qtail)
798 alert_qtail->next = qitem;
799 else
800 alert_qhead = qitem;
801 alert_qtail = qitem;
802 } else {
803 alert_callback = callback;
804 alert_ctx = ctx; /* NB this is assumed to need freeing! */
805 [alert beginSheetModalForWindow:self modalDelegate:self
806 didEndSelector:@selector(alertSheetDidEnd:returnCode:contextInfo:)
807 contextInfo:NULL];
808 }
3d9449a1 809}
810
811- (void)alertSheetDidEnd:(NSAlert *)alert returnCode:(int)returnCode
812 contextInfo:(void *)contextInfo
813{
4763c1c2 814 [self performSelectorOnMainThread:
815 @selector(alertSheetDidFinishEnding:)
816 withObject:[NSNumber numberWithInt:returnCode]
817 waitUntilDone:NO];
818}
819
820- (void)alertSheetDidFinishEnding:(id)object
821{
822 int returnCode = [object intValue];
919baedb 823
824 alert_callback(alert_ctx, returnCode); /* transfers ownership of ctx */
4763c1c2 825
826 /*
919baedb 827 * If there's an alert in our queue (either already or because
828 * the callback just queued it), start it.
4763c1c2 829 */
919baedb 830 if (alert_qhead) {
831 struct alert_queue *qnext;
832
833 alert_callback = alert_qhead->callback;
834 alert_ctx = alert_qhead->ctx;
835 [alert_qhead->alert beginSheetModalForWindow:self modalDelegate:self
836 didEndSelector:@selector(alertSheetDidEnd:returnCode:contextInfo:)
837 contextInfo:NULL];
838
839 qnext = alert_qhead->next;
840 sfree(alert_qhead);
841 alert_qhead = qnext;
842 if (!qnext)
843 alert_qtail = NULL;
844 } else {
845 alert_ctx = NULL;
846 }
3d9449a1 847}
848
1ddda1ca 849@end
850
851int from_backend(void *frontend, int is_stderr, const char *data, int len)
852{
853 SessionWindow *win = (SessionWindow *)frontend;
854 return [win fromBackend:data len:len isStderr:is_stderr];
855}
856
857void frontend_keypress(void *handle)
858{
859 /* FIXME */
860}
861
862void connection_fatal(void *frontend, char *p, ...)
863{
864 //SessionWindow *win = (SessionWindow *)frontend;
865 /* FIXME: proper OS X GUI stuff */
866 va_list ap;
867 fprintf(stderr, "FATAL ERROR: ");
868 va_start(ap, p);
869 vfprintf(stderr, p, ap);
870 va_end(ap);
871 fputc('\n', stderr);
872 exit(1);
873}
874
875void notify_remote_exit(void *frontend)
876{
877 //SessionWindow *win = (SessionWindow *)frontend;
878 /* FIXME */
879}
880
881void ldisc_update(void *frontend, int echo, int edit)
882{
883 //SessionWindow *win = (SessionWindow *)frontend;
884 /*
885 * In a GUI front end, this need do nothing.
886 */
887}
888
889void update_specials_menu(void *frontend)
890{
891 //SessionWindow *win = (SessionWindow *)frontend;
892 /* FIXME */
893}
894
895/*
896 * This is still called when mode==BELL_VISUAL, even though the
897 * visual bell is handled entirely within terminal.c, because we
898 * may want to perform additional actions on any kind of bell (for
899 * example, taskbar flashing in Windows).
900 */
901void beep(void *frontend, int mode)
902{
903 //SessionWindow *win = (SessionWindow *)frontend;
904 if (mode != BELL_VISUAL)
905 NSBeep();
906}
907
908int char_width(Context ctx, int uc)
909{
910 /*
911 * Under X, any fixed-width font really _is_ fixed-width.
912 * Double-width characters will be dealt with using a separate
913 * font. For the moment we can simply return 1.
914 */
915 return 1;
916}
917
918void palette_set(void *frontend, int n, int r, int g, int b)
919{
920 SessionWindow *win = (SessionWindow *)frontend;
921
922 if (n >= 16)
923 n += 256 - 16;
924 if (n > NALLCOLOURS)
925 return;
926 [win setColour:n r:r/255.0 g:g/255.0 b:b/255.0];
927
928 /*
929 * FIXME: do we need an OS X equivalent of set_window_background?
930 */
931}
932
933void palette_reset(void *frontend)
934{
935 SessionWindow *win = (SessionWindow *)frontend;
936 Config *cfg = [win cfg];
937
938 /* This maps colour indices in cfg to those used in colours[]. */
939 static const int ww[] = {
940 256, 257, 258, 259, 260, 261,
941 0, 8, 1, 9, 2, 10, 3, 11,
942 4, 12, 5, 13, 6, 14, 7, 15
943 };
944
945 int i;
946
947 for (i = 0; i < NCFGCOLOURS; i++) {
948 [win setColour:ww[i] r:cfg->colours[i][0]/255.0
949 g:cfg->colours[i][1]/255.0 b:cfg->colours[i][2]/255.0];
950 }
951
952 for (i = 0; i < NEXTCOLOURS; i++) {
953 if (i < 216) {
954 int r = i / 36, g = (i / 6) % 6, b = i % 6;
955 [win setColour:i+16 r:r/5.0 g:g/5.0 b:b/5.0];
956 } else {
957 int shade = i - 216;
958 float fshade = (shade + 1) / (float)(NEXTCOLOURS - 216 + 1);
959 [win setColour:i+16 r:fshade g:fshade b:fshade];
960 }
961 }
962
963 /*
964 * FIXME: do we need an OS X equivalent of set_window_background?
965 */
966}
967
968Context get_ctx(void *frontend)
969{
970 SessionWindow *win = (SessionWindow *)frontend;
971
972 /*
973 * Lock the drawing focus on the image inside the TerminalView.
974 */
975 [win drawStartFinish:YES];
976
977 [[NSGraphicsContext currentContext] setShouldAntialias:YES];
978
979 /*
980 * Cocoa drawing functions don't take a graphics context: that
981 * parameter is implicit. Therefore, we'll use the frontend
982 * handle itself as the context, on the grounds that it's as
983 * good a thing to use as any.
984 */
985 return frontend;
986}
987
988void free_ctx(Context ctx)
989{
990 SessionWindow *win = (SessionWindow *)ctx;
991
992 [win drawStartFinish:NO];
993}
994
995void do_text(Context ctx, int x, int y, wchar_t *text, int len,
996 unsigned long attr, int lattr)
997{
998 SessionWindow *win = (SessionWindow *)ctx;
999
1000 [win doText:text len:len x:x y:y attr:attr lattr:lattr];
1001}
1002
1003void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
1004 unsigned long attr, int lattr)
1005{
1006 SessionWindow *win = (SessionWindow *)ctx;
1007 Config *cfg = [win cfg];
1008 int active, passive;
1009
1010 if (attr & TATTR_PASCURS) {
1011 attr &= ~TATTR_PASCURS;
1012 passive = 1;
1013 } else
1014 passive = 0;
1015 if ((attr & TATTR_ACTCURS) && cfg->cursor_type != 0) {
1016 attr &= ~TATTR_ACTCURS;
1017 active = 1;
1018 } else
1019 active = 0;
1020
1021 [win doText:text len:len x:x y:y attr:attr lattr:lattr];
1022
1023 /*
1024 * FIXME: now draw the various cursor types (both passive and
1025 * active underlines and vertical lines, plus passive blocks).
1026 */
1027}
1028
1029/*
1030 * Minimise or restore the window in response to a server-side
1031 * request.
1032 */
1033void set_iconic(void *frontend, int iconic)
1034{
1035 //SessionWindow *win = (SessionWindow *)frontend;
1036 /* FIXME */
1037}
1038
1039/*
1040 * Move the window in response to a server-side request.
1041 */
1042void move_window(void *frontend, int x, int y)
1043{
1044 //SessionWindow *win = (SessionWindow *)frontend;
1045 /* FIXME */
1046}
1047
1048/*
1049 * Move the window to the top or bottom of the z-order in response
1050 * to a server-side request.
1051 */
1052void set_zorder(void *frontend, int top)
1053{
1054 //SessionWindow *win = (SessionWindow *)frontend;
1055 /* FIXME */
1056}
1057
1058/*
1059 * Refresh the window in response to a server-side request.
1060 */
1061void refresh_window(void *frontend)
1062{
1063 //SessionWindow *win = (SessionWindow *)frontend;
1064 /* FIXME */
1065}
1066
1067/*
1068 * Maximise or restore the window in response to a server-side
1069 * request.
1070 */
1071void set_zoomed(void *frontend, int zoomed)
1072{
1073 //SessionWindow *win = (SessionWindow *)frontend;
1074 /* FIXME */
1075}
1076
1077/*
1078 * Report whether the window is iconic, for terminal reports.
1079 */
1080int is_iconic(void *frontend)
1081{
1082 //SessionWindow *win = (SessionWindow *)frontend;
1083 return NO; /* FIXME */
1084}
1085
1086/*
1087 * Report the window's position, for terminal reports.
1088 */
1089void get_window_pos(void *frontend, int *x, int *y)
1090{
1091 //SessionWindow *win = (SessionWindow *)frontend;
1092 /* FIXME */
1093}
1094
1095/*
1096 * Report the window's pixel size, for terminal reports.
1097 */
1098void get_window_pixels(void *frontend, int *x, int *y)
1099{
1100 //SessionWindow *win = (SessionWindow *)frontend;
1101 /* FIXME */
1102}
1103
1104/*
1105 * Return the window or icon title.
1106 */
1107char *get_window_title(void *frontend, int icon)
1108{
1109 //SessionWindow *win = (SessionWindow *)frontend;
1110 return NULL; /* FIXME */
1111}
1112
1113void set_title(void *frontend, char *title)
1114{
1115 //SessionWindow *win = (SessionWindow *)frontend;
1116 /* FIXME */
1117}
1118
1119void set_icon(void *frontend, char *title)
1120{
1121 //SessionWindow *win = (SessionWindow *)frontend;
1122 /* FIXME */
1123}
1124
1125void set_sbar(void *frontend, int total, int start, int page)
1126{
1127 //SessionWindow *win = (SessionWindow *)frontend;
1128 /* FIXME */
1129}
1130
1131void get_clip(void *frontend, wchar_t ** p, int *len)
1132{
1133 //SessionWindow *win = (SessionWindow *)frontend;
1134 /* FIXME */
1135}
1136
1137void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
1138{
1139 //SessionWindow *win = (SessionWindow *)frontend;
1140 /* FIXME */
1141}
1142
1143void request_paste(void *frontend)
1144{
1145 //SessionWindow *win = (SessionWindow *)frontend;
1146 /* FIXME */
1147}
1148
1149void set_raw_mouse_mode(void *frontend, int activate)
1150{
1151 //SessionWindow *win = (SessionWindow *)frontend;
1152 /* FIXME */
1153}
1154
1155void request_resize(void *frontend, int w, int h)
1156{
1157 //SessionWindow *win = (SessionWindow *)frontend;
1158 /* FIXME */
1159}
1160
1161void sys_cursor(void *frontend, int x, int y)
1162{
1163 //SessionWindow *win = (SessionWindow *)frontend;
1164 /*
1165 * This is probably meaningless under OS X. FIXME: find out for
1166 * sure.
1167 */
1168}
1169
1170void logevent(void *frontend, const char *string)
1171{
1172 //SessionWindow *win = (SessionWindow *)frontend;
1173 /* FIXME */
1174printf("logevent: %s\n", string);
1175}
1176
1177int font_dimension(void *frontend, int which)/* 0 for width, 1 for height */
1178{
1179 //SessionWindow *win = (SessionWindow *)frontend;
1180 return 1; /* FIXME */
1181}
1182
1183void set_busy_status(void *frontend, int status)
1184{
1185 /*
1186 * We need do nothing here: the OS X `application is busy'
1187 * beachball pointer appears _automatically_ when the
1188 * application isn't responding to GUI messages.
1189 */
1190}