Add the ability to close sessions. This adds *_free() functions to most
[u/mdw/putty] / mac / macterm.c
index 299debf..02232a5 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: macterm.c,v 1.12 2002/11/26 01:32:51 ben Exp $ */
+/* $Id: macterm.c,v 1.50 2003/01/15 23:30:21 ben Exp $ */
 /*
  * Copyright (c) 1999 Simon Tatham
  * Copyright (c) 1999, 2002 Ben Harris
@@ -35,6 +35,7 @@
 #include <ControlDefinitions.h>
 #include <Fonts.h>
 #include <Gestalt.h>
+#include <LowMem.h>
 #include <MacMemory.h>
 #include <MacWindows.h>
 #include <MixedMode.h>
 #include <Scrap.h>
 #include <Script.h>
 #include <Sound.h>
+#include <StandardFile.h>
+#include <TextCommon.h>
 #include <Threads.h>
 #include <ToolUtils.h>
+#include <UnicodeConverter.h>
 
 #include <assert.h>
 #include <limits.h>
@@ -56,7 +60,9 @@
 
 #include "macresid.h"
 #include "putty.h"
+#include "charset.h"
 #include "mac.h"
+#include "storage.h"
 #include "terminal.h"
 
 #define NCOLOURS (lenof(((Config *)0)->colours))
                            (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 text_click(Session *, EventRecord *);
 
@@ -94,57 +104,55 @@ static RoutineDescriptor mac_scrolltracker_upp =
 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;
+void mac_opensession(void) {
+    Session *s;
+    StandardFileReply sfr;
+    static const OSType sftypes[] = { 'Sess', 0, 0, 0 };
+    void *sesshandle;
+    int i;
 
-    from_backend(s->term, 0, &ch, 1);
-}
+    s = smalloc(sizeof(*s));
+    memset(s, 0, sizeof(*s));
 
-static void inbuf_putstr(Session *s, const char *c) {
+    StandardGetFile(NULL, 1, sftypes, &sfr);
+    if (!sfr.sfGood) goto fail;
 
-    from_backend(s->term, 0, (char *)c, strlen(c));
-}
+    sesshandle = open_settings_r_fsp(&sfr.sfFile);
+    if (sesshandle == NULL) goto fail;
+    load_open_settings(sesshandle, TRUE, &s->cfg);
+    close_settings_r(sesshandle);
 
-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);
+    /*
+     * Select protocol. This is farmed out into a table in a
+     * separate file to enable an ssh-free variant.
+     */
+    s->back = NULL;
+    for (i = 0; backends[i].backend != NULL; i++)
+       if (backends[i].protocol == s->cfg.protocol) {
+           s->back = backends[i].backend;
+           break;
+       }
+    if (s->back == NULL) {
+       fatalbox("Unsupported protocol number found");
+    }
+    mac_startsession(s);
+    return;
+
+  fail:
+    sfree(s);
+    return;
 }
-       
 
