Sort out close-on-exit, connection_fatal(), fatalbox(), and
[u/mdw/putty] / macosx / osxwin.m
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
210 alert_ctx = NULL;
211
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 }
257 back->provide_logctx(backhandle, logctx);
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 exited = FALSE;
304
305 return self;
306 }
307
308 - (void)dealloc
309 {
310 /*
311 * FIXME: Here we must deallocate all sorts of stuff: the
312 * terminal, the backend, the ldisc, the logctx, you name it.
313 * Do so.
314 */
315 sfree(alert_ctx);
316 if (term)
317 term_free(term);
318 if (logctx)
319 log_free(logctx);
320 if (back)
321 back->free(backhandle);
322 if (ldisc)
323 ldisc_free(ldisc);
324 [super dealloc];
325 }
326
327 - (void)drawStartFinish:(BOOL)start
328 {
329 [termview drawStartFinish:start];
330 }
331
332 - (void)setColour:(int)n r:(float)r g:(float)g b:(float)b
333 {
334 [termview setColour:n r:r g:g b:b];
335 }
336
337 - (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y
338 attr:(unsigned long)attr lattr:(int)lattr
339 {
340 /* Pass this straight on to the TerminalView. */
341 [termview doText:text len:len x:x y:y attr:attr lattr:lattr];
342 }
343
344 - (Config *)cfg
345 {
346 return &cfg;
347 }
348
349 - (void)keyDown:(NSEvent *)ev
350 {
351 NSString *s = [ev characters];
352 int i;
353 int n = [s length], c = [s characterAtIndex:0], m = [ev modifierFlags];
354 int cm = [[ev charactersIgnoringModifiers] characterAtIndex:0];
355 wchar_t output[32];
356 char coutput[32];
357 int use_coutput = FALSE, special = FALSE, start, end;
358
359 //printf("n=%d c=U+%04x cm=U+%04x m=%08x\n", n, c, cm, m);
360
361 /*
362 * FIXME: Alt+numberpad codes.
363 */
364
365 /*
366 * Shift and Ctrl with PageUp/PageDown for scrollback.
367 */
368 if (n == 1 && c == NSPageUpFunctionKey && (m & NSShiftKeyMask)) {
369 term_scroll(term, 0, -term->rows/2);
370 return;
371 }
372 if (n == 1 && c == NSPageUpFunctionKey && (m & NSControlKeyMask)) {
373 term_scroll(term, 0, -1);
374 return;
375 }
376 if (n == 1 && c == NSPageDownFunctionKey && (m & NSShiftKeyMask)) {
377 term_scroll(term, 0, +term->rows/2);
378 return;
379 }
380 if (n == 1 && c == NSPageDownFunctionKey && (m & NSControlKeyMask)) {
381 term_scroll(term, 0, +1);
382 return;
383 }
384
385 /*
386 * FIXME: Shift-Ins for paste? Or is that not Maccy enough?
387 */
388
389 /*
390 * FIXME: Alt (Option? Command?) prefix in general.
391 *
392 * (Note that Alt-Shift-thing will work just by looking at
393 * charactersIgnoringModifiers; but Alt-Ctrl-thing will need
394 * processing properly, and Alt-as-in-Option won't happen at
395 * all. Hmmm.)
396 *
397 * (Note also that we need to be able to override menu key
398 * equivalents before this is particularly useful.)
399 */
400 start = 1;
401 end = start;
402
403 /*
404 * Ctrl-` is the same as Ctrl-\, unless we already have a
405 * better idea.
406 */
407 if ((m & NSControlKeyMask) && n == 1 && cm == '`' && c == '`') {
408 output[1] = '\x1c';
409 end = 2;
410 }
411
412 /* We handle Return ourselves, because it needs to be flagged as
413 * special to ldisc. */
414 if (n == 1 && c == '\015') {
415 coutput[1] = '\015';
416 use_coutput = TRUE;
417 end = 2;
418 special = TRUE;
419 }
420
421 /* Control-Shift-Space is 160 (ISO8859 nonbreaking space) */
422 if (n == 1 && (m & NSControlKeyMask) && (m & NSShiftKeyMask) &&
423 cm == ' ') {
424 output[1] = '\240';
425 end = 2;
426 }
427
428 /* Control-2, Control-Space and Control-@ are all NUL. */
429 if ((m & NSControlKeyMask) && n == 1 &&
430 (cm == '2' || cm == '@' || cm == ' ') && c == cm) {
431 output[1] = '\0';
432 end = 2;
433 }
434
435 /* We don't let MacOS tell us what Backspace is! We know better. */
436 if (cm == 0x7F && !(m & NSShiftKeyMask)) {
437 coutput[1] = cfg.bksp_is_delete ? '\x7F' : '\x08';
438 end = 2;
439 use_coutput = special = TRUE;
440 }
441 /* For Shift Backspace, do opposite of what is configured. */
442 if (cm == 0x7F && (m & NSShiftKeyMask)) {
443 coutput[1] = cfg.bksp_is_delete ? '\x08' : '\x7F';
444 end = 2;
445 use_coutput = special = TRUE;
446 }
447
448 /* Shift-Tab is ESC [ Z. Oddly, this combination generates ^Y by
449 * default on MacOS! */
450 if (cm == 0x19 && (m & NSShiftKeyMask) && !(m & NSControlKeyMask)) {
451 end = 1;
452 output[end++] = '\033';
453 output[end++] = '[';
454 output[end++] = 'Z';
455 }
456
457 /*
458 * NetHack keypad mode.
459 */
460 if (cfg.nethack_keypad && (m & NSNumericPadKeyMask)) {
461 wchar_t *keys = NULL;
462 switch (cm) {
463 case '1': keys = L"bB"; break;
464 case '2': keys = L"jJ"; break;
465 case '3': keys = L"nN"; break;
466 case '4': keys = L"hH"; break;
467 case '5': keys = L".."; break;
468 case '6': keys = L"lL"; break;
469 case '7': keys = L"yY"; break;
470 case '8': keys = L"kK"; break;
471 case '9': keys = L"uU"; break;
472 }
473 if (keys) {
474 end = 2;
475 if (m & NSShiftKeyMask)
476 output[1] = keys[1];
477 else
478 output[1] = keys[0];
479 goto done;
480 }
481 }
482
483 /*
484 * Application keypad mode.
485 */
486 if (term->app_keypad_keys && !cfg.no_applic_k &&
487 (m & NSNumericPadKeyMask)) {
488 int xkey = 0;
489 switch (cm) {
490 case NSClearLineFunctionKey: xkey = 'P'; break;
491 case '=': xkey = 'Q'; break;
492 case '/': xkey = 'R'; break;
493 case '*': xkey = 'S'; break;
494 /*
495 * FIXME: keypad - and + need to be mapped to ESC O l
496 * and ESC O k, or ESC O l and ESC O m, depending on
497 * xterm function key mode, and I can't remember which
498 * goes where.
499 */
500 case '\003': xkey = 'M'; break;
501 case '0': xkey = 'p'; break;
502 case '1': xkey = 'q'; break;
503 case '2': xkey = 'r'; break;
504 case '3': xkey = 's'; break;
505 case '4': xkey = 't'; break;
506 case '5': xkey = 'u'; break;
507 case '6': xkey = 'v'; break;
508 case '7': xkey = 'w'; break;
509 case '8': xkey = 'x'; break;
510 case '9': xkey = 'y'; break;
511 case '.': xkey = 'n'; break;
512 }
513 if (xkey) {
514 if (term->vt52_mode) {
515 if (xkey >= 'P' && xkey <= 'S') {
516 output[end++] = '\033';
517 output[end++] = xkey;
518 } else {
519 output[end++] = '\033';
520 output[end++] = '?';
521 output[end++] = xkey;
522 }
523 } else {
524 output[end++] = '\033';
525 output[end++] = 'O';
526 output[end++] = xkey;
527 }
528 goto done;
529 }
530 }
531
532 /*
533 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
534 * for integer decimal nn.)
535 *
536 * We also deal with the weird ones here. Linux VCs replace F1
537 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
538 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
539 * respectively.
540 */
541 {
542 int code = 0;
543 switch (cm) {
544 case NSF1FunctionKey:
545 code = (m & NSShiftKeyMask ? 23 : 11);
546 break;
547 case NSF2FunctionKey:
548 code = (m & NSShiftKeyMask ? 24 : 12);
549 break;
550 case NSF3FunctionKey:
551 code = (m & NSShiftKeyMask ? 25 : 13);
552 break;
553 case NSF4FunctionKey:
554 code = (m & NSShiftKeyMask ? 26 : 14);
555 break;
556 case NSF5FunctionKey:
557 code = (m & NSShiftKeyMask ? 28 : 15);
558 break;
559 case NSF6FunctionKey:
560 code = (m & NSShiftKeyMask ? 29 : 17);
561 break;
562 case NSF7FunctionKey:
563 code = (m & NSShiftKeyMask ? 31 : 18);
564 break;
565 case NSF8FunctionKey:
566 code = (m & NSShiftKeyMask ? 32 : 19);
567 break;
568 case NSF9FunctionKey:
569 code = (m & NSShiftKeyMask ? 33 : 20);
570 break;
571 case NSF10FunctionKey:
572 code = (m & NSShiftKeyMask ? 34 : 21);
573 break;
574 case NSF11FunctionKey:
575 code = 23;
576 break;
577 case NSF12FunctionKey:
578 code = 24;
579 break;
580 case NSF13FunctionKey:
581 code = 25;
582 break;
583 case NSF14FunctionKey:
584 code = 26;
585 break;
586 case NSF15FunctionKey:
587 code = 28;
588 break;
589 case NSF16FunctionKey:
590 code = 29;
591 break;
592 case NSF17FunctionKey:
593 code = 31;
594 break;
595 case NSF18FunctionKey:
596 code = 32;
597 break;
598 case NSF19FunctionKey:
599 code = 33;
600 break;
601 case NSF20FunctionKey:
602 code = 34;
603 break;
604 }
605 if (!(m & NSControlKeyMask)) switch (cm) {
606 case NSHomeFunctionKey:
607 code = 1;
608 break;
609 #ifdef FIXME
610 case GDK_Insert: case GDK_KP_Insert:
611 code = 2;
612 break;
613 #endif
614 case NSDeleteFunctionKey:
615 code = 3;
616 break;
617 case NSEndFunctionKey:
618 code = 4;
619 break;
620 case NSPageUpFunctionKey:
621 code = 5;
622 break;
623 case NSPageDownFunctionKey:
624 code = 6;
625 break;
626 }
627 /* Reorder edit keys to physical order */
628 if (cfg.funky_type == FUNKY_VT400 && code <= 6)
629 code = "\0\2\1\4\5\3\6"[code];
630
631 if (term->vt52_mode && code > 0 && code <= 6) {
632 output[end++] = '\033';
633 output[end++] = " HLMEIG"[code];
634 goto done;
635 }
636
637 if (cfg.funky_type == FUNKY_SCO && /* SCO function keys */
638 code >= 11 && code <= 34) {
639 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
640 int index = 0;
641 switch (cm) {
642 case NSF1FunctionKey: index = 0; break;
643 case NSF2FunctionKey: index = 1; break;
644 case NSF3FunctionKey: index = 2; break;
645 case NSF4FunctionKey: index = 3; break;
646 case NSF5FunctionKey: index = 4; break;
647 case NSF6FunctionKey: index = 5; break;
648 case NSF7FunctionKey: index = 6; break;
649 case NSF8FunctionKey: index = 7; break;
650 case NSF9FunctionKey: index = 8; break;
651 case NSF10FunctionKey: index = 9; break;
652 case NSF11FunctionKey: index = 10; break;
653 case NSF12FunctionKey: index = 11; break;
654 }
655 if (m & NSShiftKeyMask) index += 12;
656 if (m & NSControlKeyMask) index += 24;
657 output[end++] = '\033';
658 output[end++] = '[';
659 output[end++] = codes[index];
660 goto done;
661 }
662 if (cfg.funky_type == FUNKY_SCO && /* SCO small keypad */
663 code >= 1 && code <= 6) {
664 char codes[] = "HL.FIG";
665 if (code == 3) {
666 output[1] = '\x7F';
667 end = 2;
668 } else {
669 output[end++] = '\033';
670 output[end++] = '[';
671 output[end++] = codes[code-1];
672 }
673 goto done;
674 }
675 if ((term->vt52_mode || cfg.funky_type == FUNKY_VT100P) &&
676 code >= 11 && code <= 24) {
677 int offt = 0;
678 if (code > 15)
679 offt++;
680 if (code > 21)
681 offt++;
682 if (term->vt52_mode) {
683 output[end++] = '\033';
684 output[end++] = code + 'P' - 11 - offt;
685 } else {
686 output[end++] = '\033';
687 output[end++] = 'O';
688 output[end++] = code + 'P' - 11 - offt;
689 }
690 goto done;
691 }
692 if (cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) {
693 output[end++] = '\033';
694 output[end++] = '[';
695 output[end++] = '[';
696 output[end++] = code + 'A' - 11;
697 goto done;
698 }
699 if (cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) {
700 if (term->vt52_mode) {
701 output[end++] = '\033';
702 output[end++] = code + 'P' - 11;
703 } else {
704 output[end++] = '\033';
705 output[end++] = 'O';
706 output[end++] = code + 'P' - 11;
707 }
708 goto done;
709 }
710 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
711 if (code == 1) {
712 output[end++] = '\033';
713 output[end++] = '[';
714 output[end++] = 'H';
715 } else {
716 output[end++] = '\033';
717 output[end++] = 'O';
718 output[end++] = 'w';
719 }
720 goto done;
721 }
722 if (code) {
723 char buf[20];
724 sprintf(buf, "\x1B[%d~", code);
725 for (i = 0; buf[i]; i++)
726 output[end++] = buf[i];
727 goto done;
728 }
729 }
730
731 /*
732 * Cursor keys. (This includes the numberpad cursor keys,
733 * if we haven't already done them due to app keypad mode.)
734 */
735 {
736 int xkey = 0;
737 switch (cm) {
738 case NSUpArrowFunctionKey: xkey = 'A'; break;
739 case NSDownArrowFunctionKey: xkey = 'B'; break;
740 case NSRightArrowFunctionKey: xkey = 'C'; break;
741 case NSLeftArrowFunctionKey: xkey = 'D'; break;
742 }
743 if (xkey) {
744 /*
745 * The arrow keys normally do ESC [ A and so on. In
746 * app cursor keys mode they do ESC O A instead.
747 * Ctrl toggles the two modes.
748 */
749 if (term->vt52_mode) {
750 output[end++] = '\033';
751 output[end++] = xkey;
752 } else if (!term->app_cursor_keys ^ !(m & NSControlKeyMask)) {
753 output[end++] = '\033';
754 output[end++] = 'O';
755 output[end++] = xkey;
756 } else {
757 output[end++] = '\033';
758 output[end++] = '[';
759 output[end++] = xkey;
760 }
761 goto done;
762 }
763 }
764
765 done:
766
767 /*
768 * Failing everything else, send the exact Unicode we got from
769 * OS X.
770 */
771 if (end == start) {
772 if (n > lenof(output)-start)
773 n = lenof(output)-start; /* _shouldn't_ happen! */
774 for (i = 0; i < n; i++) {
775 output[i+start] = [s characterAtIndex:i];
776 }
777 end = n+start;
778 }
779
780 if (use_coutput) {
781 assert(special);
782 assert(end < lenof(coutput));
783 coutput[end] = '\0';
784 ldisc_send(ldisc, coutput+start, -2, TRUE);
785 } else {
786 luni_send(ldisc, output+start, end-start, TRUE);
787 }
788 }
789
790 - (int)fromBackend:(const char *)data len:(int)len isStderr:(int)is_stderr
791 {
792 return term_data(term, is_stderr, data, len);
793 }
794
795 - (void)startAlert:(NSAlert *)alert
796 withCallback:(void (*)(void *, int))callback andCtx:(void *)ctx
797 {
798 if (alert_ctx || alert_qhead) {
799 /*
800 * Queue this alert to be shown later.
801 */
802 struct alert_queue *qitem = snew(struct alert_queue);
803 qitem->next = NULL;
804 qitem->alert = alert;
805 qitem->callback = callback;
806 qitem->ctx = ctx;
807 if (alert_qtail)
808 alert_qtail->next = qitem;
809 else
810 alert_qhead = qitem;
811 alert_qtail = qitem;
812 } else {
813 alert_callback = callback;
814 alert_ctx = ctx; /* NB this is assumed to need freeing! */
815 [alert beginSheetModalForWindow:self modalDelegate:self
816 didEndSelector:@selector(alertSheetDidEnd:returnCode:contextInfo:)
817 contextInfo:NULL];
818 }
819 }
820
821 - (void)alertSheetDidEnd:(NSAlert *)alert returnCode:(int)returnCode
822 contextInfo:(void *)contextInfo
823 {
824 [self performSelectorOnMainThread:
825 @selector(alertSheetDidFinishEnding:)
826 withObject:[NSNumber numberWithInt:returnCode]
827 waitUntilDone:NO];
828 }
829
830 - (void)alertSheetDidFinishEnding:(id)object
831 {
832 int returnCode = [object intValue];
833
834 alert_callback(alert_ctx, returnCode); /* transfers ownership of ctx */
835
836 /*
837 * If there's an alert in our queue (either already or because
838 * the callback just queued it), start it.
839 */
840 if (alert_qhead) {
841 struct alert_queue *qnext;
842
843 alert_callback = alert_qhead->callback;
844 alert_ctx = alert_qhead->ctx;
845 [alert_qhead->alert beginSheetModalForWindow:self modalDelegate:self
846 didEndSelector:@selector(alertSheetDidEnd:returnCode:contextInfo:)
847 contextInfo:NULL];
848
849 qnext = alert_qhead->next;
850 sfree(alert_qhead);
851 alert_qhead = qnext;
852 if (!qnext)
853 alert_qtail = NULL;
854 } else {
855 alert_ctx = NULL;
856 }
857 }
858
859 - (void)notifyRemoteExit
860 {
861 int exitcode;
862
863 if (!exited && (exitcode = back->exitcode(backhandle)) >= 0)
864 [self endSession:(exitcode == 0)];
865 }
866
867 - (void)endSession:(int)clean
868 {
869 exited = TRUE;
870 if (ldisc) {
871 ldisc_free(ldisc);
872 ldisc = NULL;
873 }
874 if (back) {
875 back->free(backhandle);
876 backhandle = NULL;
877 back = NULL;
878 //FIXME: update specials menu;
879 }
880 if (cfg.close_on_exit == FORCE_ON ||
881 (cfg.close_on_exit == AUTO && clean))
882 [self close];
883 // FIXME: else show restart menu item
884 }
885
886 @end
887
888 int from_backend(void *frontend, int is_stderr, const char *data, int len)
889 {
890 SessionWindow *win = (SessionWindow *)frontend;
891 return [win fromBackend:data len:len isStderr:is_stderr];
892 }
893
894 void frontend_keypress(void *handle)
895 {
896 /* FIXME */
897 }
898
899 void notify_remote_exit(void *frontend)
900 {
901 SessionWindow *win = (SessionWindow *)frontend;
902
903 [win notifyRemoteExit];
904 }
905
906 void ldisc_update(void *frontend, int echo, int edit)
907 {
908 //SessionWindow *win = (SessionWindow *)frontend;
909 /*
910 * In a GUI front end, this need do nothing.
911 */
912 }
913
914 void update_specials_menu(void *frontend)
915 {
916 //SessionWindow *win = (SessionWindow *)frontend;
917 /* FIXME */
918 }
919
920 /*
921 * This is still called when mode==BELL_VISUAL, even though the
922 * visual bell is handled entirely within terminal.c, because we
923 * may want to perform additional actions on any kind of bell (for
924 * example, taskbar flashing in Windows).
925 */
926 void beep(void *frontend, int mode)
927 {
928 //SessionWindow *win = (SessionWindow *)frontend;
929 if (mode != BELL_VISUAL)
930 NSBeep();
931 }
932
933 int char_width(Context ctx, int uc)
934 {
935 /*
936 * Under X, any fixed-width font really _is_ fixed-width.
937 * Double-width characters will be dealt with using a separate
938 * font. For the moment we can simply return 1.
939 */
940 return 1;
941 }
942
943 void palette_set(void *frontend, int n, int r, int g, int b)
944 {
945 SessionWindow *win = (SessionWindow *)frontend;
946
947 if (n >= 16)
948 n += 256 - 16;
949 if (n > NALLCOLOURS)
950 return;
951 [win setColour:n r:r/255.0 g:g/255.0 b:b/255.0];
952
953 /*
954 * FIXME: do we need an OS X equivalent of set_window_background?
955 */
956 }
957
958 void palette_reset(void *frontend)
959 {
960 SessionWindow *win = (SessionWindow *)frontend;
961 Config *cfg = [win cfg];
962
963 /* This maps colour indices in cfg to those used in colours[]. */
964 static const int ww[] = {
965 256, 257, 258, 259, 260, 261,
966 0, 8, 1, 9, 2, 10, 3, 11,
967 4, 12, 5, 13, 6, 14, 7, 15
968 };
969
970 int i;
971
972 for (i = 0; i < NCFGCOLOURS; i++) {
973 [win setColour:ww[i] r:cfg->colours[i][0]/255.0
974 g:cfg->colours[i][1]/255.0 b:cfg->colours[i][2]/255.0];
975 }
976
977 for (i = 0; i < NEXTCOLOURS; i++) {
978 if (i < 216) {
979 int r = i / 36, g = (i / 6) % 6, b = i % 6;
980 [win setColour:i+16 r:r/5.0 g:g/5.0 b:b/5.0];
981 } else {
982 int shade = i - 216;
983 float fshade = (shade + 1) / (float)(NEXTCOLOURS - 216 + 1);
984 [win setColour:i+16 r:fshade g:fshade b:fshade];
985 }
986 }
987
988 /*
989 * FIXME: do we need an OS X equivalent of set_window_background?
990 */
991 }
992
993 Context get_ctx(void *frontend)
994 {
995 SessionWindow *win = (SessionWindow *)frontend;
996
997 /*
998 * Lock the drawing focus on the image inside the TerminalView.
999 */
1000 [win drawStartFinish:YES];
1001
1002 [[NSGraphicsContext currentContext] setShouldAntialias:YES];
1003
1004 /*
1005 * Cocoa drawing functions don't take a graphics context: that
1006 * parameter is implicit. Therefore, we'll use the frontend
1007 * handle itself as the context, on the grounds that it's as
1008 * good a thing to use as any.
1009 */
1010 return frontend;
1011 }
1012
1013 void free_ctx(Context ctx)
1014 {
1015 SessionWindow *win = (SessionWindow *)ctx;
1016
1017 [win drawStartFinish:NO];
1018 }
1019
1020 void do_text(Context ctx, int x, int y, wchar_t *text, int len,
1021 unsigned long attr, int lattr)
1022 {
1023 SessionWindow *win = (SessionWindow *)ctx;
1024
1025 [win doText:text len:len x:x y:y attr:attr lattr:lattr];
1026 }
1027
1028 void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
1029 unsigned long attr, int lattr)
1030 {
1031 SessionWindow *win = (SessionWindow *)ctx;
1032 Config *cfg = [win cfg];
1033 int active, passive;
1034
1035 if (attr & TATTR_PASCURS) {
1036 attr &= ~TATTR_PASCURS;
1037 passive = 1;
1038 } else
1039 passive = 0;
1040 if ((attr & TATTR_ACTCURS) && cfg->cursor_type != 0) {
1041 attr &= ~TATTR_ACTCURS;
1042 active = 1;
1043 } else
1044 active = 0;
1045
1046 [win doText:text len:len x:x y:y attr:attr lattr:lattr];
1047
1048 /*
1049 * FIXME: now draw the various cursor types (both passive and
1050 * active underlines and vertical lines, plus passive blocks).
1051 */
1052 }
1053
1054 /*
1055 * Minimise or restore the window in response to a server-side
1056 * request.
1057 */
1058 void set_iconic(void *frontend, int iconic)
1059 {
1060 //SessionWindow *win = (SessionWindow *)frontend;
1061 /* FIXME */
1062 }
1063
1064 /*
1065 * Move the window in response to a server-side request.
1066 */
1067 void move_window(void *frontend, int x, int y)
1068 {
1069 //SessionWindow *win = (SessionWindow *)frontend;
1070 /* FIXME */
1071 }
1072
1073 /*
1074 * Move the window to the top or bottom of the z-order in response
1075 * to a server-side request.
1076 */
1077 void set_zorder(void *frontend, int top)
1078 {
1079 //SessionWindow *win = (SessionWindow *)frontend;
1080 /* FIXME */
1081 }
1082
1083 /*
1084 * Refresh the window in response to a server-side request.
1085 */
1086 void refresh_window(void *frontend)
1087 {
1088 //SessionWindow *win = (SessionWindow *)frontend;
1089 /* FIXME */
1090 }
1091
1092 /*
1093 * Maximise or restore the window in response to a server-side
1094 * request.
1095 */
1096 void set_zoomed(void *frontend, int zoomed)
1097 {
1098 //SessionWindow *win = (SessionWindow *)frontend;
1099 /* FIXME */
1100 }
1101
1102 /*
1103 * Report whether the window is iconic, for terminal reports.
1104 */
1105 int is_iconic(void *frontend)
1106 {
1107 //SessionWindow *win = (SessionWindow *)frontend;
1108 return NO; /* FIXME */
1109 }
1110
1111 /*
1112 * Report the window's position, for terminal reports.
1113 */
1114 void get_window_pos(void *frontend, int *x, int *y)
1115 {
1116 //SessionWindow *win = (SessionWindow *)frontend;
1117 /* FIXME */
1118 }
1119
1120 /*
1121 * Report the window's pixel size, for terminal reports.
1122 */
1123 void get_window_pixels(void *frontend, int *x, int *y)
1124 {
1125 //SessionWindow *win = (SessionWindow *)frontend;
1126 /* FIXME */
1127 }
1128
1129 /*
1130 * Return the window or icon title.
1131 */
1132 char *get_window_title(void *frontend, int icon)
1133 {
1134 //SessionWindow *win = (SessionWindow *)frontend;
1135 return NULL; /* FIXME */
1136 }
1137
1138 void set_title(void *frontend, char *title)
1139 {
1140 //SessionWindow *win = (SessionWindow *)frontend;
1141 /* FIXME */
1142 }
1143
1144 void set_icon(void *frontend, char *title)
1145 {
1146 //SessionWindow *win = (SessionWindow *)frontend;
1147 /* FIXME */
1148 }
1149
1150 void set_sbar(void *frontend, int total, int start, int page)
1151 {
1152 //SessionWindow *win = (SessionWindow *)frontend;
1153 /* FIXME */
1154 }
1155
1156 void get_clip(void *frontend, wchar_t ** p, int *len)
1157 {
1158 //SessionWindow *win = (SessionWindow *)frontend;
1159 /* FIXME */
1160 }
1161
1162 void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
1163 {
1164 //SessionWindow *win = (SessionWindow *)frontend;
1165 /* FIXME */
1166 }
1167
1168 void request_paste(void *frontend)
1169 {
1170 //SessionWindow *win = (SessionWindow *)frontend;
1171 /* FIXME */
1172 }
1173
1174 void set_raw_mouse_mode(void *frontend, int activate)
1175 {
1176 //SessionWindow *win = (SessionWindow *)frontend;
1177 /* FIXME */
1178 }
1179
1180 void request_resize(void *frontend, int w, int h)
1181 {
1182 //SessionWindow *win = (SessionWindow *)frontend;
1183 /* FIXME */
1184 }
1185
1186 void sys_cursor(void *frontend, int x, int y)
1187 {
1188 //SessionWindow *win = (SessionWindow *)frontend;
1189 /*
1190 * This is probably meaningless under OS X. FIXME: find out for
1191 * sure.
1192 */
1193 }
1194
1195 void logevent(void *frontend, const char *string)
1196 {
1197 //SessionWindow *win = (SessionWindow *)frontend;
1198 /* FIXME */
1199 printf("logevent: %s\n", string);
1200 }
1201
1202 int font_dimension(void *frontend, int which)/* 0 for width, 1 for height */
1203 {
1204 //SessionWindow *win = (SessionWindow *)frontend;
1205 return 1; /* FIXME */
1206 }
1207
1208 void set_busy_status(void *frontend, int status)
1209 {
1210 /*
1211 * We need do nothing here: the OS X `application is busy'
1212 * beachball pointer appears _automatically_ when the
1213 * application isn't responding to GUI messages.
1214 */
1215 }