    {==========================================================================}
    { Subroutines for normal (split screen, non-pop up) windows and for        }
    { help (pop-up) windows.                                                   }
    {==========================================================================}

    {$C-}

  CONST
    Windows = 3;                        { upper, lower and full-screen window }
    Screendiv = 21;                     { Line number of screen divider line }
    Screendivh = 20;                    { The line above }
    Screendivl = 22;                    { The line below }

    Wtab : ARRAY[1..Windows, 1..8]
    OF Integer                          { X0,Y0,X1,Y1,iX,iY,textcol,backcol }

    = ((1, 1, 80, Screendivh, 1, 1, ColorUpperWindow, 0),
    (1, Screendivl, 80, 25, 1, 1, ColorLowerWindow, 0),
    (1, 1, 80, 25, 1, 1, ColorMiddleWindow, 0));

  VAR
    I : Integer;
    Ch : Char;

    {-----------------------------------------------------------------------------+
    | Select a Window                                                             |
    +-----------------------------------------------------------------------------}

  PROCEDURE SelectWindow(Win : Integer);

    BEGIN
      Window(Wtab[Win, 1], Wtab[Win, 2], Wtab[Win, 3], Wtab[Win, 4]);
      GoToXY(Wtab[Win, 5], Wtab[Win, 6]);
      TextColor(Wtab[Win, 7]);
      TextBackground(Wtab[Win, 8]);
      CurWinNo := Win;
    END;                                {SelectWindow}

    {-----------------------------------------------------------------------------+
    | Update a Window - write 'Wstring' into window 'Win'                         |
    +-----------------------------------------------------------------------------}

  PROCEDURE UpdateWindow(Win : Integer; VAR Wstring : MaxString);

    BEGIN

      { If the desired window is not already selected, select it and position }
      { the cursor. Finally set the colors.                                   }

      IF CurWinNo <> Win THEN SelectWindow(Win);
      IF ((win = 1) AND Capture_enabled) THEN BEGIN
        FOR i := 1 TO Length(Wstring) DO
          IF Wstring[i] = ^Z THEN Wstring[i] := ' ';
        {$I-} Write(Capturefile, Wstring); IOR := IOResult; {$I+}
        IF IOR <> 0 THEN BEGIN
          WriteLn('*** Error writing to Capturefile, capturing terminated ***');
          Close(Capturefile);
          Capture_enabled := False;
        END;
      END;

      Write(Wstring);                   { Write out the string }
      Wtab[Win, 5] := WhereX;           { Note the cursor position }
      Wtab[Win, 6] := WhereY;

    END;                                {UpdateWindow}

    {-----------------------------------------------------------------------------+
    | Read ANY key. Normal keys return  ch <> 00, special keys return ch = 00 and |
    | ech = <scan code>. A special key input also rings the bell                  |
    +-----------------------------------------------------------------------------}

  PROCEDURE ReadIchar(VAR ch, ech : Char);

    BEGIN
      ch := #00;
      Read(Kbd, ch);
      IF (ch = ^[) AND KeyPressed THEN
        BEGIN
          Read(Kbd, ech); ch := #00;
        END;
    END;                                {  ReadIchar  }

    {-----------------------------------------------------------------------------+
    | Wait a time for a keystroke. 'n' is approximately in millisecond            |
    +-----------------------------------------------------------------------------}

  PROCEDURE rdelay(n : Real; VAR chx, echx : Char);

    VAR i : Real;

    BEGIN
      i := 0;
      chx := #01;
      REPEAT
        i := i+1;
        IF KeyPressed THEN ReadIchar(chx, echx);
      UNTIL (chx <> #01) OR (i >= n);
    END;

  PROCEDURE waitreturn(n : Real);
    VAR dch, dchx : Char;
    BEGIN
      Write('<...Press any key to continue..>');
      rdelay(n, dch, dchx);
    END;

    {-----------------------------------------------------------------------------+
    | Date and Time functions. These exist twice because we need two different    |
    | formats. One for display on the screen divider line, the other format is    |
    | used to send the DAYTIME to the TNC-2                                       |
    +-----------------------------------------------------------------------------}

  VAR
    month, day : STRING[2];
    hour, minute, second : STRING[2];

  FUNCTION date : sd;
    VAR
      regisrec : RegPack;
      year : STRING[4];
      cx, dx : Integer;
    BEGIN
      WITH regisrec DO
        BEGIN
          ax := $2A SHL 8;
        END;
      MsDos(regisrec);
      WITH regisrec DO
        BEGIN
          Str(cx, year);
          Str(dx MOD 256, day);
          Str(dx SHR 8, month);
        END;
      IF Length(month) = 1 THEN Insert('0', month, 1);
      IF Length(day) = 1 THEN Insert('0', day, 1);
      date := day+'-'+month+'-'+year;
    END;

  FUNCTION time : st;
    VAR
      regisrec : RegPack;
      cx, dx : Integer;
    BEGIN
      WITH regisrec DO
        BEGIN
          ax := $2C SHL 8;
        END;
      MsDos(regisrec);
      WITH regisrec DO
        BEGIN
          Str(cx SHR 8, hour);
          Str(cx MOD 256, minute);
          Str(dx SHR 8, second);
        END;
      IF Length(hour) = 1 THEN Insert('0', hour, 1);
      IF Length(minute) = 1 THEN Insert('0', minute, 1);
      IF Length(second) = 1 THEN Insert('0', second, 1);
      time := hour+':'+minute+':'+second
    END;

  FUNCTION qdate : sd;
    VAR
      regisrec : RegPack;
      month, day : STRING[2];
      year : STRING[2];
      cx, dx : Integer;
    BEGIN
      WITH regisrec DO
        BEGIN
          ax := $2A SHL 8;
        END;
      MsDos(regisrec);
      WITH regisrec DO
        BEGIN
          Str(cx-1900, year);
          Str(dx MOD 256, day);
          Str(dx SHR 8, month);
        END;
      IF Length(month) = 1 THEN Insert('0', month, 1);
      IF Length(day) = 1 THEN Insert('0', day, 1);
      qdate := year+month+day;
    END;

  FUNCTION qtime : st;
    VAR
      regisrec : RegPack;
      hour, minute, second : STRING[2];
      cx, dx : Integer;
    BEGIN
      WITH regisrec DO
        BEGIN
          ax := $2C SHL 8;
        END;
      MsDos(regisrec);
      WITH regisrec DO
        BEGIN
          Str(cx SHR 8, hour);
          Str(cx MOD 256, minute);
          Str(dx SHR 8, second);
        END;
      IF Length(hour) = 1 THEN Insert('0', hour, 1);
      IF Length(minute) = 1 THEN Insert('0', minute, 1);
      IF Length(second) = 1 THEN Insert('0', second, 1);
      qtime := hour+minute+second
    END;

  PROCEDURE Frame(DivLine : Integer);
    VAR I : Integer;

    BEGIN                               {Frame}
      SelectWindow(3);
      GoToXY(1, DivLine);
      IF Wordout_enabled THEN Write('[WORD] ') ELSE Write('[LINE] ');
      FOR I := 8 TO 80 DO Write(Chr(196));
      GoToXY(28,Divline);
      IF Bells_enabled THEN Write(' [BELLS=ON] ', Chr(196), Chr(196), Chr(196))
      ELSE Write(' [BELLS=OFF] ', Chr(196), Chr(196));
      GoToXY(42,Divline);
      IF Capture_enabled THEN Write(' [CAPTURE=ON] ', Chr(196), Chr(196), Chr(196))
      ELSE Write(' [CAPTURE=OFF] ', Chr(196), Chr(196));
      pdate := '';                      {intital values for date and minute}
      minute := '00';
    END;                                {Frame}

    {-----------------------------------------------------------------------------+
    | Read the Parameter-file to load the Function-key strings                    |
    +-----------------------------------------------------------------------------}

  PROCEDURE READPARAMFILE(Filename : Maxstring);

    VAR line : Maxstring; SFile : Text;
      IOR, ic0, ic1 : Integer;
    BEGIN
      FOR ic0 := 1 TO 6 DO BEGIN
        Fstr[ic0] := '';
        Fdstr[ic0] := '';
      END;
      Assign(Sfile, Filename);
      {$I-} Reset(Sfile); IOR := IOResult; {$I+}
      IF IOR <> 0 THEN BEGIN
        WriteLn('*** Parameter File "'+Filename+'" not found ***');
        WriteLn('*** Function-Keys 1-6 are undefined ***');
        Delay(5000);
      END ELSE BEGIN
        WriteLn('LOADING text for function-keys...');
        WriteLn;
        ic1 := 1;
        WHILE (NOT EoF(Sfile) AND (ic1 <= 6)) DO BEGIN
          {$I-} ReadLn(Sfile, line); IOR := IOResult; {$I+}
          IF IOR <> 0 THEN Exit;
          IF Line[1] <> '&' THEN BEGIN
            Fstr[ic1] := Fstr[ic1]+line+cr+lf;
          END ELSE BEGIN
            Fdstr[ic1] := Copy(line, 2, 80);
            Ic1 := ic1+1;
          END;
        END;
        Close(SFile);
      END;
    END;

    {-----------------------------------------------------------------------------+
    | Define Help window number THREE  (SSS-program specific window)              |
    +-----------------------------------------------------------------------------}

  PROCEDURE HelpWindow3;

    BEGIN
      PTWOpen(3);
      ClrScr;
      GoToXY(2, 01); Write('PROGRAM CONTROL KEYS');
      GoToXY(2, 03); Write('F1  -> ', Fdstr[1]);
      GoToXY(2, 04); Write('F2  -> ', Fdstr[2]);
      GoToXY(2, 05); Write('F3  -> ', Fdstr[3]);
      GoToXY(2, 06); Write('F4  -> ', Fdstr[4]);
      GoToXY(2, 07); Write('F5  -> ', Fdstr[5]);
      GoToXY(2, 08); Write('F6  -> ', Fdstr[6]);
      GoToXY(2, 09); Write('F9  -> TNC commands');
      GoToXY(2, 10); Write('F10 -> PGM control keys');
      GoToXY(2, 12); Write('ALT/C -> Capture to file');
      GoToXY(2, 13); Write('ALT/B -> Bells ON/OFF');
      GoToXY(2, 14); Write('ALT/S -> Send textfile');
      GoToXY(2, 15); Write('ALT/A -> Abort filesend');
      GoToXY(2, 16); Write('ALT/X -> Exit program');
      GoToXY(2, 18); Waitreturn(20000.0);
      PTWClose;
      TextColor(15);
      TextBackground(0);
    END;

  PROCEDURE Transferstr(tstr : Maxstring; mode : OMode);
      {----------------------------------------------------------------------}
      { Write the string 'tstr' to the lower window, append it to the        }
      { outstring and write outsting to the comm-port.                       }
      {----------------------------------------------------------------------}

    VAR ic : Integer; xc : Maxstring;

    BEGIN
      IF Length(tstr) > 0 THEN BEGIN
        UpdateWindow(2, tstr);          {then write it onto lower window}

        { Most TNCs have AUTOLF ON, so after a LF they insert a CR. This would result
        in an extra, empty line if we send CR-LF. So better 'supress' the LF by
        overwriting it with CTRL-V }

        FOR ic := 1 TO Length(tstr) DO
          IF tstr[ic] = lf THEN tstr[ic] := ^V;
        Async_Send_String(tstr);        {write outsting to COMMport}
      END;
    END;

    {----------------------------------------------------------------------}
    { PROGRAM Control Procedures                                           }
    {----------------------------------------------------------------------}

  PROCEDURE PGMCMD_CAPTURE;
      { Switch to capture mode and prompt for capture filename }

    BEGIN
      IF NOT Capture_enabled THEN BEGIN
        OutBuffer[PGM_CAP] := '';
        SelectWindow(2);                {Clear lower screen}
        ClrScr;
        Wtab[2, 5] := 1; Wtab[2, 6] := 1;
        Tmpstr := 'Capture to File > ';
        UpdateWindow(2, Tmpstr);        {Echo to lower window}
        mode := PGM_CAP;                {Switch modes}
      END ELSE BEGIN
        Close(Capturefile);
        Capture_enabled := False;
        Frame(Screendiv);               {Re-Draw the screen divider line}
      END;
    END;


  PROCEDURE PGMCMD_SENDFILE;
      { Switch to send-mode and prompt for send filename }

    BEGIN
      IF Send_ok THEN BEGIN
        OutBuffer[PGM_SEN] := '';
        SelectWindow(2);                {Clear lower screen}
        ClrScr;
        Wtab[2, 5] := 1; Wtab[2, 6] := 1;
        Tmpstr := 'Send File > ';
        UpdateWindow(2, Tmpstr);        {Echo to lower window}
        mode := PGM_SEN;                {Switch modes}
      END ELSE BEGIN
        Tmpstr := 'Send file not supported';
        UpdateWindow(2, Tmpstr);        {Echo to lower window}
      END;
    END;

  PROCEDURE PGMCMD_BELLS;
      { Toggle bell recognition on/off }

    BEGIN
      Bells_enabled := NOT Bells_enabled;
      Frame(Screendiv);
    END;

  PROCEDURE PGMCMD_WORDOUT;
      { Switch to WORD output mode }

    BEGIN
      Wordout_enabled := True;
      Frame(Screendiv);
    END;

  PROCEDURE PGMCMD_LINEOUT;
      { Switch to LINE output mode }

    BEGIN
      Wordout_enabled := False;
      Frame(Screendiv);
    END;

  PROCEDURE PGMCMD_CLR_U_SCREEN;
      { Clear upper screen }

    BEGIN
      SelectWindow(1);
      ClrScr;
      Wtab[1, 5] := 1; Wtab[1, 6] := 1;
    END;

  PROCEDURE PGMCMD_CLR_L_SCREEN;
      { Clear lower screen }
    BEGIN
      SelectWindow(2);
      ClrScr;
      Wtab[2, 5] := 1; Wtab[2, 6] := 1;
      Outbuffer[NORM] := '';
    END;

  PROCEDURE PGMCMD_ABORT_SEND;
      { Abort a pending transmisson of a file }

    BEGIN
      IF Sendfile_enabled THEN BEGIN
        Sendfile_enabled := False;
        Close(Sendfile);
        Delay(500);
        IF Async_CTS_On THEN BEGIN
          Async_send(^Z);
          Async_send(cr);
        END;
        TmpStr := cr+lf+'*** Aborted by Operator ***'+cr+lf;
        UpdateWindow(2, Tmpstr);        {Write it to lower window}
        Delay(2000);
        IF TNC_echo_on THEN Async_Send_STring(^C'ECHO ON'^M'CONV'^M);
      END;
    END;

    {----------------------------------------------------------------------}
    { Character processing Procedures                                      }
    {----------------------------------------------------------------------}

  PROCEDURE PROCCHAR_DEL;
    BEGIN
      {Write backspace/space/backspace to lower window and truncate
      Outputstring by one}
      IF (Length(Outbuffer[mode]) > 0) THEN BEGIN
        Tmpstr := del+' '+del;
        UpdateWindow(2, Tmpstr);
        Delete(OutBuffer[mode], Length(OutBuffer[mode]), 1);
      END;
    END;


  PROCEDURE PROCCHAR_SPACE;
    BEGIN
      Tmpstr := cha;
      UpdateWindow(2, Tmpstr);          {Write it to lower window}
      OutBuffer[mode] := OutBuffer[mode]+cha; {append to outstring}
      Async_Send_String(OutBuffer[mode]); { write outsting to COMMport}
      OutBuffer[mode] := '';            {Clear OutBuffer}
    END;

  PROCEDURE PROCCHAR_CR;
    BEGIN
      IF mode = PGM_CAP THEN BEGIN

        { if line contains a filespecifier as an answer to a capture-to-file cmd }

        IF Length(OutBuffer[PGM_CAP]) = 0 THEN { default to mmddhhmm.CAP }
          OutBuffer[PGM_CAP] := month+day+hour+minute+'.CAP';
        Assign(Capturefile, OutBuffer[PGM_CAP]);
        {$I-} Reset(Capturefile); IOR := IOResult; {$I+}
        IF IOR = 0 THEN BEGIN
          WriteLn;
          WriteLn('*** Capturefile "', OutBuffer[PGM_CAP], '" already exists ***');
          Delay(1000);
        END ELSE BEGIN
          {$I-} Rewrite(Capturefile); IOR := IOResult; {$I+}
          IF IOR <> 0 THEN BEGIN
            WriteLn;
            WriteLn('*** Unable to open "', OutBuffer[PGM_CAP], '" ***');
            Delay(1000);
          END ELSE BEGIN
            WriteLn;
            WriteLn('Capturing to file "', OutBuffer[PGM_CAP], '"');
            Delay(1000);
            capture_enabled := True;
            Frame(Screendiv);           { Re-Draw the screen divider line }
          END;
        END;
      END;

      IF mode = PGM_SEN THEN BEGIN

        { if line contains a filespecifier as an answer to a send-file cmd }

        Assign(Sendfile, OutBuffer[PGM_SEN]);
        {$I-} Reset(Sendfile); IOR := IOResult; {$I+}
        IF IOR <> 0 THEN BEGIN
          WriteLn;
          WriteLn('*** Cannot open File "', OutBuffer[PGM_SEN], '" ***');
          Delay(1000);
        END ELSE BEGIN
          Sendfile_enabled := True;
          Slindex := 0;
          WriteLn;
          WriteLn('Start sending file "', OutBuffer[PGM_SEN], '"');
          WriteLn('NOTE: use ALT/A to ABORT ');
          Async_send_String(^C'ECHO OFF'^M'CONV'^M);
          Delay(1000);
        END;
      END;

      IF (mode = NORM) OR (mode = DEV_CMD) THEN BEGIN

        { if line contains text or command for tnc }

        OutBuffer[mode] := OutBuffer[mode]+cr; { append to outstring}
        Async_Send_String(OutBuffer[mode]); { write outsting to COMMport}
        IF (NOT Tnc_echo_on) THEN BEGIN
          OutBuffer[mode] := OutBuffer[mode]+lf; { append to outstring}
          IF CurWinNo <> 1 THEN SelectWindow(1); { select upper window}
          IF WhereX > 6 THEN BEGIN      { if not in pos 1-5 then}
            Tmpstr := cr+lf;            { goto pos 1 next line}
            UpdateWindow(1, Tmpstr);
          END;
          SelectWindow(1);
          TextColor(15);
          UpdateWindow(1, Outbuffer[mode]); { write it onto upper window}
        END;
        OutBuffer[mode] := '';          { Clear OutBuffer}
      END;

      Tmpstr := cr+lf;
      UpdateWindow(2, Tmpstr);          { Switch back to lower window}
      IF NOT wordout_enabled THEN BEGIN
        ClrScr;                         { Clear the Screen}
        Wtab[2, 5] := 1;                { Init cursor pos. and rewrite..}
        Wtab[2, 6] := 1;                { text string if command was executed}
      END;
      IF mode <> NORM THEN UpdateWindow(2, Outbuffer[NORM]);
      mode := NORM;                     { ALWAYS ! enter normal dialoge mode }
    END;

  PROCEDURE PROCCHAR_ESC;
    BEGIN
      SelectWindow(2);                  { Clear lower screen}
      ClrScr;
      Wtab[2, 5] := 1; Wtab[2, 6] := 1;
      Tmpstr := 'CMD>';
      UpdateWindow(2, Tmpstr);          { Echo to lower window}
      mode := DEV_CMD;                  { Switch to device command mode }
      Async_Send_String(cmdchr);        { command char into command-outbuffer}
    END;


  PROCEDURE PROCCHAR_REST;
    BEGIN
      Tmpstr := cha;
      UpdateWindow(2, Tmpstr);          {Write it to lower window}

      { Force sending of the line if the string exceeds 252 chars }

      IF Length(OutBuffer[mode]) < 252 THEN BEGIN
        OutBuffer[mode] := OutBuffer[mode]+cha; {append to outstring}
      END ELSE BEGIN
        UpdateWindow(1, Outbuffer[mode]); {write it onto upper window}
        Async_Send_String(OutBuffer[mode]); {write outsting to COMMport}
        OutBuffer[mode] := '';          {Clear OutBuffer}
        TmpStr := cr;
        UpdateWindow(2, Tmpstr);        {Switch back to lower window}
        ClrScr;                         {Clear the Screen}
        Wtab[2, 5] := 1;                {Init cursor pos. and rewrite..}
        Wtab[2, 6] := 1;                {text string if command was executed}
        IF mode = DEV_CMD THEN UpdateWindow(2, Outbuffer[NORM]);
        mode := NORM;
      END;
    END;
