X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/10135a55c1d5f8d55ae5edbcd5544f676c83a693..9e164d82c8990d3c8cff58d866fb9938907d4d7b:/mac/macterm.c diff --git a/mac/macterm.c b/mac/macterm.c index 89215177..4a75a0c6 100644 --- a/mac/macterm.c +++ b/mac/macterm.c @@ -1,4 +1,4 @@ -/* $Id: macterm.c,v 1.7 2002/11/23 14:22:11 ben Exp $ */ +/* $Id$ */ /* * Copyright (c) 1999 Simon Tatham * Copyright (c) 1999, 2002 Ben Harris @@ -33,8 +33,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -45,8 +47,9 @@ #include #include #include -#include +#include #include +#include #include #include @@ -56,17 +59,16 @@ #include "macresid.h" #include "putty.h" +#include "charset.h" #include "mac.h" #include "terminal.h" -#define NCOLOURS (lenof(((Config *)0)->colours)) - -#define DEFAULT_FG 16 -#define DEFAULT_FG_BOLD 17 -#define DEFAULT_BG 18 -#define DEFAULT_BG_BOLD 19 -#define CURSOR_FG 20 -#define CURSOR_BG 21 +#define DEFAULT_FG 256 +#define DEFAULT_FG_BOLD 257 +#define DEFAULT_BG 258 +#define DEFAULT_BG_BOLD 259 +#define CURSOR_FG 260 +#define CURSOR_BG 261 #define PTOCC(x) ((x) < 0 ? -(-(x - s->font_width - 1) / s->font_width) : \ (x) / s->font_width) @@ -74,139 +76,231 @@ (y) / s->font_height) static void mac_initfont(Session *); +static pascal OSStatus uni_to_font_fallback(UniChar *, ByteCount, ByteCount *, + TextPtr, ByteCount, ByteCount *, + LogicalAddress, + ConstUnicodeMappingPtr); static void mac_initpalette(Session *); static void mac_adjustwinbg(Session *); static void mac_adjustsize(Session *, int, int); static void mac_drawgrowicon(Session *s); +static pascal void mac_growtermdraghook(void); static pascal void mac_scrolltracker(ControlHandle, short); static pascal void do_text_for_device(short, short, GDHandle, long); -static pascal void mac_set_attr_mask(short, short, GDHandle, long); -static int mac_keytrans(Session *, EventRecord *, unsigned char *); +static void do_text_internal(Context, int, int, wchar_t *, int, + unsigned long, int); static void text_click(Session *, EventRecord *); +static void mac_activateterm(WindowPtr, EventRecord *); +static void mac_adjusttermcursor(WindowPtr, Point, RgnHandle); +static void mac_adjusttermmenus(WindowPtr); +static void mac_updateterm(WindowPtr); +static void mac_clickterm(WindowPtr, EventRecord *); +static void mac_growterm(WindowPtr, EventRecord *); +static void mac_keyterm(WindowPtr, EventRecord *); +static void mac_menuterm(WindowPtr, short, short); +static void mac_closeterm(WindowPtr); void pre_paint(Session *s); void post_paint(Session *s); -#if TARGET_RT_MAC_CFM -static RoutineDescriptor mac_scrolltracker_upp = - BUILD_ROUTINE_DESCRIPTOR(uppControlActionProcInfo, - (ProcPtr)mac_scrolltracker); -static RoutineDescriptor do_text_for_device_upp = - BUILD_ROUTINE_DESCRIPTOR(uppDeviceLoopDrawingProcInfo, - (ProcPtr)do_text_for_device); -static RoutineDescriptor mac_set_attr_mask_upp = - BUILD_ROUTINE_DESCRIPTOR(uppDeviceLoopDrawingProcInfo, - (ProcPtr)mac_set_attr_mask); -#else /* not TARGET_RT_MAC_CFM */ -#define mac_scrolltracker_upp mac_scrolltracker -#define do_text_for_device_upp do_text_for_device -#define mac_set_attr_mask_upp mac_set_attr_mask -#endif /* not TARGET_RT_MAC_CFM */ - -static void inbuf_putc(Session *s, int c) { - char ch = c; - - from_backend(s->term, 0, &ch, 1); -} - -static void inbuf_putstr(Session *s, const char *c) { - - from_backend(s->term, 0, (char *)c, strlen(c)); -} +void mac_startsession(Session *s) +{ + const char *errmsg; + int i; + WinInfo *wi; -static void display_resource(Session *s, unsigned long type, short id) { - Handle h; - int len, i; - char *t; - - h = GetResource(type, id); - if (h == NULL) - fatalbox("Can't get test resource"); - len = GetResourceSizeOnDisk(h); - DetachResource(h); - HNoPurge(h); - HLock(h); - t = *h; - from_backend(s->term, 0, t, len); - term_out(s->term); - DisposeHandle(h); -} - + init_ucs(s); -void mac_newsession(void) { - Session *s; - UInt32 starttime; - char msg[128]; - OSErr err; + /* + * Select protocol. This is farmed out into a table in a + * separate file to enable an ssh-free variant. + */ + s->back = backend_from_proto(s->cfg.protocol); + if (s->back == NULL) + fatalbox("Unsupported protocol number found"); - /* This should obviously be initialised by other means */ - s = smalloc(sizeof(*s)); - memset(s, 0, sizeof(*s)); - do_defaults(NULL, &s->cfg); - s->back = &loop_backend; - /* XXX: Own storage management? */ if (HAVE_COLOR_QD()) s->window = GetNewCWindow(wTerminal, NULL, (WindowPtr)-1); else s->window = GetNewWindow(wTerminal, NULL, (WindowPtr)-1); - SetWRefCon(s->window, (long)s); + wi = snew(WinInfo); + memset(wi, 0, sizeof(*wi)); + wi->s = s; + wi->wtype = wTerminal; + wi->activate = &mac_activateterm; + wi->adjustcursor = &mac_adjusttermcursor; + wi->adjustmenus = &mac_adjusttermmenus; + wi->update = &mac_updateterm; + wi->click = &mac_clickterm; + wi->grow = &mac_growterm; + wi->key = &mac_keyterm; + wi->menu = &mac_menuterm; + wi->close = &mac_closeterm; + SetWRefCon(s->window, (long)wi); s->scrollbar = GetNewControl(cVScroll, s->window); - s->term = term_init(s); + s->term = term_init(&s->cfg, &s->ucsdata, s); - s->logctx = log_init(s); + mac_initfont(s); + mac_initpalette(s); + if (HAVE_COLOR_QD()) { + /* Set to FALSE to not get palette updates in the background. */ + SetPalette(s->window, s->palette, TRUE); + ActivatePalette(s->window); + } + + s->logctx = log_init(s->term, &s->cfg); term_provide_logctx(s->term, s->logctx); - s->back->init(s->term, &s->backhandle, "localhost", 23, &s->realhost, 0); + errmsg = s->back->init(s, &s->backhandle, &s->cfg, s->cfg.host, + s->cfg.port, &s->realhost, s->cfg.tcp_nodelay, + s->cfg.tcp_keepalives); + if (errmsg != NULL) + fatalbox("%s", errmsg); s->back->provide_logctx(s->backhandle, s->logctx); + set_title(s, s->realhost); term_provide_resize_fn(s->term, s->back->size, s->backhandle); mac_adjustsize(s, s->cfg.height, s->cfg.width); term_size(s->term, s->cfg.height, s->cfg.width, s->cfg.savelines); - s->ldisc = ldisc_create(s->term, s->back, s->backhandle, s); + s->ldisc = ldisc_create(&s->cfg, s->term, s->back, s->backhandle, s); ldisc_send(s->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */ - mac_initfont(s); - mac_initpalette(s); - s->attr_mask = ATTR_MASK; - if (HAVE_COLOR_QD()) { - /* Set to FALSE to not get palette updates in the background. */ - SetPalette(s->window, s->palette, TRUE); - ActivatePalette(s->window); - } ShowWindow(s->window); - starttime = TickCount(); - display_resource(s, 'pTST', 128); - sprintf(msg, "Elapsed ticks: %d\015\012", TickCount() - starttime); - inbuf_putstr(s, msg); - term_out(s->term); + s->next = sesslist; + s->prev = &sesslist; + if (s->next != NULL) + s->next->prev = &s->next; + sesslist = s; } -static void mac_initfont(Session *s) { - Str255 macfont; +/* + * Try to work out a horizontal scaling factor for the current font + * that will give a chracter width of wantwidth. Return it in numer + * and denom (suitable for passing to StdText()). + */ +static void mac_workoutfontscale(Session *s, int wantwidth, + Point *numerp, Point *denomp) +{ + Point numer, denom, tmpnumer, tmpdenom; + int gotwidth, i; + const char text = 'W'; FontInfo fi; - - SetPort(s->window); - macfont[0] = sprintf((char *)&macfont[1], "%s", s->cfg.font); - GetFNum(macfont, &s->fontnum); +#if TARGET_API_MAC_CARBON + CQDProcsPtr gp = GetPortGrafProcs(GetWindowPort(s->window)); +#else + QDProcsPtr gp = s->window->grafProcs; +#endif + + numer.v = denom.v = 1; /* always */ + numer.h = denom.h = 1; + for (i = 0; i < 3; i++) { + tmpnumer = numer; + tmpdenom = denom; + if (gp != NULL) + gotwidth = InvokeQDTxMeasUPP(1, &text, &tmpnumer, &tmpdenom, &fi, + gp->txMeasProc); + else + gotwidth = StdTxMeas(1, &text, &tmpnumer, &tmpdenom, &fi); + /* The result of StdTxMeas must be scaled by the factors it returns. */ + gotwidth = FixRound(FixMul(gotwidth << 16, + FixRatio(tmpnumer.h, tmpdenom.h))); + if (gotwidth == wantwidth) + break; + numer.h *= wantwidth; + denom.h *= gotwidth; + } + *numerp = numer; + *denomp = denom; +} + +static UnicodeToTextFallbackUPP uni_to_font_fallback_upp; + +static void mac_initfont(Session *s) +{ + FontInfo fi; + TextEncoding enc; + OptionBits fbflags; + + SetPort((GrafPtr)GetWindowPort(s->window)); + GetFNum(s->cfg.font.name, &s->fontnum); TextFont(s->fontnum); - TextFace(s->cfg.fontisbold ? bold : 0); - TextSize(s->cfg.fontheight); + TextFace(s->cfg.font.face); + TextSize(s->cfg.font.size); GetFontInfo(&fi); s->font_width = CharWidth('W'); /* Well, it's what NCSA uses. */ s->font_ascent = fi.ascent; s->font_leading = fi.leading; s->font_height = s->font_ascent + fi.descent + s->font_leading; + mac_workoutfontscale(s, s->font_width, + &s->font_stdnumer, &s->font_stddenom); + mac_workoutfontscale(s, s->font_width * 2, + &s->font_widenumer, &s->font_widedenom); + TextSize(s->cfg.font.size * 2); + mac_workoutfontscale(s, s->font_width * 2, + &s->font_bignumer, &s->font_bigdenom); + TextSize(s->cfg.font.size); if (!s->cfg.bold_colour) { TextFace(bold); s->font_boldadjust = s->font_width - CharWidth('W'); } else s->font_boldadjust = 0; + + if (s->uni_to_font != NULL) + DisposeUnicodeToTextInfo(&s->uni_to_font); + if (mac_gestalts.encvvers != 0 && + UpgradeScriptInfoToTextEncoding(kTextScriptDontCare, + kTextLanguageDontCare, + kTextRegionDontCare, s->cfg.font.name, + &enc) == noErr && + CreateUnicodeToTextInfoByEncoding(enc, &s->uni_to_font) == noErr) { + if (uni_to_font_fallback_upp == NULL) + uni_to_font_fallback_upp = + NewUnicodeToTextFallbackUPP(&uni_to_font_fallback); + fbflags = kUnicodeFallbackCustomOnly; + if (mac_gestalts.uncvattr & kTECAddFallbackInterruptMask) + fbflags |= kUnicodeFallbackInterruptSafeMask; + if (SetFallbackUnicodeToText(s->uni_to_font, + uni_to_font_fallback_upp, fbflags, NULL) != noErr) { + DisposeUnicodeToTextInfo(&s->uni_to_font); + goto no_encv; + } + } else { + char cfontname[256]; + + no_encv: + s->uni_to_font = NULL; + p2cstrcpy(cfontname, s->cfg.font.name); + s->font_charset = + charset_from_macenc(FontToScript(s->fontnum), + GetScriptManagerVariable(smRegionCode), + mac_gestalts.sysvers, cfontname); + } + mac_adjustsize(s, s->term->rows, s->term->cols); } +static pascal OSStatus uni_to_font_fallback(UniChar *ucp, + ByteCount ilen, ByteCount *iusedp, TextPtr obuf, ByteCount olen, + ByteCount *ousedp, LogicalAddress cookie, ConstUnicodeMappingPtr mapping) +{ + + if (olen < 1) + return kTECOutputBufferFullStatus; + /* + * What I'd _like_ to do here is to somehow generate the + * missing-character glyph that every font is required to have. + * Unfortunately (and somewhat surprisingly), I can't find any way + * to actually ask for it explicitly. Bah. + */ + *obuf = '.'; + *iusedp = ilen; + *ousedp = 1; + return noErr; +} + /* * To be called whenever the window size changes. * rows and cols should be desired values. @@ -215,19 +309,26 @@ static void mac_initfont(Session *s) { */ static void mac_adjustsize(Session *s, int newrows, int newcols) { int winwidth, winheight; + int extraforscroll; - winwidth = newcols * s->font_width + 15; + extraforscroll=s->cfg.scrollbar ? 15 : 0; + winwidth = newcols * s->font_width + extraforscroll; winheight = newrows * s->font_height; SizeWindow(s->window, winwidth, winheight, true); - HideControl(s->scrollbar); - MoveControl(s->scrollbar, winwidth - 15, -1); - SizeControl(s->scrollbar, 16, winheight - 13); - ShowControl(s->scrollbar); + if (s->cfg.scrollbar) { + HideControl(s->scrollbar); + MoveControl(s->scrollbar, winwidth - extraforscroll, -1); + SizeControl(s->scrollbar, extraforscroll + 1, winheight - 13); + ShowControl(s->scrollbar); + } + mac_drawgrowicon(s); } -static void mac_initpalette(Session *s) { - int i; +static void mac_initpalette(Session *s) +{ + if (!HAVE_COLOR_QD()) + return; /* * Most colours should be inhibited on 2bpp displays. * Palette manager documentation suggests inhibiting all tolerant colours @@ -236,7 +337,7 @@ static void mac_initpalette(Session *s) { #define PM_NORMAL ( pmTolerant | pmInhibitC2 | \ pmInhibitG2 | pmInhibitG4 | pmInhibitG8 ) #define PM_TOLERANCE 0x2000 - s->palette = NewPalette(22, NULL, PM_NORMAL, PM_TOLERANCE); + s->palette = NewPalette(262, NULL, PM_NORMAL, PM_TOLERANCE); if (s->palette == NULL) fatalbox("Unable to create palette"); /* In 2bpp, these are the colours we want most. */ @@ -255,17 +356,19 @@ static void mac_initpalette(Session *s) { * Set the background colour of the window correctly. Should be * called whenever the default background changes. */ -static void mac_adjustwinbg(Session *s) { +static void mac_adjustwinbg(Session *s) +{ if (!HAVE_COLOR_QD()) return; -#if TARGET_RT_CFM /* XXX doesn't link (at least for 68k) */ +#if !TARGET_CPU_68K if (mac_gestalts.windattr & gestaltWindowMgrPresent) SetWindowContentColor(s->window, &(*s->palette)->pmInfo[DEFAULT_BG].ciRGB); else #endif { +#if !TARGET_API_MAC_CARBON if (s->wctab == NULL) s->wctab = (WCTabHandle)NewHandle(sizeof(**s->wctab)); if (s->wctab == NULL) @@ -276,49 +379,84 @@ static void mac_adjustwinbg(Session *s) { (*s->wctab)->ctTable[0].value = wContentColor; (*s->wctab)->ctTable[0].rgb = (*s->palette)->pmInfo[DEFAULT_BG].ciRGB; SetWinColor(s->window, s->wctab); +#endif } } /* * Set the cursor shape correctly */ -void mac_adjusttermcursor(WindowPtr window, Point mouse, RgnHandle cursrgn) { +static void mac_adjusttermcursor(WindowPtr window, Point mouse, + RgnHandle cursrgn) +{ Session *s; ControlHandle control; short part; int x, y; +#if TARGET_API_MAC_CARBON + Cursor arrow; + Rect rect; + RgnHandle visrgn; +#endif - SetPort(window); - s = (Session *)GetWRefCon(window); + SetPort((GrafPtr)GetWindowPort(window)); + s = mac_windowsession(window); GlobalToLocal(&mouse); part = FindControl(mouse, window, &control); if (control == s->scrollbar) { +#if TARGET_API_MAC_CARBON + SetCursor(GetQDGlobalsArrow(&arrow)); + RectRgn(cursrgn, GetControlBounds(s->scrollbar, &rect)); +#else SetCursor(&qd.arrow); RectRgn(cursrgn, &(*s->scrollbar)->contrlRect); - SectRgn(cursrgn, window->visRgn, cursrgn); +#endif } else { x = mouse.h / s->font_width; y = mouse.v / s->font_height; - if (s->raw_mouse) + if (s->raw_mouse) { +#if TARGET_API_MAC_CARBON + SetCursor(GetQDGlobalsArrow(&arrow)); +#else SetCursor(&qd.arrow); - else +#endif + } else SetCursor(*GetCursor(iBeamCursor)); /* Ask for shape changes if we leave this character cell. */ SetRectRgn(cursrgn, x * s->font_width, y * s->font_height, (x + 1) * s->font_width, (y + 1) * s->font_height); - SectRgn(cursrgn, window->visRgn, cursrgn); } +#if TARGET_API_MAC_CARBON + visrgn = NewRgn(); + GetPortVisibleRegion(GetWindowPort(window), visrgn); + SectRgn(cursrgn, visrgn, cursrgn); + DisposeRgn(visrgn); +#else + SectRgn(cursrgn, window->visRgn, cursrgn); +#endif } /* * Enable/disable menu items based on the active terminal window. */ -void mac_adjusttermmenus(WindowPtr window) { +#if TARGET_API_MAC_CARBON +#define DisableItem DisableMenuItem +#define EnableItem EnableMenuItem +#endif +static void mac_adjusttermmenus(WindowPtr window) +{ Session *s; MenuHandle menu; +#if !TARGET_API_MAC_CARBON long offset; +#endif - s = (Session *)GetWRefCon(window); + s = mac_windowsession(window); + menu = GetMenuHandle(mFile); + DisableItem(menu, iSave); /* XXX enable if modified */ + EnableItem(menu, iSaveAs); + EnableItem(menu, iChange); + EnableItem(menu, iDuplicate); menu = GetMenuHandle(mEdit); EnableItem(menu, 0); DisableItem(menu, iUndo); @@ -327,18 +465,26 @@ void mac_adjusttermmenus(WindowPtr window) { EnableItem(menu, iCopy); else DisableItem(menu, iCopy); - if (GetScrap(NULL, 'TEXT', &offset) == noTypeErr) +#if TARGET_API_MAC_CARBON + if (1) +#else + if (GetScrap(NULL, kScrapFlavorTypeText, &offset) == noTypeErr) +#endif DisableItem(menu, iPaste); else EnableItem(menu, iPaste); DisableItem(menu, iClear); EnableItem(menu, iSelectAll); + menu = GetMenuHandle(mWindow); + EnableItem(menu, 0); + EnableItem(menu, iShowEventLog); } -void mac_menuterm(WindowPtr window, short menu, short item) { +static void mac_menuterm(WindowPtr window, short menu, short item) +{ Session *s; - s = (Session *)GetWRefCon(window); + s = mac_windowsession(window); switch (menu) { case mEdit: switch (item) { @@ -349,17 +495,27 @@ void mac_menuterm(WindowPtr window, short menu, short item) { term_do_paste(s->term); break; } + break; + case mWindow: + switch(item) { + case iShowEventLog: + mac_showeventlog(s); + break; + } + break; } } -void mac_clickterm(WindowPtr window, EventRecord *event) { +static void mac_clickterm(WindowPtr window, EventRecord *event) +{ Session *s; Point mouse; ControlHandle control; int part; + static ControlActionUPP mac_scrolltracker_upp = NULL; - s = (Session *)GetWRefCon(window); - SetPort(window); + s = mac_windowsession(window); + SetPort((GrafPtr)GetWindowPort(window)); mouse = event->where; GlobalToLocal(&mouse); part = FindControl(mouse, window, &control); @@ -373,7 +529,10 @@ void mac_clickterm(WindowPtr window, EventRecord *event) { case kControlDownButtonPart: case kControlPageUpPart: case kControlPageDownPart: - TrackControl(control, mouse, &mac_scrolltracker_upp); + if (mac_scrolltracker_upp == NULL) + mac_scrolltracker_upp = + NewControlActionUPP(&mac_scrolltracker); + TrackControl(control, mouse, mac_scrolltracker_upp); break; } } else { @@ -381,7 +540,8 @@ void mac_clickterm(WindowPtr window, EventRecord *event) { } } -static void text_click(Session *s, EventRecord *event) { +static void text_click(Session *s, EventRecord *event) +{ Point localwhere; int row, col; static UInt32 lastwhen = 0; @@ -389,7 +549,7 @@ static void text_click(Session *s, EventRecord *event) { static int lastrow = -1, lastcol = -1; static Mouse_Action lastact = MA_NOTHING; - SetPort(s->window); + SetPort((GrafPtr)GetWindowPort(s->window)); localwhere = event->where; GlobalToLocal(&localwhere); @@ -402,8 +562,8 @@ static void text_click(Session *s, EventRecord *event) { lastact == MA_3CLK ? MA_CLICK : MA_NOTHING); else lastact = MA_CLICK; - /* Fake right button with shift key */ - term_mouse(s->term, event->modifiers & shiftKey ? MBT_RIGHT : MBT_LEFT, + term_mouse(s->term, MBT_LEFT, + event->modifiers & shiftKey ? MBT_EXTEND : MBT_SELECT, lastact, col, row, event->modifiers & shiftKey, event->modifiers & controlKey, event->modifiers & optionKey); lastsess = s; @@ -413,8 +573,8 @@ static void text_click(Session *s, EventRecord *event) { GetMouse(&localwhere); col = PTOCC(localwhere.h); row = PTOCR(localwhere.v); - term_mouse(s->term, - event->modifiers & shiftKey ? MBT_RIGHT : MBT_LEFT, + term_mouse(s->term, MBT_LEFT, + event->modifiers & shiftKey ? MBT_EXTEND : MBT_SELECT, MA_DRAG, col, row, event->modifiers & shiftKey, event->modifiers & controlKey, event->modifiers & optionKey); @@ -423,69 +583,165 @@ static void text_click(Session *s, EventRecord *event) { else if (row < 0) term_scroll(s->term, 0, row); } - term_mouse(s->term, event->modifiers & shiftKey ? MBT_RIGHT : MBT_LEFT, + term_mouse(s->term, MBT_LEFT, + event->modifiers & shiftKey ? MBT_EXTEND : MBT_SELECT, MA_RELEASE, col, row, event->modifiers & shiftKey, event->modifiers & controlKey, event->modifiers & optionKey); lastwhen = TickCount(); } -Mouse_Button translate_button(void *frontend, Mouse_Button button) +void write_clip(void *cookie, wchar_t *data, int *attr, int len, int must_deselect) { +#if !TARGET_API_MAC_CARBON + Session *s = cookie; + char *mactextbuf; + ByteCount iread, olen; + wchar_t *unitextptr; + StScrpRec *stsc; + size_t stsz; + OSErr err; + int i; - switch (button) { - case MBT_LEFT: - return MBT_SELECT; - case MBT_RIGHT: - return MBT_EXTEND; - default: - return 0; - } -} - -void write_clip(void *cookie, wchar_t *data, int len, int must_deselect) { - /* * See "Programming with the Text Encoding Conversion Manager" * Appendix E for Unicode scrap conventions. * - * XXX Need to support TEXT/styl scrap as well. - * See STScrpRec in TextEdit (Inside Macintosh: Text) for styl details. * XXX Maybe PICT scrap too. */ if (ZeroScrap() != noErr) return; - PutScrap(len * sizeof(*data), 'utxt', data); + PutScrap(len * sizeof(*data), kScrapFlavorTypeUnicode, data); + + /* Replace LINE SEPARATORs with CR for TEXT output. */ + for (i = 0; i < len; i++) + if (data[i] == 0x2028) + data[i] = 0x000d; + + mactextbuf = snewn(len, char); /* XXX DBCS */ + if (s->uni_to_font != NULL) { + err = ConvertFromUnicodeToText(s->uni_to_font, len * sizeof(UniChar), + (UniChar *)data, + kUnicodeUseFallbacksMask, + 0, NULL, NULL, NULL, + len, &iread, &olen, mactextbuf); + if (err != noErr && err != kTECUsedFallbacksStatus) + return; + } else if (s->font_charset != CS_NONE) { + unitextptr = data; + olen = charset_from_unicode(&unitextptr, &len, mactextbuf, 1024, + s->font_charset, NULL, ".", 1); + } else + return; + PutScrap(olen, kScrapFlavorTypeText, mactextbuf); + sfree(mactextbuf); + + stsz = offsetof(StScrpRec, scrpStyleTab) + sizeof(ScrpSTElement); + stsc = smalloc(stsz); + stsc->scrpNStyles = 1; + stsc->scrpStyleTab[0].scrpStartChar = 0; + stsc->scrpStyleTab[0].scrpHeight = s->font_height; + stsc->scrpStyleTab[0].scrpAscent = s->font_ascent; + stsc->scrpStyleTab[0].scrpFont = s->fontnum; + stsc->scrpStyleTab[0].scrpFace = 0; + stsc->scrpStyleTab[0].scrpSize = s->cfg.font.size; + stsc->scrpStyleTab[0].scrpColor.red = 0; + stsc->scrpStyleTab[0].scrpColor.green = 0; + stsc->scrpStyleTab[0].scrpColor.blue = 0; + PutScrap(stsz, kScrapFlavorTypeTextStyle, stsc); + sfree(stsc); +#endif } -void get_clip(void *frontend, wchar_t **p, int *lenp) { +void get_clip(void *frontend, wchar_t **p, int *lenp) +{ +#if TARGET_API_MAC_CARBON + *lenp = 0; +#else Session *s = frontend; static Handle h = NULL; + static wchar_t *data = NULL; + Handle texth; long offset; + int textlen; + TextEncoding enc; + TextToUnicodeInfo scrap_to_uni; + ByteCount iread, olen; + int charset; + char *tptr; + OSErr err; if (p == NULL) { /* release memory */ if (h != NULL) DisposeHandle(h); h = NULL; - } else - /* XXX Support TEXT-format scrap as well. */ - if (GetScrap(NULL, 'utxt', &offset) > 0) { - h = NewHandle(0); - *lenp = GetScrap(h, 'utxt', &offset) / sizeof(**p); + if (data != NULL) + sfree(data); + data = NULL; + } else { + if (GetScrap(NULL, kScrapFlavorTypeUnicode, &offset) > 0) { + if (h == NULL) + h = NewHandle(0); + *lenp = + GetScrap(h, kScrapFlavorTypeUnicode, &offset) / sizeof(**p); HLock(h); *p = (wchar_t *)*h; - if (*p == NULL || *lenp <= 0) - fatalbox("Empty scrap"); + } else if (GetScrap(NULL, kScrapFlavorTypeText, &offset) > 0) { + texth = NewHandle(0); + textlen = GetScrap(texth, kScrapFlavorTypeText, &offset); + HLock(texth); + data = snewn(textlen, wchar_t); + /* XXX should use 'styl' scrap if it's there. */ + if (mac_gestalts.encvvers != 0 && + UpgradeScriptInfoToTextEncoding(smSystemScript, + kTextLanguageDontCare, + kTextRegionDontCare, NULL, + &enc) == noErr && + CreateTextToUnicodeInfoByEncoding(enc, &scrap_to_uni) == + noErr) { + err = ConvertFromTextToUnicode(scrap_to_uni, textlen, + *texth, 0, 0, NULL, NULL, NULL, + textlen * 2, + &iread, &olen, data); + DisposeTextToUnicodeInfo(&scrap_to_uni); + if (err == noErr) { + *p = data; + *lenp = olen / sizeof(**p); + } else { + *p = NULL; + *lenp = 0; + } + } else { + charset = + charset_from_macenc(GetScriptManagerVariable(smSysScript), + GetScriptManagerVariable(smRegionCode), + mac_gestalts.sysvers, NULL); + if (charset != CS_NONE) { + tptr = *texth; + *lenp = charset_to_unicode(&tptr, &textlen, data, + textlen * 2, charset, NULL, + NULL, 0); + } + *p = data; + } + DisposeHandle(texth); } else { *p = NULL; *lenp = 0; } + } +#endif } -static pascal void mac_scrolltracker(ControlHandle control, short part) { +static pascal void mac_scrolltracker(ControlHandle control, short part) +{ Session *s; - s = (Session *)GetWRefCon((*control)->contrlOwner); +#if TARGET_API_MAC_CARBON + s = mac_windowsession(GetControlOwner(control)); +#else + s = mac_windowsession((*control)->contrlOwner); +#endif switch (part) { case kControlUpButtonPart: term_scroll(s->term, 0, -1); @@ -502,202 +758,145 @@ static pascal void mac_scrolltracker(ControlHandle control, short part) { } } -#define K_BS 0x3300 -#define K_F1 0x7a00 -#define K_F2 0x7800 -#define K_F3 0x6300 -#define K_F4 0x7600 -#define K_F5 0x6000 -#define K_F6 0x6100 -#define K_F7 0x6200 -#define K_F8 0x6400 -#define K_F9 0x6500 -#define K_F10 0x6d00 -#define K_F11 0x6700 -#define K_F12 0x6f00 -#define K_F13 0x6900 -#define K_F14 0x6b00 -#define K_F15 0x7100 -#define K_INSERT 0x7200 -#define K_HOME 0x7300 -#define K_PRIOR 0x7400 -#define K_DELETE 0x7500 -#define K_END 0x7700 -#define K_NEXT 0x7900 -#define K_LEFT 0x7b00 -#define K_RIGHT 0x7c00 -#define K_DOWN 0x7d00 -#define K_UP 0x7e00 -#define KP_0 0x5200 -#define KP_1 0x5300 -#define KP_2 0x5400 -#define KP_3 0x5500 -#define KP_4 0x5600 -#define KP_5 0x5700 -#define KP_6 0x5800 -#define KP_7 0x5900 -#define KP_8 0x5b00 -#define KP_9 0x5c00 -#define KP_CLEAR 0x4700 -#define KP_EQUAL 0x5100 -#define KP_SLASH 0x4b00 -#define KP_STAR 0x4300 -#define KP_PLUS 0x4500 -#define KP_MINUS 0x4e00 -#define KP_DOT 0x4100 -#define KP_ENTER 0x4c00 - -void mac_keyterm(WindowPtr window, EventRecord *event) { - unsigned char buf[20]; - int len; - Session *s; +static void mac_keyterm(WindowPtr window, EventRecord *event) +{ + Session *s = mac_windowsession(window); + Key_Sym keysym = PK_NULL; + unsigned int mods = 0, flags = PKF_NUMLOCK; + UniChar utxt[1]; + char txt[1]; + size_t len = 0; + ScriptCode key_script; - s = (Session *)GetWRefCon(window); - len = mac_keytrans(s, event, buf); - ldisc_send(s->ldisc, (char *)buf, len, 1); ObscureCursor(); - term_seen_key_event(s->term); - term_out(s->term); - term_update(s->term); -} -static int mac_keytrans(Session *s, EventRecord *event, - unsigned char *output) { - unsigned char *p = output; - int code; +#if 0 + fprintf(stderr, "Got key event %08x\n", event->message); +#endif /* No meta key yet -- that'll be rather fun. */ /* Keys that we handle locally */ if (event->modifiers & shiftKey) { - switch (event->message & keyCodeMask) { - case K_PRIOR: /* shift-pageup */ + switch ((event->message & keyCodeMask) >> 8) { + case 0x74: /* shift-pageup */ term_scroll(s->term, 0, -(s->term->rows - 1)); - return 0; - case K_NEXT: /* shift-pagedown */ + return; + case 0x79: /* shift-pagedown */ term_scroll(s->term, 0, +(s->term->rows - 1)); - return 0; - } - } - - /* - * Control-2 should return ^@ (0x00), Control-6 should return - * ^^ (0x1E), and Control-Minus should return ^_ (0x1F). Since - * the DOS keyboard handling did it, and we have nothing better - * to do with the key combo in question, we'll also map - * Control-Backquote to ^\ (0x1C). - */ - - if (event->modifiers & controlKey) { - switch (event->message & charCodeMask) { - case ' ': case '2': - *p++ = 0x00; - return p - output; - case '`': - *p++ = 0x1c; - return p - output; - case '6': - *p++ = 0x1e; - return p - output; - case '/': - *p++ = 0x1f; - return p - output; + return; } } - /* - * First, all the keys that do tilde codes. (ESC '[' nn '~', - * for integer decimal nn.) - * - * We also deal with the weird ones here. Linux VCs replace F1 - * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but - * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w - * respectively. - */ - code = 0; - switch (event->message & keyCodeMask) { - case K_F1: code = (event->modifiers & shiftKey ? 23 : 11); break; - case K_F2: code = (event->modifiers & shiftKey ? 24 : 12); break; - case K_F3: code = (event->modifiers & shiftKey ? 25 : 13); break; - case K_F4: code = (event->modifiers & shiftKey ? 26 : 14); break; - case K_F5: code = (event->modifiers & shiftKey ? 28 : 15); break; - case K_F6: code = (event->modifiers & shiftKey ? 29 : 17); break; - case K_F7: code = (event->modifiers & shiftKey ? 31 : 18); break; - case K_F8: code = (event->modifiers & shiftKey ? 32 : 19); break; - case K_F9: code = (event->modifiers & shiftKey ? 33 : 20); break; - case K_F10: code = (event->modifiers & shiftKey ? 34 : 21); break; - case K_F11: code = 23; break; - case K_F12: code = 24; break; - case K_HOME: code = 1; break; - case K_INSERT: code = 2; break; - case K_DELETE: code = 3; break; - case K_END: code = 4; break; - case K_PRIOR: code = 5; break; - case K_NEXT: code = 6; break; - } - if (s->cfg.funky_type == 1 && code >= 11 && code <= 15) { - p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11); - return p - output; - } - if (s->cfg.rxvt_homeend && (code == 1 || code == 4)) { - p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw"); - return p - output; - } - if (code) { - p += sprintf((char *)p, "\x1B[%d~", code); - return p - output; + if (event->modifiers & shiftKey) + mods |= PKM_SHIFT; + if (event->modifiers & controlKey) + mods |= PKM_CONTROL; + if (event->what == autoKey) + flags |= PKF_REPEAT; + + /* Mac key events consist of a virtual key code and a character code. */ + + switch ((event->message & keyCodeMask) >> 8) { + case 0x24: keysym = PK_RETURN; break; + case 0x30: keysym = PK_TAB; break; + case 0x33: keysym = PK_BACKSPACE; break; + case 0x35: keysym = PK_ESCAPE; break; + + case 0x7A: keysym = PK_F1; break; + case 0x78: keysym = PK_F2; break; + case 0x63: keysym = PK_F3; break; + case 0x76: keysym = PK_F4; break; + case 0x60: keysym = PK_F5; break; + case 0x61: keysym = PK_F6; break; + case 0x62: keysym = PK_F7; break; + case 0x64: keysym = PK_F8; break; + case 0x65: keysym = PK_F9; break; + case 0x6D: keysym = PK_F10; break; + case 0x67: keysym = PK_F11; break; + case 0x6F: keysym = PK_F12; break; + case 0x69: keysym = PK_F13; break; + case 0x6B: keysym = PK_F14; break; + case 0x71: keysym = PK_F15; break; + + case 0x72: keysym = PK_INSERT; break; + case 0x73: keysym = PK_HOME; break; + case 0x74: keysym = PK_PAGEUP; break; + case 0x75: keysym = PK_DELETE; break; + case 0x77: keysym = PK_END; break; + case 0x79: keysym = PK_PAGEDOWN; break; + + case 0x47: keysym = PK_PF1; break; + case 0x51: keysym = PK_PF2; break; + case 0x4B: keysym = PK_PF3; break; + case 0x43: keysym = PK_PF4; break; + case 0x4E: keysym = PK_KPMINUS; break; + case 0x45: keysym = PK_KPCOMMA; break; + case 0x41: keysym = PK_KPDECIMAL; break; + case 0x4C: keysym = PK_KPENTER; break; + case 0x52: keysym = PK_KP0; break; + case 0x53: keysym = PK_KP1; break; + case 0x54: keysym = PK_KP2; break; + case 0x55: keysym = PK_KP3; break; + case 0x56: keysym = PK_KP4; break; + case 0x57: keysym = PK_KP5; break; + case 0x58: keysym = PK_KP6; break; + case 0x59: keysym = PK_KP7; break; + case 0x5B: keysym = PK_KP8; break; + case 0x5C: keysym = PK_KP9; break; + + case 0x7B: keysym = PK_LEFT; break; + case 0x7C: keysym = PK_RIGHT; break; + case 0x7D: keysym = PK_DOWN; break; + case 0x7E: keysym = PK_UP; break; } - if (s->term->app_keypad_keys) { - switch (event->message & keyCodeMask) { - case KP_ENTER: p += sprintf((char *)p, "\x1BOM"); return p - output; - case KP_CLEAR: p += sprintf((char *)p, "\x1BOP"); return p - output; - case KP_EQUAL: p += sprintf((char *)p, "\x1BOQ"); return p - output; - case KP_SLASH: p += sprintf((char *)p, "\x1BOR"); return p - output; - case KP_STAR: p += sprintf((char *)p, "\x1BOS"); return p - output; - case KP_PLUS: p += sprintf((char *)p, "\x1BOl"); return p - output; - case KP_MINUS: p += sprintf((char *)p, "\x1BOm"); return p - output; - case KP_DOT: p += sprintf((char *)p, "\x1BOn"); return p - output; - case KP_0: p += sprintf((char *)p, "\x1BOp"); return p - output; - case KP_1: p += sprintf((char *)p, "\x1BOq"); return p - output; - case KP_2: p += sprintf((char *)p, "\x1BOr"); return p - output; - case KP_3: p += sprintf((char *)p, "\x1BOs"); return p - output; - case KP_4: p += sprintf((char *)p, "\x1BOt"); return p - output; - case KP_5: p += sprintf((char *)p, "\x1BOu"); return p - output; - case KP_6: p += sprintf((char *)p, "\x1BOv"); return p - output; - case KP_7: p += sprintf((char *)p, "\x1BOw"); return p - output; - case KP_8: p += sprintf((char *)p, "\x1BOx"); return p - output; - case KP_9: p += sprintf((char *)p, "\x1BOy"); return p - output; + /* Map from key script to Unicode. */ + txt[0] = event->message & charCodeMask; + key_script = GetScriptManagerVariable(smKeyScript); + + if (mac_gestalts.encvvers != 0) { + static TextToUnicodeInfo key_to_uni = NULL; + static ScriptCode key_to_uni_script; + TextEncoding enc; + ByteCount iread, olen; + OSErr err; + + if (key_to_uni != NULL && key_to_uni_script != key_script) + DisposeTextToUnicodeInfo(&key_to_uni); + if (key_to_uni == NULL || key_to_uni_script != key_script) { + if (UpgradeScriptInfoToTextEncoding(key_script, + kTextLanguageDontCare, + kTextRegionDontCare, NULL, + &enc) == noErr && + CreateTextToUnicodeInfoByEncoding(enc, &key_to_uni) == noErr) + key_to_uni_script = key_script; + else + key_to_uni = NULL; + } + if (key_to_uni != NULL) { + err = ConvertFromTextToUnicode(key_to_uni, 1, txt, + (kUnicodeKeepInfoMask | + kUnicodeStringUnterminatedMask), + 0, NULL, NULL, NULL, + sizeof(utxt), &iread, &olen, utxt); + if (err == noErr) + len = olen / sizeof(*utxt); + } + } else { + int charset; + char *tptr = txt; + int tlen = 1; + + charset = charset_from_macenc(key_script, + GetScriptManagerVariable(smRegionCode), + mac_gestalts.sysvers, NULL); + if (charset != CS_NONE) { + len = charset_to_unicode(&tptr, &tlen, utxt, sizeof(utxt), charset, + NULL, NULL, 0); } } - - switch (event->message & keyCodeMask) { - case K_UP: - p += sprintf((char *)p, - s->term->app_cursor_keys ? "\x1BOA" : "\x1B[A"); - return p - output; - case K_DOWN: - p += sprintf((char *)p, - s->term->app_cursor_keys ? "\x1BOB" : "\x1B[B"); - return p - output; - case K_RIGHT: - p += sprintf((char *)p, - s->term->app_cursor_keys ? "\x1BOC" : "\x1B[C"); - return p - output; - case K_LEFT: - p += sprintf((char *)p, - s->term->app_cursor_keys ? "\x1BOD" : "\x1B[D"); - return p - output; - case KP_ENTER: - *p++ = 0x0d; - return p - output; - case K_BS: - *p++ = (s->cfg.bksp_is_delete ? 0x7f : 0x08); - return p - output; - default: - *p++ = event->message & charCodeMask; - return p - output; - } + term_key(s->term, keysym, utxt, len, mods, flags); } void request_paste(void *frontend) @@ -712,30 +911,142 @@ void request_paste(void *frontend) term_do_paste(s->term); } -void mac_growterm(WindowPtr window, EventRecord *event) { +static struct { + Rect msgrect; + Point msgorigin; + Point zeromouse; + Session *s; + char oldmsg[20]; +} growterm_state; + +static void mac_growterm(WindowPtr window, EventRecord *event) +{ Rect limits; long grow_result; int newrows, newcols; Session *s; +#if !TARGET_API_MAC_CARBON + DragGrayRgnUPP draghooksave; + GrafPtr portsave; + FontInfo fi; +#endif + + s = mac_windowsession(window); + +#if !TARGET_API_MAC_CARBON + draghooksave = LMGetDragHook(); + growterm_state.oldmsg[0] = '\0'; + growterm_state.zeromouse = event->where; + growterm_state.zeromouse.h -= s->term->cols * s->font_width; + growterm_state.zeromouse.v -= s->term->rows * s->font_height; + growterm_state.s = s; + GetPort(&portsave); + SetPort(s->window); + BackColor(whiteColor); + ForeColor(blackColor); + TextFont(systemFont); + TextFace(0); + TextSize(12); + GetFontInfo(&fi); + SetRect(&growterm_state.msgrect, 0, 0, + StringWidth("\p99999x99999") + 4, fi.ascent + fi.descent + 4); + SetPt(&growterm_state.msgorigin, 2, fi.ascent + 2); + LMSetDragHook(NewDragGrayRgnUPP(mac_growtermdraghook)); +#endif - s = (Session *)GetWRefCon(window); SetRect(&limits, s->font_width + 15, s->font_height, SHRT_MAX, SHRT_MAX); grow_result = GrowWindow(window, event->where, &limits); + +#if !TARGET_API_MAC_CARBON + DisposeDragGrayRgnUPP(LMGetDragHook()); + LMSetDragHook(draghooksave); + InvalRect(&growterm_state.msgrect); + + SetPort(portsave); +#endif + if (grow_result != 0) { newrows = HiWord(grow_result) / s->font_height; newcols = (LoWord(grow_result) - 15) / s->font_width; mac_adjustsize(s, newrows, newcols); term_size(s->term, newrows, newcols, s->cfg.savelines); + s->cfg.height=s->term->rows; + s->cfg.width=s->term->cols; + } +} + +#if !TARGET_API_MAC_CARBON +static pascal void mac_growtermdraghook(void) +{ + Session *s = growterm_state.s; + GrafPtr portsave; + Point mouse; + char buf[20]; + unsigned char pbuf[20]; + int newrows, newcols; + + GetMouse(&mouse); + newrows = (mouse.v - growterm_state.zeromouse.v) / s->font_height; + if (newrows < 1) newrows = 1; + newcols = (mouse.h - growterm_state.zeromouse.h) / s->font_width; + if (newcols < 1) newcols = 1; + sprintf(buf, "%dx%d", newcols, newrows); + if (strcmp(buf, growterm_state.oldmsg) == 0) + return; + strcpy(growterm_state.oldmsg, buf); + c2pstrcpy(pbuf, buf); + + GetPort(&portsave); + SetPort(growterm_state.s->window); + EraseRect(&growterm_state.msgrect); + MoveTo(growterm_state.msgorigin.h, growterm_state.msgorigin.v); + DrawString(pbuf); + SetPort(portsave); +} +#endif + +void mac_closeterm(WindowPtr window) +{ + int alertret; + Session *s = mac_windowsession(window); + + if (s->cfg.warn_on_close && !s->session_closed) { + ParamText("\pAre you sure you want to close this session?", + NULL, NULL, NULL); + alertret=CautionAlert(wQuestion, NULL); + if (alertret == 2) { + /* Cancel */ + return; + } } + + HideWindow(s->window); + *s->prev = s->next; + s->next->prev = s->prev; + if (s->ldisc) + ldisc_free(s->ldisc); + if (s->back) + s->back->free(s->backhandle); + log_free(s->logctx); + if (s->uni_to_font != NULL) + DisposeUnicodeToTextInfo(&s->uni_to_font); + term_free(s->term); + mac_freeeventlog(s); + sfree((WinInfo *)GetWRefCon(s->window)); + DisposeWindow(s->window); + DisposePalette(s->palette); + sfree(s); } -void mac_activateterm(WindowPtr window, Boolean active) { +static void mac_activateterm(WindowPtr window, EventRecord *event) +{ Session *s; + Boolean active = (event->modifiers & activeFlag) != 0; - s = (Session *)GetWRefCon(window); - s->term->has_focus = active; + s = mac_windowsession(window); + term_set_focus(s->term, active); term_update(s->term); - if (active) + if (active && s->cfg.scrollbar) ShowControl(s->scrollbar); else { if (HAVE_COLOR_QD()) @@ -747,18 +1058,27 @@ void mac_activateterm(WindowPtr window, Boolean active) { mac_drawgrowicon(s); } -void mac_updateterm(WindowPtr window) { +static void mac_updateterm(WindowPtr window) +{ Session *s; + Rect bbox; +#if TARGET_API_MAC_CARBON + RgnHandle visrgn; +#endif - s = (Session *)GetWRefCon(window); - SetPort(window); + s = mac_windowsession(window); + SetPort((GrafPtr)GetWindowPort(window)); BeginUpdate(window); pre_paint(s); - term_paint(s->term, s, - PTOCC((*window->visRgn)->rgnBBox.left), - PTOCR((*window->visRgn)->rgnBBox.top), - PTOCC((*window->visRgn)->rgnBBox.right), - PTOCR((*window->visRgn)->rgnBBox.bottom), 1); +#if TARGET_API_MAC_CARBON + visrgn = NewRgn(); + GetPortVisibleRegion(GetWindowPort(window), visrgn); + GetRegionBounds(visrgn, &bbox); +#else + bbox = (*window->visRgn)->rgnBBox; +#endif + term_paint(s->term, s, PTOCC(bbox.left), PTOCR(bbox.top), + PTOCC(bbox.right), PTOCR(bbox.bottom), 1); /* Restore default colours in case the Window Manager uses them */ if (HAVE_COLOR_QD()) { PmForeColor(DEFAULT_FG); @@ -768,34 +1088,51 @@ void mac_updateterm(WindowPtr window) { BackColor(blackColor); } if (FrontWindow() != window) +#if TARGET_API_MAC_CARBON + EraseRect(GetControlBounds(s->scrollbar, &bbox)); + UpdateControls(window, visrgn); + DisposeRgn(visrgn); +#else EraseRect(&(*s->scrollbar)->contrlRect); UpdateControls(window, window->visRgn); +#endif mac_drawgrowicon(s); post_paint(s); EndUpdate(window); } -static void mac_drawgrowicon(Session *s) { +static void mac_drawgrowicon(Session *s) +{ Rect clip; + RgnHandle savergn; - SetPort(s->window); - /* Stop DrawGrowIcon giving us space for a horizontal scrollbar */ - SetRect(&clip, s->window->portRect.right - 15, SHRT_MIN, - SHRT_MAX, SHRT_MAX); + SetPort((GrafPtr)GetWindowPort(s->window)); + /* + * Stop DrawGrowIcon giving us space for a horizontal scrollbar + * See Tech Note TB575 for details. + */ +#if TARGET_API_MAC_CARBON + GetPortBounds(GetWindowPort(s->window), &clip); +#else + clip = s->window->portRect; +#endif + clip.left = clip.right - 15; + savergn = NewRgn(); + GetClip(savergn); ClipRect(&clip); DrawGrowIcon(s->window); - clip.left = SHRT_MIN; - ClipRect(&clip); + SetClip(savergn); + DisposeRgn(savergn); } struct do_text_args { Session *s; Rect textrect; - Rect leadrect; char *text; int len; unsigned long attr; int lattr; + Point numer, denom; }; /* @@ -803,43 +1140,98 @@ struct do_text_args { * * x and y are text row and column (zero-based) */ -void do_text(Context ctx, int x, int y, char *text, int len, - unsigned long attr, int lattr) { +static void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, + unsigned long attr, int lattr) +{ Session *s = ctx; - int style = 0; + int style; struct do_text_args a; - RgnHandle textrgn; + RgnHandle textrgn, saveclip; +#if TARGET_API_MAC_CARBON + RgnHandle visrgn; +#endif + char mactextbuf[1024]; + wchar_t *unitextptr; + int fontwidth; + ByteCount iread, olen; + OSStatus err; + static DeviceLoopDrawingUPP do_text_for_device_upp = NULL; + + assert(len <= 1024); + + SetPort((GrafPtr)GetWindowPort(s->window)); + + fontwidth = s->font_width; + if ((lattr & LATTR_MODE) != LATTR_NORM) + fontwidth *= 2; - SetPort(s->window); - /* First check this text is relevant */ a.textrect.top = y * s->font_height; a.textrect.bottom = (y + 1) * s->font_height; - a.textrect.left = x * s->font_width; - a.textrect.right = (x + len) * s->font_width; + a.textrect.left = x * fontwidth; + a.textrect.right = (x + len) * fontwidth; + if (a.textrect.right > s->term->cols * s->font_width) + a.textrect.right = s->term->cols * s->font_width; +#if TARGET_API_MAC_CARBON + visrgn = NewRgn(); + GetPortVisibleRegion(GetWindowPort(s->window), visrgn); + if (!RectInRgn(&a.textrect, visrgn)) { + DisposeRgn(visrgn); + return; + } + DisposeRgn(visrgn); +#else if (!RectInRgn(&a.textrect, s->window->visRgn)) return; +#endif + + if (s->uni_to_font != NULL) { + err = ConvertFromUnicodeToText(s->uni_to_font, len * sizeof(UniChar), + text, kUnicodeUseFallbacksMask, + 0, NULL, NULL, NULL, + 1024, &iread, &olen, mactextbuf); + if (err != noErr && err != kTECUsedFallbacksStatus) + olen = 0; + } else if (s->font_charset != CS_NONE) { + /* XXX this is bogus if wchar_t and UniChar are different sizes. */ + unitextptr = (wchar_t *)text; + olen = charset_from_unicode(&unitextptr, &len, mactextbuf, 1024, + s->font_charset, NULL, ".", 1); + } else + olen = 0; a.s = s; - a.text = text; - a.len = len; + a.text = mactextbuf; + a.len = olen; a.attr = attr; a.lattr = lattr; - if (s->font_leading > 0) - SetRect(&a.leadrect, - a.textrect.left, a.textrect.bottom - s->font_leading, - a.textrect.right, a.textrect.bottom); - else - SetRect(&a.leadrect, 0, 0, 0, 0); - SetPort(s->window); + switch (lattr & LATTR_MODE) { + case LATTR_NORM: + TextSize(s->cfg.font.size); + a.numer = s->font_stdnumer; + a.denom = s->font_stddenom; + break; + case LATTR_WIDE: + TextSize(s->cfg.font.size); + a.numer = s->font_widenumer; + a.denom = s->font_widedenom; + break; + case LATTR_TOP: + case LATTR_BOT: + TextSize(s->cfg.font.size * 2); + a.numer = s->font_bignumer; + a.denom = s->font_bigdenom; + break; + } + SetPort((GrafPtr)GetWindowPort(s->window)); TextFont(s->fontnum); - if (s->cfg.fontisbold || (attr & ATTR_BOLD) && !s->cfg.bold_colour) + style = s->cfg.font.face; + if ((attr & ATTR_BOLD) && !s->cfg.bold_colour) style |= bold; if (attr & ATTR_UNDER) style |= underline; TextFace(style); - TextSize(s->cfg.fontheight); - SetFractEnable(FALSE); /* We want characters on pixel boundaries */ + TextMode(srcOr); if (HAVE_COLOR_QD()) if (style & bold) { SpaceExtra(s->font_boldadjust << 16); @@ -848,72 +1240,121 @@ void do_text(Context ctx, int x, int y, char *text, int len, SpaceExtra(0); CharExtra(0); } + saveclip = NewRgn(); + GetClip(saveclip); + ClipRect(&a.textrect); textrgn = NewRgn(); RectRgn(textrgn, &a.textrect); - if (HAVE_COLOR_QD()) - DeviceLoop(textrgn, &do_text_for_device_upp, (long)&a, 0); - else + if (HAVE_COLOR_QD()) { + if (do_text_for_device_upp == NULL) + do_text_for_device_upp = + NewDeviceLoopDrawingUPP(&do_text_for_device); + DeviceLoop(textrgn, do_text_for_device_upp, (long)&a, 0); + } else do_text_for_device(1, 0, NULL, (long)&a); + SetClip(saveclip); + DisposeRgn(saveclip); DisposeRgn(textrgn); /* Tell the window manager about it in case this isn't an update */ +#if TARGET_API_MAC_CARBON + ValidWindowRect(s->window, &a.textrect); +#else ValidRect(&a.textrect); +#endif } -static pascal void do_text_for_device(short depth, short devflags, - GDHandle device, long cookie) { - struct do_text_args *a; - int bgcolour, fgcolour, bright; +/* + * Wrapper that handles combining characters. + */ +void do_text(Context ctx, int x, int y, wchar_t *text, int len, + unsigned long attr, int lattr) +{ + if (attr & TATTR_COMBINING) { + unsigned long a = 0; + attr &= ~TATTR_COMBINING; + while (len--) { + do_text_internal(ctx, x, y, text, 1, attr | a, lattr); + text++; + a = TATTR_COMBINING; + } + } else + do_text_internal(ctx, x, y, text, len, attr, lattr); +} - a = (struct do_text_args *)cookie; +static pascal void do_text_for_device(short depth, short devflags, + GDHandle device, long cookie) +{ + struct do_text_args *a = (struct do_text_args *)cookie; + int bgcolour, fgcolour, bright, reverse, tmp; +#if TARGET_API_MAC_CARBON + CQDProcsPtr gp = GetPortGrafProcs(GetWindowPort(a->s->window)); +#else + QDProcsPtr gp = a->s->window->grafProcs; +#endif bright = (a->attr & ATTR_BOLD) && a->s->cfg.bold_colour; + reverse = a->attr & ATTR_REVERSE; - TextMode(a->attr & ATTR_REVERSE ? notSrcCopy : srcCopy); + if (depth == 1 && (a->attr & TATTR_ACTCURS)) + reverse = !reverse; - switch (depth) { - case 1: - /* XXX This should be done with a _little_ more configurability */ - ForeColor(whiteColor); - BackColor(blackColor); - if (a->attr & TATTR_ACTCURS) - TextMode(a->attr & ATTR_REVERSE ? srcCopy : notSrcCopy); - break; - case 2: - if (a->attr & TATTR_ACTCURS) { - PmForeColor(CURSOR_FG); - PmBackColor(CURSOR_BG); - TextMode(srcCopy); + if (HAVE_COLOR_QD()) { + if (depth > 2) { + fgcolour = ((a->attr & ATTR_FGMASK) >> ATTR_FGSHIFT); + bgcolour = ((a->attr & ATTR_BGMASK) >> ATTR_BGSHIFT); } else { - PmForeColor(bright ? DEFAULT_FG_BOLD : DEFAULT_FG); - PmBackColor(DEFAULT_BG); + /* + * NB: bold reverse in 2bpp breaks with the usual PuTTY model and + * boldens the background, because that's all we can do. + */ + fgcolour = bright ? DEFAULT_FG_BOLD : DEFAULT_FG; + bgcolour = DEFAULT_BG; } - break; - default: - if (a->attr & TATTR_ACTCURS) { + if (reverse) { + tmp = fgcolour; + fgcolour = bgcolour; + bgcolour = tmp; + } + if (bright && depth > 2) + if (fgcolour < 16) fgcolour |=8; + else if (fgcolour >= 256) fgcolour |=1; + if ((a->attr & TATTR_ACTCURS) && depth > 1) { fgcolour = CURSOR_FG; bgcolour = CURSOR_BG; - TextMode(srcCopy); - } else { - fgcolour = ((a->attr & ATTR_FGMASK) >> ATTR_FGSHIFT) * 2; - bgcolour = ((a->attr & ATTR_BGMASK) >> ATTR_BGSHIFT) * 2; - if (bright) - if (a->attr & ATTR_REVERSE) - bgcolour++; - else - fgcolour++; } PmForeColor(fgcolour); PmBackColor(bgcolour); - break; + } else { /* No Color Quickdraw */ + /* XXX This should be done with a _little_ more configurability */ + if (reverse) { + ForeColor(blackColor); + BackColor(whiteColor); + } else { + ForeColor(whiteColor); + BackColor(blackColor); + } } - if (a->attr & ATTR_REVERSE) - PaintRect(&a->leadrect); - else - EraseRect(&a->leadrect); - MoveTo(a->textrect.left, a->textrect.top + a->s->font_ascent); + if (!(a->attr & TATTR_COMBINING)) + EraseRect(&a->textrect); + switch (a->lattr & LATTR_MODE) { + case LATTR_NORM: + case LATTR_WIDE: + MoveTo(a->textrect.left, a->textrect.top + a->s->font_ascent); + break; + case LATTR_TOP: + MoveTo(a->textrect.left, a->textrect.top + a->s->font_ascent * 2); + break; + case LATTR_BOT: + MoveTo(a->textrect.left, + a->textrect.top - a->s->font_height + a->s->font_ascent * 2); + break; + } /* FIXME: Sort out bold width adjustments on Original QuickDraw. */ - DrawText(a->text, 0, a->len); + if (gp != NULL) + InvokeQDTextUPP(a->len, a->text, a->numer, a->denom, gp->textProc); + else + StdText(a->len, a->text, a->numer, a->denom); if (a->attr & TATTR_PASCURS) { PenNormal(); @@ -929,7 +1370,7 @@ static pascal void do_text_for_device(short depth, short devflags, } } -void do_cursor(Context ctx, int x, int y, char *text, int len, +void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, unsigned long attr, int lattr) { @@ -940,50 +1381,71 @@ void do_cursor(Context ctx, int x, int y, char *text, int len, * Call from the terminal emulator to get its graphics context. * Should probably be called start_redraw or something. */ -void pre_paint(Session *s) { +void pre_paint(Session *s) +{ + GDHandle gdh; + Rect myrect, tmprect; +#if TARGET_API_MAC_CARBON + RgnHandle visrgn; +#endif - s->attr_mask = ATTR_INVALID; - if (HAVE_COLOR_QD()) - DeviceLoop(s->window->visRgn, &mac_set_attr_mask_upp, (long)s, 0); - else - mac_set_attr_mask(1, 0, NULL, (long)s); + if (HAVE_COLOR_QD()) { + s->term->attr_mask = 0; + SetPort((GrafPtr)GetWindowPort(s->window)); +#if TARGET_API_MAC_CARBON + visrgn = NewRgn(); + GetPortVisibleRegion(GetWindowPort(s->window), visrgn); + GetRegionBounds(visrgn, &myrect); + DisposeRgn(visrgn); +#else + myrect = (*s->window->visRgn)->rgnBBox; +#endif + LocalToGlobal((Point *)&myrect.top); + LocalToGlobal((Point *)&myrect.bottom); + for (gdh = GetDeviceList(); + gdh != NULL; + gdh = GetNextDevice(gdh)) { + if (TestDeviceAttribute(gdh, screenDevice) && + TestDeviceAttribute(gdh, screenActive) && + SectRect(&(*gdh)->gdRect, &myrect, &tmprect)) { + switch ((*(*gdh)->gdPMap)->pixelSize) { + case 1: + if (s->cfg.bold_colour) + s->term->attr_mask |= ~(ATTR_COLOURS | + (s->cfg.bold_colour ? ATTR_BOLD : 0)); + break; + case 2: + s->term->attr_mask |= ~ATTR_COLOURS; + break; + default: + s->term->attr_mask = ~0; + return; /* No point checking more screens. */ + } + } + } + } else + s->term->attr_mask = ~(ATTR_COLOURS | + (s->cfg.bold_colour ? ATTR_BOLD : 0)); } -Context get_ctx(void *frontend) { +Context get_ctx(void *frontend) +{ Session *s = frontend; pre_paint(s); return s; } -void free_ctx(Context ctx) { +void free_ctx(Context ctx) +{ } -static pascal void mac_set_attr_mask(short depth, short devflags, - GDHandle device, long cookie) { - - Session *s = (Session *)cookie; - - switch (depth) { - default: - s->attr_mask |= ATTR_FGMASK | ATTR_BGMASK; - /* FALLTHROUGH */ - case 2: - s->attr_mask |= ATTR_BOLD; - /* FALLTHROUGH */ - case 1: - s->attr_mask |= ATTR_UNDER | ATTR_REVERSE | TATTR_ACTCURS | - TATTR_PASCURS | ATTR_ASCII | ATTR_GBCHR | ATTR_LINEDRW | - (s->cfg.bold_colour ? 0 : ATTR_BOLD); - break; - } -} - /* * Presumably this does something in Windows */ -void post_paint(Session *s) { +void post_paint(Session *s) +{ } @@ -994,15 +1456,15 @@ void post_paint(Session *s) { * start is the line number of the top of the display * page is the length of the displayed page */ -void set_sbar(void *frontend, int total, int start, int page) { +void set_sbar(void *frontend, int total, int start, int page) +{ Session *s = frontend; /* We don't redraw until we've set everything up, to avoid glitches */ - (*s->scrollbar)->contrlMin = 0; - (*s->scrollbar)->contrlMax = total - page; + SetControlMinimum(s->scrollbar, 0); + SetControlMaximum(s->scrollbar, total - page); SetControlValue(s->scrollbar, start); -#if TARGET_RT_CFM - /* XXX: This doesn't link for me. */ +#if !TARGET_CPU_68K if (mac_gestalts.cntlattr & gestaltControlMgrPresent) SetControlViewSize(s->scrollbar, page); #endif @@ -1021,7 +1483,7 @@ void sys_cursor(void *frontend, int x, int y) * may want to perform additional actions on any kind of bell (for * example, taskbar flashing in Windows). */ -void beep(void *frontend, int mode) +void do_beep(void *frontend, int mode) { if (mode != BELL_VISUAL) SysBeep(30); @@ -1051,15 +1513,24 @@ void set_icon(void *frontend, char *icon) { /* * Set the window title */ -void set_title(void *frontend, char *title) { +void set_title(void *frontend, char *title) +{ Session *s = frontend; Str255 mactitle; - mactitle[0] = sprintf((char *)&mactitle[1], "%s", title); + c2pstrcpy(mactitle, title); SetWTitle(s->window, mactitle); } /* + * Used by backend to indicate busy-ness + */ +void set_busy_status(void *frontend, int status) +{ + /* FIXME do something */ +} + +/* * set or clear the "raw mouse message" mode */ void set_raw_mouse_mode(void *frontend, int activate) @@ -1073,11 +1544,25 @@ void set_raw_mouse_mode(void *frontend, int activate) /* * Resize the window at the emulator's request */ -void request_resize(void *frontend, int w, int h) { +void request_resize(void *frontend, int w, int h) +{ Session *s = frontend; - - s->term->cols = w; - s->term->rows = h; + RgnHandle grayrgn; + Rect graybox; + int wlim, hlim; + + /* Arbitrarily clip to the size of the desktop. */ + grayrgn = GetGrayRgn(); +#if TARGET_API_MAC_CARBON + GetRegionBounds(grayrgn, &graybox); +#else + graybox = (*grayrgn)->rgnBBox; +#endif + wlim = (graybox.right - graybox.left) / s->font_width; + hlim = (graybox.bottom - graybox.top) / s->font_height; + if (w > wlim) w = wlim; + if (h > hlim) h = hlim; + term_size(s->term, h, w, s->cfg.savelines); mac_initfont(s); } @@ -1166,9 +1651,15 @@ int is_iconic(void *frontend) void get_window_pos(void *frontend, int *x, int *y) { Session *s = frontend; + Rect rect; - *x = s->window->portRect.left; - *y = s->window->portRect.top; +#if TARGET_API_MAC_CARBON + GetPortBounds(GetWindowPort(s->window), &rect); +#else + rect = s->window->portRect; +#endif + *x = rect.left; + *y = rect.top; } /* @@ -1177,9 +1668,15 @@ void get_window_pos(void *frontend, int *x, int *y) void get_window_pixels(void *frontend, int *x, int *y) { Session *s = frontend; + Rect rect; - *x = s->window->portRect.right - s->window->portRect.left; - *y = s->window->portRect.bottom - s->window->portRect.top; +#if TARGET_API_MAC_CARBON + GetPortBounds(GetWindowPort(s->window), &rect); +#else + rect = s->window->portRect; +#endif + *x = rect.right - rect.left; + *y = rect.bottom - rect.top; } /* @@ -1188,9 +1685,12 @@ void get_window_pixels(void *frontend, int *x, int *y) char *get_window_title(void *frontend, int icon) { Session *s = frontend; + Str255 ptitle; + static char title[256]; - /* Erm, we don't save this at the moment */ - return ""; + GetWTitle(s->window, ptitle); + p2cstrcpy(title, ptitle); + return title; } /* @@ -1213,48 +1713,59 @@ static void real_palette_set(Session *s, int n, int r, int g, int b) /* * Set the logical palette. Called by the terminal emulator. */ -void palette_set(void *frontend, int n, int r, int g, int b) { +void palette_set(void *frontend, int n, int r, int g, int b) +{ Session *s = frontend; - static const int first[21] = { - 0, 2, 4, 6, 8, 10, 12, 14, - 1, 3, 5, 7, 9, 11, 13, 15, - 16, 17, 18, 20, 21 - }; if (!HAVE_COLOR_QD()) return; - real_palette_set(s, first[n], r, g, b); - if (first[n] == 18) - real_palette_set(s, first[n]+1, r, g, b); - if (first[n] == DEFAULT_BG) - mac_adjustwinbg(s); + real_palette_set(s, n, r, g, b); + if (n == DEFAULT_BG) + mac_adjustwinbg(s); + ActivatePalette(s->window); } /* * Reset to the default palette */ -void palette_reset(void *frontend) { +void palette_reset(void *frontend) +{ Session *s = frontend; /* This maps colour indices in cfg to those used in our palette. */ static const int ww[] = { - 6, 7, 8, 9, 10, 11, 12, 13, - 14, 15, 16, 17, 18, 19, 20, 21, - 0, 1, 2, 3, 4, 5 + 256, 257, 258, 259, 260, 261, + 0, 8, 1, 9, 2, 10, 3, 11, + 4, 12, 5, 13, 6, 14, 7, 15 }; + int i; if (!HAVE_COLOR_QD()) return; - assert(lenof(ww) == NCOLOURS); - - for (i = 0; i < NCOLOURS; i++) { - real_palette_set(s, i, - s->cfg.colours[ww[i]][0], - s->cfg.colours[ww[i]][1], - s->cfg.colours[ww[i]][2]); + for (i = 0; i < 22; i++) { + int w = ww[i]; + real_palette_set(s,w, + s->cfg.colours[i][0], + s->cfg.colours[i][1], + s->cfg.colours[i][2]); } + for (i = 0; i < 240; i++) { + if (i < 216) { + int r = i / 36, g = (i / 6) % 6, b = i % 6; + real_palette_set(s,i+16, + r * 0x33, + g * 0x33, + b * 0x33); + } else { + int shade = i - 216; + shade = (shade + 1) * 0xFF / (240 - 216 + 1); + real_palette_set(s,i+16,shade,shade,shade); + } + } + + mac_adjustwinbg(s); ActivatePalette(s->window); /* Palette Manager will generate update events as required. */ @@ -1264,29 +1775,64 @@ void palette_reset(void *frontend) { * Scroll the screen. (`lines' is +ve for scrolling forward, -ve * for backward.) */ -void do_scroll(void *frontend, int topline, int botline, int lines) { - Session *s = frontend; +void do_scroll(Context ctx, int topline, int botline, int lines) +{ + Session *s = ctx; Rect r; - RgnHandle update; + RgnHandle scrollrgn = NewRgn(); + RgnHandle movedupdate = NewRgn(); + RgnHandle update = NewRgn(); + Point g2l = { 0, 0 }; - /* FIXME: This is seriously broken on Original QuickDraw. No idea why. */ - SetPort(s->window); + SetPort((GrafPtr)GetWindowPort(s->window)); + + /* + * Work out the part of the update region that will scrolled by + * this operation. + */ + if (lines > 0) + SetRectRgn(scrollrgn, 0, (topline + lines) * s->font_height, + s->term->cols * s->font_width, + (botline + 1) * s->font_height); + else + SetRectRgn(scrollrgn, 0, topline * s->font_height, + s->term->cols * s->font_width, + (botline - lines + 1) * s->font_height); +#if TARGET_API_MAC_CARBON + GetWindowRegion(s->window, kWindowUpdateRgn, movedupdate); +#else + GetWindowUpdateRgn(s->window, movedupdate); +#endif + GlobalToLocal(&g2l); + OffsetRgn(movedupdate, g2l.h, g2l.v); /* Convert to local co-ords. */ + SectRgn(scrollrgn, movedupdate, movedupdate); /* Clip scrolled section. */ +#if TARGET_API_MAC_CARBON + ValidWindowRgn(s->window, movedupdate); +#else + ValidRgn(movedupdate); +#endif + OffsetRgn(movedupdate, 0, -lines * s->font_height); /* Scroll it. */ + + PenNormal(); if (HAVE_COLOR_QD()) PmBackColor(DEFAULT_BG); else - BackColor(blackColor); - update = NewRgn(); + BackColor(blackColor); /* XXX make configurable */ SetRect(&r, 0, topline * s->font_height, s->term->cols * s->font_width, (botline + 1) * s->font_height); ScrollRect(&r, 0, - lines * s->font_height, update); - /* XXX: move update region? */ - InvalRgn(update); - DisposeRgn(update); -} -void logevent(void *frontend, char *str) { +#if TARGET_API_MAC_CARBON + InvalWindowRgn(s->window, update); + InvalWindowRgn(s->window, movedupdate); +#else + InvalRgn(update); + InvalRgn(movedupdate); +#endif - /* XXX Do something */ + DisposeRgn(scrollrgn); + DisposeRgn(movedupdate); + DisposeRgn(update); } /* Dummy routine, only required in plink. */ @@ -1294,6 +1840,12 @@ void ldisc_update(void *frontend, int echo, int edit) { } +char *get_ttymode(void *frontend, const char *mode) +{ + Session *s = frontend; + return term_get_ttymode(s->term, mode); +} + /* * Mac PuTTY doesn't support printing yet. */ @@ -1326,13 +1878,27 @@ void frontend_keypress(void *handle) * Ask whether to wipe a session log file before writing to it. * Returns 2 for wipe, 1 for append, 0 for cancel (don't log). */ -int askappend(void *frontend, char *filename) +int askappend(void *frontend, Filename filename, + void (*callback)(void *ctx, int result), void *ctx) { /* FIXME: not implemented yet. */ return 2; } +int from_backend(void *frontend, int is_stderr, const char *data, int len) +{ + Session *s = frontend; + + return term_data(s->term, is_stderr, data, len); +} + +int get_userpass_input(prompts_t *p, unsigned char *in, int inlen) +{ + Session *s = p->frontend; + return term_get_userpass_input(s->term, p, in, inlen); +} + /* * Emacs magic: * Local Variables: