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