52c33b867675865ab7766721e33b698e0b555188
[sgt/putty] / mac / macterm.c
1 /* $Id: macterm.c,v 1.68 2003/02/04 23:39:26 ben Exp $ */
2 /*
3 * Copyright (c) 1999 Simon Tatham
4 * Copyright (c) 1999, 2002 Ben Harris
5 * All rights reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person
8 * obtaining a copy of this software and associated documentation
9 * files (the "Software"), to deal in the Software without
10 * restriction, including without limitation the rights to use,
11 * copy, modify, merge, publish, distribute, sublicense, and/or
12 * sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following
14 * conditions:
15 *
16 * The above copyright notice and this permission notice shall be
17 * included in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
23 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
24 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
27 */
28
29 /*
30 * macterm.c -- Macintosh terminal front-end
31 */
32
33 #include <MacTypes.h>
34 #include <Controls.h>
35 #include <ControlDefinitions.h>
36 #include <FixMath.h>
37 #include <Fonts.h>
38 #include <Gestalt.h>
39 #include <LowMem.h>
40 #include <MacMemory.h>
41 #include <MacWindows.h>
42 #include <MixedMode.h>
43 #include <Palettes.h>
44 #include <Quickdraw.h>
45 #include <QuickdrawText.h>
46 #include <Resources.h>
47 #include <Scrap.h>
48 #include <Script.h>
49 #include <Sound.h>
50 #include <TextCommon.h>
51 #include <ToolUtils.h>
52 #include <UnicodeConverter.h>
53
54 #include <assert.h>
55 #include <limits.h>
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include <string.h>
59
60 #include "macresid.h"
61 #include "putty.h"
62 #include "charset.h"
63 #include "mac.h"
64 #include "terminal.h"
65
66 #define NCOLOURS (lenof(((Config *)0)->colours))
67
68 #define DEFAULT_FG 16
69 #define DEFAULT_FG_BOLD 17
70 #define DEFAULT_BG 18
71 #define DEFAULT_BG_BOLD 19
72 #define CURSOR_FG 20
73 #define CURSOR_BG 21
74
75 #define PTOCC(x) ((x) < 0 ? -(-(x - s->font_width - 1) / s->font_width) : \
76 (x) / s->font_width)
77 #define PTOCR(y) ((y) < 0 ? -(-(y - s->font_height - 1) / s->font_height) : \
78 (y) / s->font_height)
79
80 static void mac_initfont(Session *);
81 static pascal OSStatus uni_to_font_fallback(UniChar *, ByteCount, ByteCount *,
82 TextPtr, ByteCount, ByteCount *,
83 LogicalAddress,
84 ConstUnicodeMappingPtr);
85 static void mac_initpalette(Session *);
86 static void mac_adjustwinbg(Session *);
87 static void mac_adjustsize(Session *, int, int);
88 static void mac_drawgrowicon(Session *s);
89 static pascal void mac_growtermdraghook(void);
90 static pascal void mac_scrolltracker(ControlHandle, short);
91 static pascal void do_text_for_device(short, short, GDHandle, long);
92 static void text_click(Session *, EventRecord *);
93
94 void pre_paint(Session *s);
95 void post_paint(Session *s);
96
97 void mac_startsession(Session *s)
98 {
99 char *errmsg;
100 int i;
101 WinInfo *wi;
102
103 init_ucs(s);
104
105 /*
106 * Select protocol. This is farmed out into a table in a
107 * separate file to enable an ssh-free variant.
108 */
109 s->back = NULL;
110 for (i = 0; backends[i].backend != NULL; i++)
111 if (backends[i].protocol == s->cfg.protocol) {
112 s->back = backends[i].backend;
113 break;
114 }
115 if (s->back == NULL)
116 fatalbox("Unsupported protocol number found");
117
118 /* XXX: Own storage management? */
119 if (HAVE_COLOR_QD())
120 s->window = GetNewCWindow(wTerminal, NULL, (WindowPtr)-1);
121 else
122 s->window = GetNewWindow(wTerminal, NULL, (WindowPtr)-1);
123 wi = smalloc(sizeof(*wi));
124 wi->s = s;
125 wi->wtype = wTerminal;
126 SetWRefCon(s->window, (long)wi);
127 s->scrollbar = GetNewControl(cVScroll, s->window);
128 s->term = term_init(&s->cfg, &s->ucsdata, s);
129
130 mac_initfont(s);
131 mac_initpalette(s);
132 if (HAVE_COLOR_QD()) {
133 /* Set to FALSE to not get palette updates in the background. */
134 SetPalette(s->window, s->palette, TRUE);
135 ActivatePalette(s->window);
136 }
137
138 s->logctx = log_init(s, &s->cfg);
139 term_provide_logctx(s->term, s->logctx);
140
141 errmsg = s->back->init(s->term, &s->backhandle, &s->cfg, s->cfg.host,
142 s->cfg.port, &s->realhost, s->cfg.tcp_nodelay);
143 if (errmsg != NULL)
144 fatalbox("%s", errmsg);
145 s->back->provide_logctx(s->backhandle, s->logctx);
146 set_title(s, s->realhost);
147
148 term_provide_resize_fn(s->term, s->back->size, s->backhandle);
149
150 mac_adjustsize(s, s->cfg.height, s->cfg.width);
151 term_size(s->term, s->cfg.height, s->cfg.width, s->cfg.savelines);
152
153 s->ldisc = ldisc_create(&s->cfg, s->term, s->back, s->backhandle, s);
154 ldisc_send(s->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */
155
156 ShowWindow(s->window);
157 s->next = sesslist;
158 s->prev = &sesslist;
159 if (s->next != NULL)
160 s->next->prev = &s->next;
161 sesslist = s;
162 }
163
164 /*
165 * Try to work out a horizontal scaling factor for the current font
166 * that will give a chracter width of wantwidth. Return it in numer
167 * and denom (suitable for passing to StdText()).
168 */
169 static void mac_workoutfontscale(Session *s, int wantwidth,
170 Point *numerp, Point *denomp)
171 {
172 Point numer, denom, tmpnumer, tmpdenom;
173 int gotwidth, i;
174 const char text = 'W';
175 FontInfo fi;
176 #if TARGET_API_MAC_CARBON
177 CQDProcsPtr gp = GetPortGrafProcs(GetWindowPort(s->window));;
178 #else
179 QDProcsPtr gp = s->window->grafProcs;;
180 #endif
181
182 numer.v = denom.v = 1; /* always */
183 numer.h = denom.h = 1;
184 for (i = 0; i < 3; i++) {
185 tmpnumer = numer;
186 tmpdenom = denom;
187 if (gp != NULL)
188 gotwidth = InvokeQDTxMeasUPP(1, &text, &tmpnumer, &tmpdenom, &fi,
189 gp->txMeasProc);
190 else
191 gotwidth = StdTxMeas(1, &text, &tmpnumer, &tmpdenom, &fi);
192 /* The result of StdTxMeas must be scaled by the factors it returns. */
193 gotwidth = FixRound(FixMul(gotwidth << 16,
194 FixRatio(tmpnumer.h, tmpdenom.h)));
195 if (gotwidth == wantwidth)
196 break;
197 numer.h *= wantwidth;
198 denom.h *= gotwidth;
199 }
200 *numerp = numer;
201 *denomp = denom;
202 }
203
204 static UnicodeToTextFallbackUPP uni_to_font_fallback_upp;
205
206 static void mac_initfont(Session *s) {
207 FontInfo fi;
208 TextEncoding enc;
209 OptionBits fbflags;
210
211 SetPort((GrafPtr)GetWindowPort(s->window));
212 GetFNum(s->cfg.font.name, &s->fontnum);
213 TextFont(s->fontnum);
214 TextFace(s->cfg.font.face);
215 TextSize(s->cfg.font.size);
216 GetFontInfo(&fi);
217 s->font_width = CharWidth('W'); /* Well, it's what NCSA uses. */
218 s->font_ascent = fi.ascent;
219 s->font_leading = fi.leading;
220 s->font_height = s->font_ascent + fi.descent + s->font_leading;
221 mac_workoutfontscale(s, s->font_width,
222 &s->font_stdnumer, &s->font_stddenom);
223 mac_workoutfontscale(s, s->font_width * 2,
224 &s->font_widenumer, &s->font_widedenom);
225 TextSize(s->cfg.font.size * 2);
226 mac_workoutfontscale(s, s->font_width * 2,
227 &s->font_bignumer, &s->font_bigdenom);
228 TextSize(s->cfg.font.size);
229 if (!s->cfg.bold_colour) {
230 TextFace(bold);
231 s->font_boldadjust = s->font_width - CharWidth('W');
232 } else
233 s->font_boldadjust = 0;
234
235 if (s->uni_to_font != NULL)
236 DisposeUnicodeToTextInfo(&s->uni_to_font);
237 if (mac_gestalts.encvvers != 0 &&
238 UpgradeScriptInfoToTextEncoding(kTextScriptDontCare,
239 kTextLanguageDontCare,
240 kTextRegionDontCare, s->cfg.font.name,
241 &enc) == noErr &&
242 CreateUnicodeToTextInfoByEncoding(enc, &s->uni_to_font) == noErr) {
243 if (uni_to_font_fallback_upp == NULL)
244 uni_to_font_fallback_upp =
245 NewUnicodeToTextFallbackUPP(&uni_to_font_fallback);
246 fbflags = kUnicodeFallbackCustomOnly;
247 if (mac_gestalts.uncvattr & kTECAddFallbackInterruptMask)
248 fbflags |= kUnicodeFallbackInterruptSafeMask;
249 if (SetFallbackUnicodeToText(s->uni_to_font,
250 uni_to_font_fallback_upp, fbflags, NULL) != noErr) {
251 DisposeUnicodeToTextInfo(&s->uni_to_font);
252 goto no_encv;
253 }
254 } else {
255 char cfontname[256];
256
257 no_encv:
258 s->uni_to_font = NULL;
259 p2cstrcpy(cfontname, s->cfg.font.name);
260 s->font_charset =
261 charset_from_macenc(FontToScript(s->fontnum),
262 GetScriptManagerVariable(smRegionCode),
263 mac_gestalts.sysvers, cfontname);
264 }
265
266 mac_adjustsize(s, s->term->rows, s->term->cols);
267 }
268
269 static pascal OSStatus uni_to_font_fallback(UniChar *ucp,
270 ByteCount ilen, ByteCount *iusedp, TextPtr obuf, ByteCount olen,
271 ByteCount *ousedp, LogicalAddress cookie, ConstUnicodeMappingPtr mapping)
272 {
273
274 if (olen < 1)
275 return kTECOutputBufferFullStatus;
276 /*
277 * What I'd _like_ to do here is to somehow generate the
278 * missing-character glyph that every font is required to have.
279 * Unfortunately (and somewhat surprisingly), I can't find any way
280 * to actually ask for it explicitly. Bah.
281 */
282 *obuf = '.';
283 *iusedp = ilen;
284 *ousedp = 1;
285 return noErr;
286 }
287
288 /*
289 * Called every time round the event loop.
290 */
291 void mac_pollterm(void)
292 {
293 Session *s;
294
295 for (s = sesslist; s != NULL; s = s->next) {
296 term_out(s->term);
297 term_update(s->term);
298 }
299 }
300
301 /*
302 * To be called whenever the window size changes.
303 * rows and cols should be desired values.
304 * It's assumed the terminal emulator will be informed, and will set rows
305 * and cols for us.
306 */
307 static void mac_adjustsize(Session *s, int newrows, int newcols) {
308 int winwidth, winheight;
309
310 winwidth = newcols * s->font_width + 15;
311 winheight = newrows * s->font_height;
312 SizeWindow(s->window, winwidth, winheight, true);
313 HideControl(s->scrollbar);
314 MoveControl(s->scrollbar, winwidth - 15, -1);
315 SizeControl(s->scrollbar, 16, winheight - 13);
316 ShowControl(s->scrollbar);
317 mac_drawgrowicon(s);
318 }
319
320 static void mac_initpalette(Session *s) {
321
322 if (!HAVE_COLOR_QD())
323 return;
324 /*
325 * Most colours should be inhibited on 2bpp displays.
326 * Palette manager documentation suggests inhibiting all tolerant colours
327 * on greyscale displays.
328 */
329 #define PM_NORMAL ( pmTolerant | pmInhibitC2 | \
330 pmInhibitG2 | pmInhibitG4 | pmInhibitG8 )
331 #define PM_TOLERANCE 0x2000
332 s->palette = NewPalette(22, NULL, PM_NORMAL, PM_TOLERANCE);
333 if (s->palette == NULL)
334 fatalbox("Unable to create palette");
335 /* In 2bpp, these are the colours we want most. */
336 SetEntryUsage(s->palette, DEFAULT_BG,
337 PM_NORMAL &~ pmInhibitC2, PM_TOLERANCE);
338 SetEntryUsage(s->palette, DEFAULT_FG,
339 PM_NORMAL &~ pmInhibitC2, PM_TOLERANCE);
340 SetEntryUsage(s->palette, DEFAULT_FG_BOLD,
341 PM_NORMAL &~ pmInhibitC2, PM_TOLERANCE);
342 SetEntryUsage(s->palette, CURSOR_BG,
343 PM_NORMAL &~ pmInhibitC2, PM_TOLERANCE);
344 palette_reset(s);
345 }
346
347 /*
348 * Set the background colour of the window correctly. Should be
349 * called whenever the default background changes.
350 */
351 static void mac_adjustwinbg(Session *s) {
352
353 if (!HAVE_COLOR_QD())
354 return;
355 #if !TARGET_CPU_68K
356 if (mac_gestalts.windattr & gestaltWindowMgrPresent)
357 SetWindowContentColor(s->window,
358 &(*s->palette)->pmInfo[DEFAULT_BG].ciRGB);
359 else
360 #endif
361 {
362 #if !TARGET_API_MAC_CARBON
363 if (s->wctab == NULL)
364 s->wctab = (WCTabHandle)NewHandle(sizeof(**s->wctab));
365 if (s->wctab == NULL)
366 return; /* do without */
367 (*s->wctab)->wCSeed = 0;
368 (*s->wctab)->wCReserved = 0;
369 (*s->wctab)->ctSize = 0;
370 (*s->wctab)->ctTable[0].value = wContentColor;
371 (*s->wctab)->ctTable[0].rgb = (*s->palette)->pmInfo[DEFAULT_BG].ciRGB;
372 SetWinColor(s->window, s->wctab);
373 #endif
374 }
375 }
376
377 /*
378 * Set the cursor shape correctly
379 */
380 void mac_adjusttermcursor(WindowPtr window, Point mouse, RgnHandle cursrgn) {
381 Session *s;
382 ControlHandle control;
383 short part;
384 int x, y;
385 #if TARGET_API_MAC_CARBON
386 Cursor arrow;
387 Rect rect;
388 RgnHandle visrgn;
389 #endif
390
391 SetPort((GrafPtr)GetWindowPort(window));
392 s = mac_windowsession(window);
393 GlobalToLocal(&mouse);
394 part = FindControl(mouse, window, &control);
395 if (control == s->scrollbar) {
396 #if TARGET_API_MAC_CARBON
397 SetCursor(GetQDGlobalsArrow(&arrow));
398 RectRgn(cursrgn, GetControlBounds(s->scrollbar, &rect));
399 #else
400 SetCursor(&qd.arrow);
401 RectRgn(cursrgn, &(*s->scrollbar)->contrlRect);
402 #endif
403 } else {
404 x = mouse.h / s->font_width;
405 y = mouse.v / s->font_height;
406 if (s->raw_mouse) {
407 #if TARGET_API_MAC_CARBON
408 SetCursor(GetQDGlobalsArrow(&arrow));
409 #else
410 SetCursor(&qd.arrow);
411 #endif
412 } else
413 SetCursor(*GetCursor(iBeamCursor));
414 /* Ask for shape changes if we leave this character cell. */
415 SetRectRgn(cursrgn, x * s->font_width, y * s->font_height,
416 (x + 1) * s->font_width, (y + 1) * s->font_height);
417 }
418 #if TARGET_API_MAC_CARBON
419 visrgn = NewRgn();
420 GetPortVisibleRegion(GetWindowPort(window), visrgn);
421 SectRgn(cursrgn, visrgn, cursrgn);
422 DisposeRgn(visrgn);
423 #else
424 SectRgn(cursrgn, window->visRgn, cursrgn);
425 #endif
426 }
427
428 /*
429 * Enable/disable menu items based on the active terminal window.
430 */
431 #if TARGET_API_MAC_CARBON
432 #define DisableItem DisableMenuItem
433 #define EnableItem EnableMenuItem
434 #endif
435 void mac_adjusttermmenus(WindowPtr window) {
436 Session *s;
437 MenuHandle menu;
438 #if !TARGET_API_MAC_CARBON
439 long offset;
440 #endif
441
442 s = mac_windowsession(window);
443 menu = GetMenuHandle(mFile);
444 DisableItem(menu, iSave); /* XXX enable if modified */
445 EnableItem(menu, iSaveAs);
446 EnableItem(menu, iDuplicate);
447 menu = GetMenuHandle(mEdit);
448 EnableItem(menu, 0);
449 DisableItem(menu, iUndo);
450 DisableItem(menu, iCut);
451 if (1/*s->term->selstate == SELECTED*/)
452 EnableItem(menu, iCopy);
453 else
454 DisableItem(menu, iCopy);
455 #if TARGET_API_MAC_CARBON
456 if (1)
457 #else
458 if (GetScrap(NULL, kScrapFlavorTypeText, &offset) == noTypeErr)
459 #endif
460 DisableItem(menu, iPaste);
461 else
462 EnableItem(menu, iPaste);
463 DisableItem(menu, iClear);
464 EnableItem(menu, iSelectAll);
465 }
466
467 void mac_menuterm(WindowPtr window, short menu, short item) {
468 Session *s;
469
470 s = mac_windowsession(window);
471 switch (menu) {
472 case mEdit:
473 switch (item) {
474 case iCopy:
475 /* term_copy(s); */
476 break;
477 case iPaste:
478 term_do_paste(s->term);
479 break;
480 }
481 }
482 }
483
484 void mac_clickterm(WindowPtr window, EventRecord *event) {
485 Session *s;
486 Point mouse;
487 ControlHandle control;
488 int part;
489 static ControlActionUPP mac_scrolltracker_upp = NULL;
490
491 s = mac_windowsession(window);
492 SetPort((GrafPtr)GetWindowPort(window));
493 mouse = event->where;
494 GlobalToLocal(&mouse);
495 part = FindControl(mouse, window, &control);
496 if (control == s->scrollbar) {
497 switch (part) {
498 case kControlIndicatorPart:
499 if (TrackControl(control, mouse, NULL) == kControlIndicatorPart)
500 term_scroll(s->term, +1, GetControlValue(control));
501 break;
502 case kControlUpButtonPart:
503 case kControlDownButtonPart:
504 case kControlPageUpPart:
505 case kControlPageDownPart:
506 if (mac_scrolltracker_upp == NULL)
507 mac_scrolltracker_upp =
508 NewControlActionUPP(&mac_scrolltracker);
509 TrackControl(control, mouse, mac_scrolltracker_upp);
510 break;
511 }
512 } else {
513 text_click(s, event);
514 }
515 }
516
517 static void text_click(Session *s, EventRecord *event) {
518 Point localwhere;
519 int row, col;
520 static UInt32 lastwhen = 0;
521 static Session *lastsess = NULL;
522 static int lastrow = -1, lastcol = -1;
523 static Mouse_Action lastact = MA_NOTHING;
524
525 SetPort((GrafPtr)GetWindowPort(s->window));
526 localwhere = event->where;
527 GlobalToLocal(&localwhere);
528
529 col = PTOCC(localwhere.h);
530 row = PTOCR(localwhere.v);
531 if (event->when - lastwhen < GetDblTime() &&
532 row == lastrow && col == lastcol && s == lastsess)
533 lastact = (lastact == MA_CLICK ? MA_2CLK :
534 lastact == MA_2CLK ? MA_3CLK :
535 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
536 else
537 lastact = MA_CLICK;
538 term_mouse(s->term, MBT_LEFT,
539 event->modifiers & shiftKey ? MBT_EXTEND : MBT_SELECT,
540 lastact, col, row, event->modifiers & shiftKey,
541 event->modifiers & controlKey, event->modifiers & optionKey);
542 lastsess = s;
543 lastrow = row;
544 lastcol = col;
545 while (StillDown()) {
546 GetMouse(&localwhere);
547 col = PTOCC(localwhere.h);
548 row = PTOCR(localwhere.v);
549 term_mouse(s->term, MBT_LEFT,
550 event->modifiers & shiftKey ? MBT_EXTEND : MBT_SELECT,
551 MA_DRAG, col, row, event->modifiers & shiftKey,
552 event->modifiers & controlKey,
553 event->modifiers & optionKey);
554 if (row > s->term->rows - 1)
555 term_scroll(s->term, 0, row - (s->term->rows - 1));
556 else if (row < 0)
557 term_scroll(s->term, 0, row);
558 }
559 term_mouse(s->term, MBT_LEFT,
560 event->modifiers & shiftKey ? MBT_EXTEND : MBT_SELECT,
561 MA_RELEASE, col, row, event->modifiers & shiftKey,
562 event->modifiers & controlKey, event->modifiers & optionKey);
563 lastwhen = TickCount();
564 }
565
566 void write_clip(void *cookie, wchar_t *data, int len, int must_deselect)
567 {
568 #if !TARGET_API_MAC_CARBON
569 Session *s = cookie;
570 char *mactextbuf;
571 ByteCount iread, olen;
572 wchar_t *unitextptr;
573 StScrpRec *stsc;
574 size_t stsz;
575 OSErr err;
576 int i;
577
578 /*
579 * See "Programming with the Text Encoding Conversion Manager"
580 * Appendix E for Unicode scrap conventions.
581 *
582 * XXX Maybe PICT scrap too.
583 */
584 if (ZeroScrap() != noErr)
585 return;
586 PutScrap(len * sizeof(*data), kScrapFlavorTypeUnicode, data);
587
588 /* Replace LINE SEPARATORs with CR for TEXT output. */
589 for (i = 0; i < len; i++)
590 if (data[i] == 0x2028)
591 data[i] = 0x000d;
592
593 mactextbuf = smalloc(len); /* XXX DBCS */
594 if (s->uni_to_font != NULL) {
595 err = ConvertFromUnicodeToText(s->uni_to_font, len * sizeof(UniChar),
596 (UniChar *)data,
597 kUnicodeUseFallbacksMask,
598 0, NULL, NULL, NULL,
599 len, &iread, &olen, mactextbuf);
600 if (err != noErr && err != kTECUsedFallbacksStatus)
601 return;
602 } else if (s->font_charset != CS_NONE) {
603 unitextptr = data;
604 olen = charset_from_unicode(&unitextptr, &len, mactextbuf, 1024,
605 s->font_charset, NULL, ".", 1);
606 } else
607 return;
608 PutScrap(olen, kScrapFlavorTypeText, mactextbuf);
609 sfree(mactextbuf);
610
611 stsz = offsetof(StScrpRec, scrpStyleTab) + sizeof(ScrpSTElement);
612 stsc = smalloc(stsz);
613 stsc->scrpNStyles = 1;
614 stsc->scrpStyleTab[0].scrpStartChar = 0;
615 stsc->scrpStyleTab[0].scrpHeight = s->font_height;
616 stsc->scrpStyleTab[0].scrpAscent = s->font_ascent;
617 stsc->scrpStyleTab[0].scrpFont = s->fontnum;
618 stsc->scrpStyleTab[0].scrpFace = 0;
619 stsc->scrpStyleTab[0].scrpSize = s->cfg.font.size;
620 stsc->scrpStyleTab[0].scrpColor.red = 0;
621 stsc->scrpStyleTab[0].scrpColor.green = 0;
622 stsc->scrpStyleTab[0].scrpColor.blue = 0;
623 PutScrap(stsz, kScrapFlavorTypeTextStyle, stsc);
624 sfree(stsc);
625 #endif
626 }
627
628 void get_clip(void *frontend, wchar_t **p, int *lenp) {
629 #if TARGET_API_MAC_CARBON
630 *lenp = 0;
631 #else
632 Session *s = frontend;
633 static Handle h = NULL;
634 static wchar_t *data = NULL;
635 Handle texth;
636 long offset;
637 int textlen;
638 TextEncoding enc;
639 TextToUnicodeInfo scrap_to_uni;
640 ByteCount iread, olen;
641 int charset;
642 char *tptr;
643 OSErr err;
644
645 if (p == NULL) {
646 /* release memory */
647 if (h != NULL)
648 DisposeHandle(h);
649 h = NULL;
650 if (data != NULL)
651 sfree(data);
652 data = NULL;
653 } else {
654 if (GetScrap(NULL, kScrapFlavorTypeUnicode, &offset) > 0) {
655 if (h == NULL)
656 h = NewHandle(0);
657 *lenp =
658 GetScrap(h, kScrapFlavorTypeUnicode, &offset) / sizeof(**p);
659 HLock(h);
660 *p = (wchar_t *)*h;
661 } else if (GetScrap(NULL, kScrapFlavorTypeText, &offset) > 0) {
662 texth = NewHandle(0);
663 textlen = GetScrap(texth, kScrapFlavorTypeText, &offset);
664 HLock(texth);
665 data = smalloc(textlen * 2);
666 /* XXX should use 'styl' scrap if it's there. */
667 if (mac_gestalts.encvvers != 0 &&
668 UpgradeScriptInfoToTextEncoding(smSystemScript,
669 kTextLanguageDontCare,
670 kTextRegionDontCare, NULL,
671 &enc) == noErr &&
672 CreateTextToUnicodeInfoByEncoding(enc, &scrap_to_uni) ==
673 noErr) {
674 err = ConvertFromTextToUnicode(scrap_to_uni, textlen,
675 *texth, 0, 0, NULL, NULL, NULL,
676 textlen * 2,
677 &iread, &olen, data);
678 DisposeTextToUnicodeInfo(&scrap_to_uni);
679 if (err == noErr) {
680 *p = data;
681 *lenp = olen / sizeof(**p);
682 } else {
683 *p = NULL;
684 *lenp = 0;
685 }
686 } else {
687 charset =
688 charset_from_macenc(GetScriptManagerVariable(smSysScript),
689 GetScriptManagerVariable(smRegionCode),
690 mac_gestalts.sysvers, NULL);
691 if (charset != CS_NONE) {
692 tptr = *texth;
693 *lenp = charset_to_unicode(&tptr, &textlen, data,
694 textlen * 2, charset, NULL,
695 NULL, 0);
696 }
697 *p = data;
698 }
699 DisposeHandle(texth);
700 } else {
701 *p = NULL;
702 *lenp = 0;
703 }
704 }
705 #endif
706 }
707
708 static pascal void mac_scrolltracker(ControlHandle control, short part) {
709 Session *s;
710
711 #if TARGET_API_MAC_CARBON
712 s = mac_windowsession(GetControlOwner(control));
713 #else
714 s = mac_windowsession((*control)->contrlOwner);
715 #endif
716 switch (part) {
717 case kControlUpButtonPart:
718 term_scroll(s->term, 0, -1);
719 break;
720 case kControlDownButtonPart:
721 term_scroll(s->term, 0, +1);
722 break;
723 case kControlPageUpPart:
724 term_scroll(s->term, 0, -(s->term->rows - 1));
725 break;
726 case kControlPageDownPart:
727 term_scroll(s->term, 0, +(s->term->rows - 1));
728 break;
729 }
730 }
731
732 void mac_keyterm(WindowPtr window, EventRecord *event) {
733 Session *s = mac_windowsession(window);
734 Key_Sym keysym = PK_NULL;
735 unsigned int mods = 0, flags = PKF_NUMLOCK;
736 UniChar utxt[1];
737 char txt[1];
738 size_t len = 0;
739 ScriptCode key_script;
740
741 ObscureCursor();
742
743 #if 0
744 fprintf(stderr, "Got key event %08x\n", event->message);
745 #endif
746
747 /* No meta key yet -- that'll be rather fun. */
748
749 /* Keys that we handle locally */
750 if (event->modifiers & shiftKey) {
751 switch ((event->message & keyCodeMask) >> 8) {
752 case 0x74: /* shift-pageup */
753 term_scroll(s->term, 0, -(s->term->rows - 1));
754 return;
755 case 0x79: /* shift-pagedown */
756 term_scroll(s->term, 0, +(s->term->rows - 1));
757 return;
758 }
759 }
760
761 if (event->modifiers & shiftKey)
762 mods |= PKM_SHIFT;
763 if (event->modifiers & controlKey)
764 mods |= PKM_CONTROL;
765 if (event->what == autoKey)
766 flags |= PKF_REPEAT;
767
768 /* Mac key events consist of a virtual key code and a character code. */
769
770 switch ((event->message & keyCodeMask) >> 8) {
771 case 0x24: keysym = PK_RETURN; break;
772 case 0x30: keysym = PK_TAB; break;
773 case 0x33: keysym = PK_BACKSPACE; break;
774 case 0x35: keysym = PK_ESCAPE; break;
775
776 case 0x7A: keysym = PK_F1; break;
777 case 0x78: keysym = PK_F2; break;
778 case 0x63: keysym = PK_F3; break;
779 case 0x76: keysym = PK_F4; break;
780 case 0x60: keysym = PK_F5; break;
781 case 0x61: keysym = PK_F6; break;
782 case 0x62: keysym = PK_F7; break;
783 case 0x64: keysym = PK_F8; break;
784 case 0x65: keysym = PK_F9; break;
785 case 0x6D: keysym = PK_F10; break;
786 case 0x67: keysym = PK_F11; break;
787 case 0x6F: keysym = PK_F12; break;
788 case 0x69: keysym = PK_F13; break;
789 case 0x6B: keysym = PK_F14; break;
790 case 0x71: keysym = PK_F15; break;
791
792 case 0x72: keysym = PK_INSERT; break;
793 case 0x73: keysym = PK_HOME; break;
794 case 0x74: keysym = PK_PAGEUP; break;
795 case 0x75: keysym = PK_DELETE; break;
796 case 0x77: keysym = PK_END; break;
797 case 0x79: keysym = PK_PAGEDOWN; break;
798
799 case 0x47: keysym = PK_PF1; break;
800 case 0x51: keysym = PK_PF2; break;
801 case 0x4B: keysym = PK_PF3; break;
802 case 0x43: keysym = PK_PF4; break;
803 case 0x4E: keysym = PK_KPMINUS; break;
804 case 0x45: keysym = PK_KPCOMMA; break;
805 case 0x41: keysym = PK_KPDECIMAL; break;
806 case 0x4C: keysym = PK_KPENTER; break;
807 case 0x52: keysym = PK_KP0; break;
808 case 0x53: keysym = PK_KP1; break;
809 case 0x54: keysym = PK_KP2; break;
810 case 0x55: keysym = PK_KP3; break;
811 case 0x56: keysym = PK_KP4; break;
812 case 0x57: keysym = PK_KP5; break;
813 case 0x58: keysym = PK_KP6; break;
814 case 0x59: keysym = PK_KP7; break;
815 case 0x5B: keysym = PK_KP8; break;
816 case 0x5C: keysym = PK_KP9; break;
817
818 case 0x7B: keysym = PK_LEFT; break;
819 case 0x7C: keysym = PK_RIGHT; break;
820 case 0x7D: keysym = PK_DOWN; break;
821 case 0x7E: keysym = PK_UP; break;
822 }
823
824 /* Map from key script to Unicode. */
825 txt[0] = event->message & charCodeMask;
826 key_script = GetScriptManagerVariable(smKeyScript);
827
828 if (mac_gestalts.encvvers != 0) {
829 static TextToUnicodeInfo key_to_uni = NULL;
830 static ScriptCode key_to_uni_script;
831 TextEncoding enc;
832 ByteCount iread, olen;
833 OSErr err;
834
835 if (key_to_uni != NULL && key_to_uni_script != key_script)
836 DisposeTextToUnicodeInfo(&key_to_uni);
837 if (key_to_uni == NULL || key_to_uni_script != key_script) {
838 if (UpgradeScriptInfoToTextEncoding(key_script,
839 kTextLanguageDontCare,
840 kTextRegionDontCare, NULL,
841 &enc) == noErr &&
842 CreateTextToUnicodeInfoByEncoding(enc, &key_to_uni) == noErr)
843 key_to_uni_script = key_script;
844 else
845 key_to_uni = NULL;
846 }
847 if (key_to_uni != NULL) {
848 err = ConvertFromTextToUnicode(key_to_uni, 1, txt,
849 (kUnicodeKeepInfoMask |
850 kUnicodeStringUnterminatedMask),
851 0, NULL, NULL, NULL,
852 sizeof(utxt), &iread, &olen, utxt);
853 if (err == noErr)
854 len = olen / sizeof(*utxt);
855 }
856 } else {
857 int charset;
858 char *tptr = txt;
859 int tlen = 1;
860
861 charset = charset_from_macenc(key_script,
862 GetScriptManagerVariable(smRegionCode),
863 mac_gestalts.sysvers, NULL);
864 if (charset != CS_NONE) {
865 len = charset_to_unicode(&tptr, &tlen, utxt, sizeof(utxt), charset,
866 NULL, NULL, 0);
867 }
868 }
869 term_key(s->term, keysym, utxt, len, mods, flags);
870 }
871
872 void request_paste(void *frontend)
873 {
874 Session *s = frontend;
875
876 /*
877 * In the Mac OS, pasting is synchronous: we can read the
878 * clipboard with no difficulty, so request_paste() can just go
879 * ahead and paste.
880 */
881 term_do_paste(s->term);
882 }
883
884 static struct {
885 Rect msgrect;
886 Point msgorigin;
887 Point zeromouse;
888 Session *s;
889 char oldmsg[20];
890 } growterm_state;
891
892 void mac_growterm(WindowPtr window, EventRecord *event) {
893 Rect limits;
894 long grow_result;
895 int newrows, newcols;
896 Session *s;
897 #if !TARGET_API_MAC_CARBON
898 DragGrayRgnUPP draghooksave;
899 GrafPtr portsave;
900 FontInfo fi;
901 #endif
902
903 s = mac_windowsession(window);
904
905 #if !TARGET_API_MAC_CARBON
906 draghooksave = LMGetDragHook();
907 growterm_state.oldmsg[0] = '\0';
908 growterm_state.zeromouse = event->where;
909 growterm_state.zeromouse.h -= s->term->cols * s->font_width;
910 growterm_state.zeromouse.v -= s->term->rows * s->font_height;
911 growterm_state.s = s;
912 GetPort(&portsave);
913 SetPort(s->window);
914 BackColor(whiteColor);
915 ForeColor(blackColor);
916 TextFont(systemFont);
917 TextFace(0);
918 TextSize(12);
919 GetFontInfo(&fi);
920 SetRect(&growterm_state.msgrect, 0, 0,
921 StringWidth("\p99999x99999") + 4, fi.ascent + fi.descent + 4);
922 SetPt(&growterm_state.msgorigin, 2, fi.ascent + 2);
923 LMSetDragHook(NewDragGrayRgnUPP(mac_growtermdraghook));
924 #endif
925
926 SetRect(&limits, s->font_width + 15, s->font_height, SHRT_MAX, SHRT_MAX);
927 grow_result = GrowWindow(window, event->where, &limits);
928
929 #if !TARGET_API_MAC_CARBON
930 DisposeDragGrayRgnUPP(LMGetDragHook());
931 LMSetDragHook(draghooksave);
932 InvalRect(&growterm_state.msgrect);
933
934 SetPort(portsave);
935 #endif
936
937 if (grow_result != 0) {
938 newrows = HiWord(grow_result) / s->font_height;
939 newcols = (LoWord(grow_result) - 15) / s->font_width;
940 mac_adjustsize(s, newrows, newcols);
941 term_size(s->term, newrows, newcols, s->cfg.savelines);
942 }
943 }
944
945 #if !TARGET_API_MAC_CARBON
946 static pascal void mac_growtermdraghook(void)
947 {
948 Session *s = growterm_state.s;
949 GrafPtr portsave;
950 Point mouse;
951 char buf[20];
952 unsigned char pbuf[20];
953 int newrows, newcols;
954
955 GetMouse(&mouse);
956 newrows = (mouse.v - growterm_state.zeromouse.v) / s->font_height;
957 if (newrows < 1) newrows = 1;
958 newcols = (mouse.h - growterm_state.zeromouse.h) / s->font_width;
959 if (newcols < 1) newcols = 1;
960 sprintf(buf, "%dx%d", newcols, newrows);
961 if (strcmp(buf, growterm_state.oldmsg) == 0)
962 return;
963 strcpy(growterm_state.oldmsg, buf);
964 c2pstrcpy(pbuf, buf);
965
966 GetPort(&portsave);
967 SetPort(growterm_state.s->window);
968 EraseRect(&growterm_state.msgrect);
969 MoveTo(growterm_state.msgorigin.h, growterm_state.msgorigin.v);
970 DrawString(pbuf);
971 SetPort(portsave);
972 }
973 #endif
974
975 void mac_closeterm(WindowPtr window)
976 {
977 Session *s = mac_windowsession(window);
978
979 /* XXX warn on close */
980 HideWindow(s->window);
981 *s->prev = s->next;
982 s->next->prev = s->prev;
983 ldisc_free(s->ldisc);
984 s->back->free(s->backhandle);
985 log_free(s->logctx);
986 if (s->uni_to_font != NULL)
987 DisposeUnicodeToTextInfo(&s->uni_to_font);
988 term_free(s->term);
989 sfree((WinInfo *)GetWRefCon(s->window));
990 DisposeWindow(s->window);
991 DisposePalette(s->palette);
992 sfree(s);
993 }
994
995 void mac_activateterm(WindowPtr window, Boolean active) {
996 Session *s;
997
998 s = mac_windowsession(window);
999 s->term->has_focus = active;
1000 term_update(s->term);
1001 if (active)
1002 ShowControl(s->scrollbar);
1003 else {
1004 if (HAVE_COLOR_QD())
1005 PmBackColor(DEFAULT_BG);/* HideControl clears behind the control */
1006 else
1007 BackColor(blackColor);
1008 HideControl(s->scrollbar);
1009 }
1010 mac_drawgrowicon(s);
1011 }
1012
1013 void mac_updateterm(WindowPtr window) {
1014 Session *s;
1015 Rect bbox;
1016 #if TARGET_API_MAC_CARBON
1017 RgnHandle visrgn;
1018 #endif
1019
1020 s = mac_windowsession(window);
1021 SetPort((GrafPtr)GetWindowPort(window));
1022 BeginUpdate(window);
1023 pre_paint(s);
1024 #if TARGET_API_MAC_CARBON
1025 visrgn = NewRgn();
1026 GetPortVisibleRegion(GetWindowPort(window), visrgn);
1027 GetRegionBounds(visrgn, &bbox);
1028 #else
1029 bbox = (*window->visRgn)->rgnBBox;
1030 #endif
1031 term_paint(s->term, s, PTOCC(bbox.left), PTOCR(bbox.top),
1032 PTOCC(bbox.right), PTOCR(bbox.bottom), 1);
1033 /* Restore default colours in case the Window Manager uses them */
1034 if (HAVE_COLOR_QD()) {
1035 PmForeColor(DEFAULT_FG);
1036 PmBackColor(DEFAULT_BG);
1037 } else {
1038 ForeColor(whiteColor);
1039 BackColor(blackColor);
1040 }
1041 if (FrontWindow() != window)
1042 #if TARGET_API_MAC_CARBON
1043 EraseRect(GetControlBounds(s->scrollbar, &bbox));
1044 UpdateControls(window, visrgn);
1045 DisposeRgn(visrgn);
1046 #else
1047 EraseRect(&(*s->scrollbar)->contrlRect);
1048 UpdateControls(window, window->visRgn);
1049 #endif
1050 mac_drawgrowicon(s);
1051 post_paint(s);
1052 EndUpdate(window);
1053 }
1054
1055 static void mac_drawgrowicon(Session *s) {
1056 Rect clip;
1057 RgnHandle savergn;
1058
1059 SetPort((GrafPtr)GetWindowPort(s->window));
1060 /*
1061 * Stop DrawGrowIcon giving us space for a horizontal scrollbar
1062 * See Tech Note TB575 for details.
1063 */
1064 #if TARGET_API_MAC_CARBON
1065 GetPortBounds(GetWindowPort(s->window), &clip);
1066 #else
1067 clip = s->window->portRect;
1068 #endif
1069 clip.left = clip.right - 15;
1070 savergn = NewRgn();
1071 GetClip(savergn);
1072 ClipRect(&clip);
1073 DrawGrowIcon(s->window);
1074 SetClip(savergn);
1075 DisposeRgn(savergn);
1076 }
1077
1078 struct do_text_args {
1079 Session *s;
1080 Rect textrect;
1081 char *text;
1082 int len;
1083 unsigned long attr;
1084 int lattr;
1085 Point numer, denom;
1086 };
1087
1088 /*
1089 * Call from the terminal emulator to draw a bit of text
1090 *
1091 * x and y are text row and column (zero-based)
1092 */
1093 void do_text(Context ctx, int x, int y, char *text, int len,
1094 unsigned long attr, int lattr) {
1095 Session *s = ctx;
1096 int style;
1097 struct do_text_args a;
1098 RgnHandle textrgn, saveclip;
1099 #if TARGET_API_MAC_CARBON
1100 RgnHandle visrgn;
1101 #endif
1102 char mactextbuf[1024];
1103 UniChar unitextbuf[1024];
1104 wchar_t *unitextptr;
1105 int i, fontwidth;
1106 ByteCount iread, olen;
1107 OSStatus err;
1108 static DeviceLoopDrawingUPP do_text_for_device_upp = NULL;
1109
1110 assert(len <= 1024);
1111
1112 SetPort((GrafPtr)GetWindowPort(s->window));
1113
1114 fontwidth = s->font_width;
1115 if ((lattr & LATTR_MODE) != LATTR_NORM)
1116 fontwidth *= 2;
1117
1118 /* First check this text is relevant */
1119 a.textrect.top = y * s->font_height;
1120 a.textrect.bottom = (y + 1) * s->font_height;
1121 a.textrect.left = x * fontwidth;
1122 a.textrect.right = (x + len) * fontwidth;
1123 if (a.textrect.right > s->term->cols * s->font_width)
1124 a.textrect.right = s->term->cols * s->font_width;
1125 #if TARGET_API_MAC_CARBON
1126 visrgn = NewRgn();
1127 GetPortVisibleRegion(GetWindowPort(s->window), visrgn);
1128 if (!RectInRgn(&a.textrect, visrgn)) {
1129 DisposeRgn(visrgn);
1130 return;
1131 }
1132 DisposeRgn(visrgn);
1133 #else
1134 if (!RectInRgn(&a.textrect, s->window->visRgn))
1135 return;
1136 #endif
1137
1138 /* Unpack Unicode from the mad format we get passed */
1139 for (i = 0; i < len; i++)
1140 unitextbuf[i] = (unsigned char)text[i] | (attr & CSET_MASK);
1141
1142 if (s->uni_to_font != NULL) {
1143 err = ConvertFromUnicodeToText(s->uni_to_font, len * sizeof(UniChar),
1144 unitextbuf, kUnicodeUseFallbacksMask,
1145 0, NULL, NULL, NULL,
1146 1024, &iread, &olen, mactextbuf);
1147 if (err != noErr && err != kTECUsedFallbacksStatus)
1148 olen = 0;
1149 } else if (s->font_charset != CS_NONE) {
1150 /* XXX this is bogus if wchar_t and UniChar are different sizes. */
1151 unitextptr = (wchar_t *)unitextbuf;
1152 olen = charset_from_unicode(&unitextptr, &len, mactextbuf, 1024,
1153 s->font_charset, NULL, ".", 1);
1154 } else
1155 olen = 0;
1156
1157 a.s = s;
1158 a.text = mactextbuf;
1159 a.len = olen;
1160 a.attr = attr;
1161 a.lattr = lattr;
1162 switch (lattr & LATTR_MODE) {
1163 case LATTR_NORM:
1164 TextSize(s->cfg.font.size);
1165 a.numer = s->font_stdnumer;
1166 a.denom = s->font_stddenom;
1167 break;
1168 case LATTR_WIDE:
1169 TextSize(s->cfg.font.size);
1170 a.numer = s->font_widenumer;
1171 a.denom = s->font_widedenom;
1172 break;
1173 case LATTR_TOP:
1174 case LATTR_BOT:
1175 TextSize(s->cfg.font.size * 2);
1176 a.numer = s->font_bignumer;
1177 a.denom = s->font_bigdenom;
1178 break;
1179 }
1180 SetPort((GrafPtr)GetWindowPort(s->window));
1181 TextFont(s->fontnum);
1182 style = s->cfg.font.face;
1183 if ((attr & ATTR_BOLD) && !s->cfg.bold_colour)
1184 style |= bold;
1185 if (attr & ATTR_UNDER)
1186 style |= underline;
1187 TextFace(style);
1188 TextMode(srcOr);
1189 if (HAVE_COLOR_QD())
1190 if (style & bold) {
1191 SpaceExtra(s->font_boldadjust << 16);
1192 CharExtra(s->font_boldadjust << 16);
1193 } else {
1194 SpaceExtra(0);
1195 CharExtra(0);
1196 }
1197 saveclip = NewRgn();
1198 GetClip(saveclip);
1199 ClipRect(&a.textrect);
1200 textrgn = NewRgn();
1201 RectRgn(textrgn, &a.textrect);
1202 if (HAVE_COLOR_QD()) {
1203 if (do_text_for_device_upp == NULL)
1204 do_text_for_device_upp =
1205 NewDeviceLoopDrawingUPP(&do_text_for_device);
1206 DeviceLoop(textrgn, do_text_for_device_upp, (long)&a, 0);
1207 } else
1208 do_text_for_device(1, 0, NULL, (long)&a);
1209 SetClip(saveclip);
1210 DisposeRgn(saveclip);
1211 DisposeRgn(textrgn);
1212 /* Tell the window manager about it in case this isn't an update */
1213 #if TARGET_API_MAC_CARBON
1214 ValidWindowRect(s->window, &a.textrect);
1215 #else
1216 ValidRect(&a.textrect);
1217 #endif
1218 }
1219
1220 static pascal void do_text_for_device(short depth, short devflags,
1221 GDHandle device, long cookie) {
1222 struct do_text_args *a = (struct do_text_args *)cookie;
1223 int bgcolour, fgcolour, bright, reverse, tmp;
1224 #if TARGET_API_MAC_CARBON
1225 CQDProcsPtr gp = GetPortGrafProcs(GetWindowPort(a->s->window));
1226 #else
1227 QDProcsPtr gp = a->s->window->grafProcs;
1228 #endif
1229
1230 bright = (a->attr & ATTR_BOLD) && a->s->cfg.bold_colour;
1231 reverse = a->attr & ATTR_REVERSE;
1232
1233 if (depth == 1 && (a->attr & TATTR_ACTCURS))
1234 reverse = !reverse;
1235
1236 if (HAVE_COLOR_QD()) {
1237 if (depth > 2) {
1238 fgcolour = ((a->attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1239 fgcolour = (fgcolour & 0xF) * 2 + (fgcolour & 0x10 ? 1 : 0);
1240 bgcolour = ((a->attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1241 bgcolour = (bgcolour & 0xF) * 2 + (bgcolour & 0x10 ? 1 : 0);
1242 } else {
1243 /*
1244 * NB: bold reverse in 2bpp breaks with the usual PuTTY model and
1245 * boldens the background, because that's all we can do.
1246 */
1247 fgcolour = bright ? DEFAULT_FG_BOLD : DEFAULT_FG;
1248 bgcolour = DEFAULT_BG;
1249 }
1250 if (reverse) {
1251 tmp = fgcolour;
1252 fgcolour = bgcolour;
1253 bgcolour = tmp;
1254 }
1255 if (bright && depth > 2)
1256 fgcolour |= 1;
1257 if ((a->attr & TATTR_ACTCURS) && depth > 1) {
1258 fgcolour = CURSOR_FG;
1259 bgcolour = CURSOR_BG;
1260 }
1261 PmForeColor(fgcolour);
1262 PmBackColor(bgcolour);
1263 } else { /* No Color Quickdraw */
1264 /* XXX This should be done with a _little_ more configurability */
1265 if (reverse) {
1266 ForeColor(blackColor);
1267 BackColor(whiteColor);
1268 } else {
1269 ForeColor(whiteColor);
1270 BackColor(blackColor);
1271 }
1272 }
1273
1274 EraseRect(&a->textrect);
1275 switch (a->lattr & LATTR_MODE) {
1276 case LATTR_NORM:
1277 case LATTR_WIDE:
1278 MoveTo(a->textrect.left, a->textrect.top + a->s->font_ascent);
1279 break;
1280 case LATTR_TOP:
1281 MoveTo(a->textrect.left, a->textrect.top + a->s->font_ascent * 2);
1282 break;
1283 case LATTR_BOT:
1284 MoveTo(a->textrect.left,
1285 a->textrect.top - a->s->font_height + a->s->font_ascent * 2);
1286 break;
1287 }
1288 /* FIXME: Sort out bold width adjustments on Original QuickDraw. */
1289 if (gp != NULL)
1290 InvokeQDTextUPP(a->len, a->text, a->numer, a->denom, gp->textProc);
1291 else
1292 StdText(a->len, a->text, a->numer, a->denom);
1293
1294 if (a->attr & TATTR_PASCURS) {
1295 PenNormal();
1296 switch (depth) {
1297 case 1:
1298 PenMode(patXor);
1299 break;
1300 default:
1301 PmForeColor(CURSOR_BG);
1302 break;
1303 }
1304 FrameRect(&a->textrect);
1305 }
1306 }
1307
1308 void do_cursor(Context ctx, int x, int y, char *text, int len,
1309 unsigned long attr, int lattr)
1310 {
1311
1312 do_text(ctx, x, y, text, len, attr, lattr);
1313 }
1314
1315 /*
1316 * Call from the terminal emulator to get its graphics context.
1317 * Should probably be called start_redraw or something.
1318 */
1319 void pre_paint(Session *s) {
1320 GDHandle gdh;
1321 Rect myrect, tmprect;
1322 #if TARGET_API_MAC_CARBON
1323 RgnHandle visrgn;
1324 #endif
1325
1326 if (HAVE_COLOR_QD()) {
1327 s->term->attr_mask = 0;
1328 SetPort((GrafPtr)GetWindowPort(s->window));
1329 #if TARGET_API_MAC_CARBON
1330 visrgn = NewRgn();
1331 GetPortVisibleRegion(GetWindowPort(s->window), visrgn);
1332 GetRegionBounds(visrgn, &myrect);
1333 DisposeRgn(visrgn);
1334 #else
1335 myrect = (*s->window->visRgn)->rgnBBox;
1336 #endif
1337 LocalToGlobal((Point *)&myrect.top);
1338 LocalToGlobal((Point *)&myrect.bottom);
1339 for (gdh = GetDeviceList();
1340 gdh != NULL;
1341 gdh = GetNextDevice(gdh)) {
1342 if (TestDeviceAttribute(gdh, screenDevice) &&
1343 TestDeviceAttribute(gdh, screenActive) &&
1344 SectRect(&(*gdh)->gdRect, &myrect, &tmprect)) {
1345 switch ((*(*gdh)->gdPMap)->pixelSize) {
1346 case 1:
1347 if (s->cfg.bold_colour)
1348 s->term->attr_mask |= ~(ATTR_COLOURS |
1349 (s->cfg.bold_colour ? ATTR_BOLD : 0));
1350 break;
1351 case 2:
1352 s->term->attr_mask |= ~ATTR_COLOURS;
1353 break;
1354 default:
1355 s->term->attr_mask = ~0;
1356 return; /* No point checking more screens. */
1357 }
1358 }
1359 }
1360 } else
1361 s->term->attr_mask = ~(ATTR_COLOURS |
1362 (s->cfg.bold_colour ? ATTR_BOLD : 0));
1363 }
1364
1365 Context get_ctx(void *frontend) {
1366 Session *s = frontend;
1367
1368 pre_paint(s);
1369 return s;
1370 }
1371
1372 void free_ctx(Context ctx) {
1373
1374 }
1375
1376 /*
1377 * Presumably this does something in Windows
1378 */
1379 void post_paint(Session *s) {
1380
1381 }
1382
1383 /*
1384 * Set the scroll bar position
1385 *
1386 * total is the line number of the bottom of the working screen
1387 * start is the line number of the top of the display
1388 * page is the length of the displayed page
1389 */
1390 void set_sbar(void *frontend, int total, int start, int page) {
1391 Session *s = frontend;
1392
1393 /* We don't redraw until we've set everything up, to avoid glitches */
1394 SetControlMinimum(s->scrollbar, 0);
1395 SetControlMaximum(s->scrollbar, total - page);
1396 SetControlValue(s->scrollbar, start);
1397 #if !TARGET_CPU_68K
1398 if (mac_gestalts.cntlattr & gestaltControlMgrPresent)
1399 SetControlViewSize(s->scrollbar, page);
1400 #endif
1401 }
1402
1403 void sys_cursor(void *frontend, int x, int y)
1404 {
1405 /*
1406 * I think his is meaningless under Mac OS.
1407 */
1408 }
1409
1410 /*
1411 * This is still called when mode==BELL_VISUAL, even though the
1412 * visual bell is handled entirely within terminal.c, because we
1413 * may want to perform additional actions on any kind of bell (for
1414 * example, taskbar flashing in Windows).
1415 */
1416 void beep(void *frontend, int mode)
1417 {
1418 if (mode != BELL_VISUAL)
1419 SysBeep(30);
1420 /*
1421 * XXX We should indicate the relevant window and/or use the
1422 * Notification Manager
1423 */
1424 }
1425
1426 int char_width(Context ctx, int uc)
1427 {
1428 /*
1429 * Until we support exciting character-set stuff, assume all chars are
1430 * single-width.
1431 */
1432 return 1;
1433 }
1434
1435 /*
1436 * Set icon string -- a no-op here (Windowshade?)
1437 */
1438 void set_icon(void *frontend, char *icon) {
1439 Session *s = frontend;
1440
1441 }
1442
1443 /*
1444 * Set the window title
1445 */
1446 void set_title(void *frontend, char *title) {
1447 Session *s = frontend;
1448 Str255 mactitle;
1449
1450 c2pstrcpy(mactitle, title);
1451 SetWTitle(s->window, mactitle);
1452 }
1453
1454 /*
1455 * set or clear the "raw mouse message" mode
1456 */
1457 void set_raw_mouse_mode(void *frontend, int activate)
1458 {
1459 Session *s = frontend;
1460
1461 s->raw_mouse = activate;
1462 /* FIXME: Should call mac_updatetermcursor as appropriate. */
1463 }
1464
1465 /*
1466 * Resize the window at the emulator's request
1467 */
1468 void request_resize(void *frontend, int w, int h) {
1469 Session *s = frontend;
1470
1471 term_size(s->term, h, w, s->cfg.savelines);
1472 mac_initfont(s);
1473 }
1474
1475 /*
1476 * Iconify (actually collapse) the window at the emulator's request.
1477 */
1478 void set_iconic(void *frontend, int iconic)
1479 {
1480 Session *s = frontend;
1481 UInt32 features;
1482
1483 if (mac_gestalts.apprvers >= 0x0100 &&
1484 GetWindowFeatures(s->window, &features) == noErr &&
1485 (features & kWindowCanCollapse))
1486 CollapseWindow(s->window, iconic);
1487 }
1488
1489 /*
1490 * Move the window in response to a server-side request.
1491 */
1492 void move_window(void *frontend, int x, int y)
1493 {
1494 Session *s = frontend;
1495
1496 MoveWindow(s->window, x, y, FALSE);
1497 }
1498
1499 /*
1500 * Move the window to the top or bottom of the z-order in response
1501 * to a server-side request.
1502 */
1503 void set_zorder(void *frontend, int top)
1504 {
1505 Session *s = frontend;
1506
1507 /*
1508 * We also change the input focus to point to the topmost window,
1509 * since that's probably what the Human Interface Guidelines would
1510 * like us to do.
1511 */
1512 if (top)
1513 SelectWindow(s->window);
1514 else
1515 SendBehind(s->window, NULL);
1516 }
1517
1518 /*
1519 * Refresh the window in response to a server-side request.
1520 */
1521 void refresh_window(void *frontend)
1522 {
1523 Session *s = frontend;
1524
1525 term_invalidate(s->term);
1526 }
1527
1528 /*
1529 * Maximise or restore the window in response to a server-side
1530 * request.
1531 */
1532 void set_zoomed(void *frontend, int zoomed)
1533 {
1534 Session *s = frontend;
1535
1536 ZoomWindow(s->window, zoomed ? inZoomOut : inZoomIn, FALSE);
1537 }
1538
1539 /*
1540 * Report whether the window is iconic, for terminal reports.
1541 */
1542 int is_iconic(void *frontend)
1543 {
1544 Session *s = frontend;
1545 UInt32 features;
1546
1547 if (mac_gestalts.apprvers >= 0x0100 &&
1548 GetWindowFeatures(s->window, &features) == noErr &&
1549 (features & kWindowCanCollapse))
1550 return IsWindowCollapsed(s->window);
1551 return FALSE;
1552 }
1553
1554 /*
1555 * Report the window's position, for terminal reports.
1556 */
1557 void get_window_pos(void *frontend, int *x, int *y)
1558 {
1559 Session *s = frontend;
1560 Rect rect;
1561
1562 #if TARGET_API_MAC_CARBON
1563 GetPortBounds(GetWindowPort(s->window), &rect);
1564 #else
1565 rect = s->window->portRect;
1566 #endif
1567 *x = rect.left;
1568 *y = rect.top;
1569 }
1570
1571 /*
1572 * Report the window's pixel size, for terminal reports.
1573 */
1574 void get_window_pixels(void *frontend, int *x, int *y)
1575 {
1576 Session *s = frontend;
1577 Rect rect;
1578
1579 #if TARGET_API_MAC_CARBON
1580 GetPortBounds(GetWindowPort(s->window), &rect);
1581 #else
1582 rect = s->window->portRect;
1583 #endif
1584 *x = rect.right - rect.left;
1585 *y = rect.bottom - rect.top;
1586 }
1587
1588 /*
1589 * Return the window or icon title.
1590 */
1591 char *get_window_title(void *frontend, int icon)
1592 {
1593 Session *s = frontend;
1594 Str255 ptitle;
1595 static char title[256];
1596
1597 GetWTitle(s->window, ptitle);
1598 p2cstrcpy(title, ptitle);
1599 return title;
1600 }
1601
1602 /*
1603 * real_palette_set(): This does the actual palette-changing work on behalf
1604 * of palette_set(). Does _not_ call ActivatePalette() in case the caller
1605 * is doing a batch of updates.
1606 */
1607 static void real_palette_set(Session *s, int n, int r, int g, int b)
1608 {
1609 RGBColor col;
1610
1611 if (!HAVE_COLOR_QD())
1612 return;
1613 col.red = r * 0x0101;
1614 col.green = g * 0x0101;
1615 col.blue = b * 0x0101;
1616 SetEntryColor(s->palette, n, &col);
1617 }
1618
1619 /*
1620 * Set the logical palette. Called by the terminal emulator.
1621 */
1622 void palette_set(void *frontend, int n, int r, int g, int b) {
1623 Session *s = frontend;
1624 static const int first[21] = {
1625 0, 2, 4, 6, 8, 10, 12, 14,
1626 1, 3, 5, 7, 9, 11, 13, 15,
1627 16, 17, 18, 20, 21
1628 };
1629
1630 if (!HAVE_COLOR_QD())
1631 return;
1632 real_palette_set(s, first[n], r, g, b);
1633 if (first[n] == 18)
1634 real_palette_set(s, first[n]+1, r, g, b);
1635 if (first[n] == DEFAULT_BG)
1636 mac_adjustwinbg(s);
1637 ActivatePalette(s->window);
1638 }
1639
1640 /*
1641 * Reset to the default palette
1642 */
1643 void palette_reset(void *frontend) {
1644 Session *s = frontend;
1645 /* This maps colour indices in cfg to those used in our palette. */
1646 static const int ww[] = {
1647 6, 7, 8, 9, 10, 11, 12, 13,
1648 14, 15, 16, 17, 18, 19, 20, 21,
1649 0, 1, 2, 3, 4, 5
1650 };
1651 int i;
1652
1653 if (!HAVE_COLOR_QD())
1654 return;
1655
1656 assert(lenof(ww) == NCOLOURS);
1657
1658 for (i = 0; i < NCOLOURS; i++) {
1659 real_palette_set(s, i,
1660 s->cfg.colours[ww[i]][0],
1661 s->cfg.colours[ww[i]][1],
1662 s->cfg.colours[ww[i]][2]);
1663 }
1664 mac_adjustwinbg(s);
1665 ActivatePalette(s->window);
1666 /* Palette Manager will generate update events as required. */
1667 }
1668
1669 /*
1670 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
1671 * for backward.)
1672 */
1673 void do_scroll(Context ctx, int topline, int botline, int lines) {
1674 Session *s = ctx;
1675 Rect r;
1676 RgnHandle scrollrgn = NewRgn();
1677 RgnHandle movedupdate = NewRgn();
1678 RgnHandle update = NewRgn();
1679 Point g2l = { 0, 0 };
1680
1681 SetPort((GrafPtr)GetWindowPort(s->window));
1682
1683 /*
1684 * Work out the part of the update region that will scrolled by
1685 * this operation.
1686 */
1687 if (lines > 0)
1688 SetRectRgn(scrollrgn, 0, (topline + lines) * s->font_height,
1689 s->term->cols * s->font_width,
1690 (botline + 1) * s->font_height);
1691 else
1692 SetRectRgn(scrollrgn, 0, topline * s->font_height,
1693 s->term->cols * s->font_width,
1694 (botline - lines + 1) * s->font_height);
1695 #if TARGET_API_MAC_CARBON
1696 GetWindowRegion(s->window, kWindowUpdateRgn, movedupdate);
1697 #else
1698 GetWindowUpdateRgn(s->window, movedupdate);
1699 #endif
1700 GlobalToLocal(&g2l);
1701 OffsetRgn(movedupdate, g2l.h, g2l.v); /* Convert to local co-ords. */
1702 SectRgn(scrollrgn, movedupdate, movedupdate); /* Clip scrolled section. */
1703 #if TARGET_API_MAC_CARBON
1704 ValidWindowRgn(s->window, movedupdate);
1705 #else
1706 ValidRgn(movedupdate);
1707 #endif
1708 OffsetRgn(movedupdate, 0, -lines * s->font_height); /* Scroll it. */
1709
1710 PenNormal();
1711 if (HAVE_COLOR_QD())
1712 PmBackColor(DEFAULT_BG);
1713 else
1714 BackColor(blackColor); /* XXX make configurable */
1715 SetRect(&r, 0, topline * s->font_height,
1716 s->term->cols * s->font_width, (botline + 1) * s->font_height);
1717 ScrollRect(&r, 0, - lines * s->font_height, update);
1718
1719 #if TARGET_API_MAC_CARBON
1720 InvalWindowRgn(s->window, update);
1721 InvalWindowRgn(s->window, movedupdate);
1722 #else
1723 InvalRgn(update);
1724 InvalRgn(movedupdate);
1725 #endif
1726
1727 DisposeRgn(scrollrgn);
1728 DisposeRgn(movedupdate);
1729 DisposeRgn(update);
1730 }
1731
1732 void logevent(void *frontend, char *str) {
1733
1734 fprintf(stderr, "%s\n", str);
1735 }
1736
1737 /* Dummy routine, only required in plink. */
1738 void ldisc_update(void *frontend, int echo, int edit)
1739 {
1740 }
1741
1742 /*
1743 * Mac PuTTY doesn't support printing yet.
1744 */
1745 printer_job *printer_start_job(char *printer)
1746 {
1747
1748 return NULL;
1749 }
1750
1751 void printer_job_data(printer_job *pj, void *data, int len)
1752 {
1753 }
1754
1755 void printer_finish_job(printer_job *pj)
1756 {
1757 }
1758
1759 void frontend_keypress(void *handle)
1760 {
1761 /*
1762 * Keypress termination in non-Close-On-Exit mode is not
1763 * currently supported in PuTTY proper, because the window
1764 * always has a perfectly good Close button anyway. So we do
1765 * nothing here.
1766 */
1767 return;
1768 }
1769
1770 /*
1771 * Ask whether to wipe a session log file before writing to it.
1772 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
1773 */
1774 int askappend(void *frontend, Filename filename)
1775 {
1776
1777 /* FIXME: not implemented yet. */
1778 return 2;
1779 }
1780
1781 /*
1782 * Emacs magic:
1783 * Local Variables:
1784 * c-file-style: "simon"
1785 * End:
1786 */
1787