-void mac_newsession(void) {
-    Session *s;
-    UInt32 starttime;
-    char msg[128];
-    OSErr err;
+void mac_startsession(Session *s)
+{
+    char *errmsg;
 
-    /* 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;
-       
+    init_ucs(s);
     /* XXX: Own storage management? */
     if (HAVE_COLOR_QD())
        s->window = GetNewCWindow(wTerminal, NULL, (WindowPtr)-1);
@@ -152,13 +160,25 @@ void mac_newsession(void) {
        s->window = GetNewWindow(wTerminal, NULL, (WindowPtr)-1);
     SetWRefCon(s->window, (long)s);
     s->scrollbar = GetNewControl(cVScroll, s->window);
-    s->term = term_init(&s->cfg, s);
+    s->term = term_init(&s->cfg, &s->ucsdata, 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);
+    s->logctx = log_init(s, &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->term, &s->backhandle, &s->cfg, s->cfg.host,
+                          s->cfg.port, &s->realhost, s->cfg.tcp_nodelay);
+    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);
 
@@ -168,28 +188,59 @@ void mac_newsession(void) {
     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;
+}
+
+/*
+ * 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;
+
+    numer.v = denom.v = 1; /* always */
+    numer.h = denom.h = 1;
+    for (i = 0; i < 3; i++) {
+       tmpnumer = numer;
+       tmpdenom = denom;
+       if (s->window->grafProcs != NULL)
+           gotwidth = InvokeQDTxMeasUPP(1, &text, &tmpnumer, &tmpdenom, &fi,
+                                        s->window->grafProcs->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) {
     Str255 macfont;
     FontInfo fi;
+    TextEncoding enc;
+    OptionBits fbflags;
+
     SetPort(s->window);
-    macfont[0] = sprintf((char *)&macfont[1], "%s", s->cfg.font);
+    c2pstrcpy(macfont, s->cfg.font);
     GetFNum(macfont, &s->fontnum);
     TextFont(s->fontnum);
     TextFace(s->cfg.fontisbold ? bold : 0);
@@ -199,14 +250,83 @@ static void mac_initfont(Session *s) {
     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.fontheight * 2);
+    mac_workoutfontscale(s, s->font_width * 2,
+                        &s->font_bignumer, &s->font_bigdenom);
+    TextSize(s->cfg.fontheight);
     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, macfont,
+                                       &enc) == noErr &&
+       CreateUnicodeToTextInfoByEncoding(enc, &s->uni_to_font) == noErr) {
+       if (uni_to_font_fallback_upp == NULL)
+           uni_to_font_fallback_upp =
+               NewUnicodeToTextFallbackProc(&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 {
+      no_encv:
+       s->uni_to_font = NULL;
+       s->font_charset =
+           charset_from_macenc(FontToScript(s->fontnum),
+                               GetScriptManagerVariable(smRegionCode),
+                               mac_gestalts.sysvers, s->cfg.font);
+    }
+
     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;
+}
+
+/*
+ * Called every time round the event loop.
+ */
+void mac_pollterm(void)
+{
+    Session *s;
+
+    for (s = sesslist; s != NULL; s = s->next) {
+       term_out(s->term);
+       term_update(s->term);
+    }
+}
+
 /*
  * To be called whenever the window size changes.
  * rows and cols should be desired values.
@@ -223,10 +343,10 @@ static void mac_adjustsize(Session *s, int newrows, int newcols) {
     MoveControl(s->scrollbar, winwidth - 15, -1);
     SizeControl(s->scrollbar, 16, winheight - 13);
     ShowControl(s->scrollbar);
+    mac_drawgrowicon(s);
 }
 
 static void mac_initpalette(Session *s) {
-    int i;
 
     if (!HAVE_COLOR_QD())
        return;
@@ -261,7 +381,7 @@ 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);
@@ -714,15 +834,53 @@ void request_paste(void *frontend)
     term_do_paste(s->term);
 }
 
+static struct {
+    Rect msgrect;
+    Point msgorigin;
+    Point zeromouse;
+    Session *s;
+    char oldmsg[20];
+} growterm_state;
+
 void mac_growterm(WindowPtr window, EventRecord *event) {
     Rect limits;
     long grow_result;
     int newrows, newcols;
     Session *s;
+    DragGrayRgnUPP draghooksave;
+    GrafPtr portsave;
+    FontInfo fi;
 
     s = (Session *)GetWRefCon(window);
+
+    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));
+
     SetRect(&limits, s->font_width + 15, s->font_height, SHRT_MAX, SHRT_MAX);
     grow_result = GrowWindow(window, event->where, &limits);
+
+    DisposeDragGrayRgnUPP(LMGetDragHook());
+    LMSetDragHook(draghooksave);
+    InvalRect(&growterm_state.msgrect);
+
+    SetPort(portsave);
+
     if (grow_result != 0) {
        newrows = HiWord(grow_result) / s->font_height;
        newcols = (LoWord(grow_result) - 15) / s->font_width;
@@ -731,6 +889,53 @@ void mac_growterm(WindowPtr window, EventRecord *event) {
     }
 }
 
+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);
+}
+
+void mac_closeterm(WindowPtr window)
+{
+    Session *s = (Session *)GetWRefCon(window);
+
+    /* XXX warn on close */
+    HideWindow(s->window);
+    *s->prev = s->next;
+    s->next->prev = s->prev;
+    ldisc_free(s->ldisc);
+    s->back->free(s->backhandle);
+    log_free(s->logctx);
+    if (s->uni_to_font != NULL)
+       DisposeUnicodeToTextInfo(&s->uni_to_font);
+    term_free(s->term);
+    DisposeWindow(s->window);
+    DisposePalette(s->palette);
+    sfree(s);
+}
+
 void mac_activateterm(WindowPtr window, Boolean active) {
     Session *s;
 
@@ -779,15 +984,21 @@ void mac_updateterm(WindowPtr window) {
 
 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);
+    /*
+     * Stop DrawGrowIcon giving us space for a horizontal scrollbar
+     * See Tech Note TB575 for details.
+     */
+    clip = s->window->portRect;
+    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 {
@@ -797,6 +1008,7 @@ struct do_text_args {
     int len;
     unsigned long attr;
     int lattr;
+    Point numer, denom;
 };
 
 /*
@@ -809,23 +1021,74 @@ void do_text(Context ctx, int x, int y, char *text, int len,
     Session *s = ctx;
     int style = 0;
     struct do_text_args a;
-    RgnHandle textrgn;
+    RgnHandle textrgn, saveclip;
+    char mactextbuf[1024];
+    UniChar unitextbuf[1024];
+    wchar_t *unitextptr;
+    int i, fontwidth;
+    ByteCount iread, olen;
+    OSStatus err;
+
+    assert(len <= 1024);
 
     SetPort(s->window);
-    
+
+    fontwidth = s->font_width;
+    if ((lattr & LATTR_MODE) != LATTR_NORM)
+       fontwidth *= 2;
+
     /* 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 (!RectInRgn(&a.textrect, s->window->visRgn))
        return;
 
+    /* Unpack Unicode from the mad format we get passed */
+    for (i = 0; i < len; i++)
+       unitextbuf[i] = (unsigned char)text[i] | (attr & CSET_MASK);
+
+    if (s->uni_to_font != NULL) {
+       err = ConvertFromUnicodeToText(s->uni_to_font, len * sizeof(UniChar),
+                                      unitextbuf, 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 *)unitextbuf;
+       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;
+    switch (lattr & LATTR_MODE) {
+      case LATTR_NORM:
+       TextSize(s->cfg.fontheight);
+       a.numer = s->font_stdnumer;
+       a.denom = s->font_stddenom;
+       break;
+      case LATTR_WIDE:
+       TextSize(s->cfg.fontheight);
+       a.numer = s->font_widenumer;
+       a.denom = s->font_widedenom;
+       break;
+      case LATTR_TOP:
+      case LATTR_BOT:
+       TextSize(s->cfg.fontheight * 2);
+       a.numer = s->font_bignumer;
+       a.denom = s->font_bigdenom;
+       break;
+    }
     SetPort(s->window);
     TextFont(s->fontnum);
     if (s->cfg.fontisbold || (attr & ATTR_BOLD) && !s->cfg.bold_colour)
@@ -833,9 +1096,7 @@ void do_text(Context ctx, int x, int y, char *text, int len,
     if (attr & ATTR_UNDER)
        style |= underline;
     TextFace(style);
-    TextSize(s->cfg.fontheight);
     TextMode(srcOr);
-    SetFractEnable(FALSE); /* We want characters on pixel boundaries */
     if (HAVE_COLOR_QD())
        if (style & bold) {
            SpaceExtra(s->font_boldadjust << 16);
@@ -844,12 +1105,17 @@ 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
        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 */
     ValidRect(&a.textrect);
@@ -905,9 +1171,25 @@ static pascal void do_text_for_device(short depth, short devflags,
     }
 
     EraseRect(&a->textrect);
-    MoveTo(a->textrect.left, a->textrect.top + a->s->font_ascent);
+    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 (a->s->window->grafProcs != NULL)
+       InvokeQDTextUPP(a->len, a->text, a->numer, a->denom,
+                       a->s->window->grafProcs->textProc);
+    else
+       StdText(a->len, a->text, a->numer, a->denom);
 
     if (a->attr & TATTR_PASCURS) {
        PenNormal();
@@ -935,12 +1217,39 @@ void do_cursor(Context ctx, int x, int y, char *text, int len,
  * Should probably be called start_redraw or something.
  */
 void pre_paint(Session *s) {
+    GDHandle gdh;
+    Rect myrect, tmprect;
 
-    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(s->window);
+       myrect = (*s->window->visRgn)->rgnBBox;
+       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) {
@@ -954,26 +1263,6 @@ 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
  */
@@ -995,8 +1284,7 @@ void set_sbar(void *frontend, int total, int start, int page) {
     (*s->scrollbar)->contrlMin = 0;
     (*s->scrollbar)->contrlMax = 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
@@ -1049,7 +1337,7 @@ 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);
 }
 
@@ -1070,8 +1358,7 @@ void set_raw_mouse_mode(void *frontend, int activate)
 void request_resize(void *frontend, int w, int h) {
     Session *s = frontend;
 
-    s->term->cols = w;
-    s->term->rows = h;
+    term_size(s->term, h, w, s->cfg.savelines);
     mac_initfont(s);
 }
 
@@ -1182,9 +1469,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;
 }
 
 /*
@@ -1261,25 +1551,52 @@ void palette_reset(void *frontend) {
 void do_scroll(void *frontend, int topline, int botline, int lines) {
     Session *s = frontend;
     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);
+
+    /*
+     * 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);
+    CopyRgn(((WindowPeek)s->window)->updateRgn, movedupdate);
+    GlobalToLocal(&g2l);
+    OffsetRgn(movedupdate, g2l.h, g2l.v); /* Convert to local co-ords. */
+    SectRgn(scrollrgn, movedupdate, movedupdate); /* Clip scrolled section. */
+    ValidRgn(movedupdate);
+    OffsetRgn(movedupdate, 0, -lines * s->font_height); /* Scroll it. */
+
     PenNormal();
     if (HAVE_COLOR_QD())
        PmBackColor(DEFAULT_BG);
-    update = NewRgn();
+    else
+       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);
+    InvalRgn(movedupdate);
+
+    DisposeRgn(scrollrgn);
+    DisposeRgn(movedupdate);
     DisposeRgn(update);
 }
 
 void logevent(void *frontend, char *str) {
 
-    /* XXX Do something */
+    fprintf(stderr, "%s\n", str);
 }
 
 /* Dummy routine, only required in plink. */