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