-- ChipUserIntImpl.mesa
-- a program to run within Chipmonk

-- last modified by E. McCreight, March 8, 1983  1:17 PM
-- written by E. McCreight, August 7, 1981  3:52 PM

DIRECTORY
  ChipUserInt,
  InlineDefs,
  MiscDefs,
  multiGraphicsDefs,
  ppdddefs,
  ppddefs,
  ppdefs,
  ProcessDefs,
  SegmentDefs,
  StringDefs;

ChipUserIntImpl: MONITOR
  IMPORTS InlineDefs, MiscDefs, multiGraphicsDefs,
  ppdddefs, ppddefs, ppdefs, ProcessDefs, SegmentDefs,
  StringDefs
  EXPORTS ChipUserInt =
  BEGIN OPEN ppdddefs, ppddefs, ppdefs;

  DEL: CHARACTER = 177C;
  ESC: CHARACTER = 033C;
  CR: CHARACTER = 015C;
  LF: CHARACTER = 012C;
  BS: CHARACTER = 010C;

  UNDO: CHARACTER = 200C;
  DEBUG: CHARACTER = 201C;
  OTHER: CHARACTER = 007C; -- Ascii BEL

  Punt: PUBLIC SIGNAL = CODE;

  Explain: PUBLIC PROCEDURE [what, why, explanation: LONG STRING ← NIL] =
    BEGIN
    FreeString[RequestString[what, why, explanation]];
    END;

  FixExtension: PUBLIC PROCEDURE [s, ext: STRING] RETURNS [se: STRING] =
    BEGIN
    lastPeriod: CARDINAL ← s.length;
    FOR i: CARDINAL IN [0..s.length) DO
      IF s[i] = '. THEN lastPeriod ← i ENDLOOP;
    se ← GetString[lastPeriod + ext.length];
    FOR i: CARDINAL IN [0..lastPeriod) DO se[i] ← s[i] ENDLOOP;
    se.length ← lastPeriod;
    StringDefs.AppendString[to: se, from: ext];
    FreeString[s];
    END;

  RequestString: PUBLIC PROCEDURE[s1, s2, s3: LONG STRING ← NIL,
    lowerCaseOK: BOOLEAN ← FALSE,
    flashColor: BOOLEAN ← TRUE,
    breakChars: LONG STRING ← NIL,
    appendBreakChar: BOOLEAN ← FALSE,
    initResult: LONG STRING ← NIL]
    RETURNS[sResult: STRING] =
    BEGIN OPEN multiGraphicsDefs, ProcessDefs;
    s: STRING = [200];
    tiCx: CARDINAL = 100;
    tiCy: CARDINAL = bwMsgTop+4;
    tiLy: CARDINAL = 50;
    refreshPrompt: BOOLEAN ← TRUE;
    colorInverted: BOOLEAN ← FALSE;
    colorWasOn: BOOLEAN ← ColorIsOn[];
    repaintProcess: PROCESS;
    doRepaint, repaintDone: CONDITION;
    tfb: Rect = [tiCx-3, tiCy, 700, bwMsgBottom-6];
      -- the rectangle on the b/w screen containing the text feedback area

    RepaintText: ENTRY PROCEDURE[] =
      BEGIN ENABLE ABORTED => GOTO StopRepainting;
      DO
        WAIT doRepaint;
        IF refreshPrompt THEN
          BEGIN

          RT: PROC [s: LONG STRING, delta: INTEGER] =
            BEGIN
            ss: STRING = [200];
            CopyString[from: s, to: ss];
            [] ← ReplaceText[ss, tiCx, bwMsgTop + delta, fnt, normal];
            END;

          EraseArea[tfb.x1, tfb.y1, tfb.x2, tfb.y2];
          RT[s1, 20];
          RT[s2, 30];
          RT[s3, 40];
          drawString[s, tiCx, bwMsgTop + 50];
          IF colorInverted OR (flashColor AND s.length=0) THEN
            SetColorParity[invert: NOT colorInverted];
          END;
        NOTIFY repaintDone;
        ENDLOOP;
      EXITS StopRepainting => RestoreDisplay[];
      END; -- of RepaintText

    RepaintNow: ENTRY PROCEDURE =
      BEGIN
      refreshPrompt ← TRUE;
      NOTIFY doRepaint;
      WAIT repaintDone;
      END;

    LoanScreen: ENTRY PROCEDURE =
      BEGIN OPEN ProcessDefs;
      RestoreDisplay[];
      END;

    RestoreDisplay: INTERNAL PROCEDURE =
      BEGIN
      refreshPrompt ← FALSE;
      SetColorParity[invert: FALSE];
      EraseArea[tfb.x1, tfb.y1, tfb.x2, tfb.y2];
      END;

    SetColorParity: INTERNAL PROCEDURE[invert: BOOLEAN] =
      BEGIN
      IF invert#colorInverted THEN
        BEGIN
        colorInverted ← invert;
        IF invert THEN invertColors[] ELSE restoreColors[];
        END;
      END;

    GetString: PROCEDURE[s: STRING] =
      BEGIN OPEN InlineDefs, multiGraphicsDefs, StringDefs;

      cmdKey: BOOLEAN ← FALSE;

      CheckKey: keCheckProc =
        BEGIN
        IF BITAND[k.ctl, 17B]=0 AND k.k IN [0..67B] THEN
          RETURN[return: TRUE, execute: FALSE]
        ELSE
          BEGIN
          LoanScreen[];
          cmdKey ← TRUE;
          RETURN[return: TRUE, execute: TRUE];
          END;
        END;

      RepaintNow[];
      DO
        ucDigits: ARRAY[0..11B] OF CHARACTER =
          ['), '!, '@, '#, '$, '%, '~, '&, '*, '(];
        specialChars: ARRAY BOOLEAN OF ARRAY[44B..64B] OF
          CHARACTER =
          [FALSE: [';, '=, ',, '-, '., '/, '', '[, '\, '], '?, '←, '?, '?, '?, '?, ' ],
          TRUE: [':, '+, '<, ',, '>, '?, '", '{, '|, '}, '?, '↑, '?, '?, '?, '?, ' ]];
        k: keyEvent ← CmdTwerp[CheckKey];
        t: CARDINAL ← k.k;
        shift: BOOLEAN ← BITAND[k.ctl, 20B]#0;
        [, xx, yy] ← deScaledCursor[k.mx, k.my];
        IF cmdKey THEN
          {s.length ← 0; RepaintNow[]; cmdKey ← FALSE; LOOP};
        IF BITAND[k.ctl, 17B]=0 THEN
          BEGIN

          IsBreak: PROCEDURE[c: CHARACTER]
            RETURNS[BOOLEAN] =
            BEGIN
            IF breakChars=NIL THEN RETURN[c=CR OR c=ESC OR c=LF]
            ELSE
              FOR i: CARDINAL IN [0..breakChars.length) DO
                IF c=breakChars[i] THEN RETURN[TRUE];
                ENDLOOP;
            RETURN[FALSE];
            END;

          c: CHARACTER ← (SELECT t FROM
            < 12B => IF shift THEN ucDigits[t] ELSE (t + '0),
            < 44B => t + (IF lowerCaseOK AND NOT shift THEN 'a-12B ELSE 'A-12B),
            60B => CR,
            65B => ESC,
            61B => LF,
            56B => UNDO,
            63B => DEL,
            62B => BS,
            IN [44B..64B] => specialChars[shift][t],
            67B => DEBUG,
            ENDCASE => OTHER);
          IF IsBreak[c] THEN
            BEGIN
            IF appendBreakChar THEN AppendChar[s, c];
            RETURN;
            END;
          SELECT c FROM
            UNDO => SIGNAL Punt;
            DEL => s.length ← 0; -- DEL
            BS => IF s.length > 0 THEN s.length ← s.length - 1; -- BS
            DEBUG =>
              BEGIN
              ss: STRING = [200];
              CopyString[from: s1, to: ss];
              MiscDefs.CallDebugger[ss];
              END;
            OTHER => NULL;
            ENDCASE => AppendChar[s, c];
          END;
        RepaintNow[];
        ENDLOOP;
      END; -- of GetString

    IF s1=NIL THEN s1 ← ""L;
    IF s2=NIL THEN s2 ← ""L;
    IF s3=NIL THEN s3 ← ""L;
    IF initResult#NIL THEN
      BEGIN
      s.length ← initResult.length;
      FOR i: CARDINAL IN [0..s.length) DO s[i] ← initResult[i] ENDLOOP;
      END
    ELSE s.length ← 0;
    SetTimeout[condition: @doRepaint, ticks: MsecToTicks[1000]];
    SetTimeout[condition: @repaintDone, ticks: MsecToTicks[1000]];
    repaintProcess ← FORK RepaintText[];
    Yield[]; -- get the repainter to wait on doRepaint
    ColorOn[];
    GetString[s !
      ppdefs.QuitSig => GOTO DoPunt;
      UNWIND =>
        BEGIN
        Abort[repaintProcess];
        JOIN repaintProcess;
        END
      ];
    Abort[repaintProcess];
    JOIN repaintProcess;
    IF NOT colorWasOn THEN ColorOff[];
    RETURN[newString[s]];
    EXITS
      DoPunt => SIGNAL Punt
    END; -- of RequestString

  RemarkAtPoint, DebugAtPoint:
    PUBLIC PROCEDURE[p: Point, s: LONG STRING] =
    BEGIN
    cChange ← TRUE;
    p ← GridPoint[p];
    setCoffset[p.x, p.y];
    putMark[p.x, p.y];
    FreeString[RequestString[s1: s, s2: "(Confirm)"L,
      flashColor: FALSE]];
    END; -- of RemarkAtPoint

  MoveToXY: PUBLIC PROCEDURE[x, y: locNum] =
    -- for the debugger
    {RemarkAtPoint[p: [x: x, y: y], s: ""]};

  GridPoint: PUBLIC PROCEDURE[p: Point] RETURNS[Point] =
    {RETURN[[x: Lambda*(p.x/Lambda), y: Lambda*(p.y/Lambda)]]};

  RequestInteger: PUBLIC PROCEDURE[s1, s2: LONG STRING ← NIL]
    RETURNS[INTEGER] =
    BEGIN
    DO
      s: STRING ← RequestString[s1, s2, "(an integer)"L];
      n: INTEGER ← StringDefs.StringToDecimal[s: s
        ! StringDefs.InvalidNumber => {FreeString[s]; LOOP}];
      FreeString[s];
      RETURN[n];
      ENDLOOP;
    END;

  HeSaysYes: PUBLIC PROCEDURE[s1, s2, s3: LONG STRING ← NIL]
    RETURNS[BOOLEAN] =
    BEGIN
    breaks: STRING ← [20];
    breaks[0] ← CR;
    breaks[1] ← ESC;
    breaks[2] ← DEL;
    breaks[3] ← ' ;
    breaks[4] ← 'Y;
    breaks[5] ← 'y;
    breaks[6] ← 'N;
    breaks[7] ← 'n;
    breaks[8] ← 'T;
    breaks[9] ← 't;
    breaks[10] ← 'F;
    breaks[11] ← 'f;
    breaks[12] ← '1;
    breaks[13] ← '0;
    breaks.length ← 14;
    IF s3=NIL THEN s3 ← "(yes or no)"L;
    DO
      s: STRING ← RequestString[s1: s1, s2: s2, s3: s3,
        breakChars: breaks, appendBreakChar: TRUE];
      IF s.length > 0 THEN
        BEGIN
        c: CHARACTER ← s[0];
        FreeString[s];
        SELECT c FROM
          't, 'T, 'y, 'Y, '1, CR, ESC, '  => RETURN[TRUE];
          'f, 'F, 'n, 'N, '0, DEL => RETURN[FALSE];
          ENDCASE => NULL;
        END
      ELSE FreeString[s];
      ENDLOOP;
    END;

  CopyString: PROC [from, to: LONG STRING] =
    BEGIN
    to.length ← 0;
    IF from#NIL THEN
      BEGIN
      to.length ← from.length;
      FOR i: CARDINAL IN [0..from.length) DO to[i] ← from[i] ENDLOOP;
      END;
    END;

  isDorado: BOOLEAN = SegmentDefs.GetMemoryConfig[].AltoType =
    Dorado;

-- Dolphin color interface

  ColorDCB: TYPE = MACHINE DEPENDENT RECORD [
    bitMap (0): LONG POINTER,
    colorMap (2): LONG POINTER
    ];
  colorDCBPtr: POINTER TO ColorDCB = LOOPHOLE[414B];
  oldColorDCB: ColorDCB;

-- Dorado color interface

  pMonitorHead: POINTER TO POINTER = LOOPHOLE[414B];
  oldMonitorHead: POINTER;


  ColorIsOn: PUBLIC PROCEDURE RETURNS[BOOLEAN] = 
    {RETURN[IF isDorado THEN pMonitorHead↑#NIL
      ELSE colorDCBPtr.bitMap#NIL]};

  ColorOn: PUBLIC PROCEDURE =
    BEGIN
    IF NOT ColorIsOn[] THEN
      BEGIN
      IF isDorado THEN pMonitorHead↑ ← oldMonitorHead
      ELSE colorDCBPtr↑ ← oldColorDCB;
      END;
    END;

  ColorOff: PUBLIC PROCEDURE =
    BEGIN
    IF ColorIsOn[] THEN
      BEGIN
      IF isDorado THEN
        BEGIN
        oldMonitorHead ← pMonitorHead↑;
        pMonitorHead↑ ← NIL;
        END
      ELSE
        BEGIN
        oldColorDCB ← colorDCBPtr↑;
        colorDCBPtr↑ ← [bitMap: NIL, colorMap: NIL];
        END;
      ProcessDefs.Pause[ProcessDefs.MsecToTicks[20]];
        -- allows controller to finish old bits
      END;
    END;

  END. -- of ChipUserIntImpl