home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pmos2002.zip / SRC / VIO / windows.mod < prev   
Text File  |  1998-01-28  |  93KB  |  2,393 lines

  1. IMPLEMENTATION MODULE Windows;
  2.  
  3.         (********************************************************)
  4.         (*                                                      *)
  5.         (*                  Text-mode screen windows.           *)
  6.         (*                                                      *)
  7.         (*  Programmer:         P. Moylan                       *)
  8.         (*  Last edited:        26 January 1998                 *)
  9.         (*  Status:             Working                         *)
  10.         (*                                                      *)
  11.         (*    Faults:                                           *)
  12.         (*      1. (fixed)                                      *)
  13.         (*      2. (fixed)                                      *)
  14.         (*                                                      *)
  15.         (*      For further thought at some later stage:        *)
  16.         (*         -    (maybe) extend the set of procedures    *)
  17.         (*              for which w^.access0p5 is locked.       *)
  18.         (*                                                      *)
  19.         (********************************************************)
  20.  
  21. FROM SYSTEM IMPORT
  22.     (* type *)  CARD8,
  23.     (* proc *)  ADR, CAST;
  24.  
  25. IMPORT OS2;
  26.  
  27. FROM Types IMPORT
  28.     (* proc *)  FarPointer, FarCharPointer;
  29.  
  30. FROM LowLevel IMPORT
  31.     (* proc *)  Far, MakePointer, FarAddOffset, Copy, FarCopy, CopyUp,
  32.                 IXORB, HighWord;
  33.  
  34. FROM Storage IMPORT
  35.     (* proc *)  ALLOCATE, DEALLOCATE;
  36.  
  37. FROM FinalExit IMPORT
  38.     (* proc *)  SetTerminationProcedure, TerminationMessage;
  39.  
  40. FROM TaskControl IMPORT
  41.     (* type *)  Lock,
  42.     (* proc *)  CreateTask, CurrentTaskID, CreateLock, DestroyLock,
  43.                 Obtain, Release, ReleaseAllLocks;
  44.  
  45. FROM Semaphores IMPORT
  46.     (* type *)  Semaphore,
  47.     (* proc *)  CreateSemaphore, DestroySemaphore, Wait, Signal, SemaphoreHolder;
  48.  
  49. FROM Keyboard IMPORT
  50.     (* proc *)  InKey, PutBack;
  51.  
  52. CONST testing = TRUE;
  53.  
  54. (************************************************************************)
  55. (*      If you want black-and-white operation even though your          *)
  56. (*      display supports colour (e.g. for the case where the colours    *)
  57. (*      are not very readable), set ForcedMonochrome to TRUE.           *)
  58. (*      Otherwise, this module selects colour operation if it           *)
  59. (*      thinks that the hardware can support it.                        *)
  60. (************************************************************************)
  61.  
  62. CONST ForcedMonochrome = FALSE;
  63.  
  64. (************************************************************************)
  65.  
  66. CONST
  67.     BytesPerChar = 2;                   (* # bytes/char in video buffer *)
  68.     CharsPerRow = MaxColumnNumber + 1;  (* characters per row           *)
  69.     BytesPerRow = BytesPerChar*CharsPerRow;
  70.     CharsInBuffer = CharsPerRow * (MaxRowNumber+1);
  71.     BytesInBuffer = BytesPerChar * CharsInBuffer;
  72.                                         (* size of window buffer        *)
  73.     DefaultPage = 0;
  74.     DefaultTabs =
  75. "        T       T       T       T       T       T       T       T       T";
  76.  
  77. (************************************************************************)
  78.  
  79. TYPE
  80.     BufferSubscript = [0..CharsInBuffer - 1];
  81.     ExtendedBufferSubscript = [0..CharsInBuffer];
  82.  
  83.     CloseHandlerList = POINTER TO CloseHandlerRecord;
  84.     CloseHandlerRecord = RECORD
  85.                             next: CloseHandlerList;
  86.                             proc: CloseHandlerProc;
  87.                          END (*RECORD*);
  88.  
  89.     PageChangeList = POINTER TO PageChangeRecord;
  90.     PageChangeRecord = RECORD
  91.                           next: PageChangeList;
  92.                           proc: PageChangeHandler;
  93.                        END (*RECORD*);
  94.  
  95.     ScreenChar = RECORD
  96.                      val: CHAR;
  97.                      attr: CARD8;
  98.                  END (*RECORD*);
  99.  
  100.     Window = POINTER TO WindowData;
  101.  
  102.         (****************************************************************)
  103.         (*                                                              *)
  104.         (* WindowData records are linked (by next and previous) as a    *)
  105.         (* doubly linked list, to implement a stack of windows.         *)
  106.         (* There is a separate stack for each display page.             *)
  107.         (* Variable TopWindow[page] points to the top of this stack.    *)
  108.         (*                                                              *)
  109.         (* The access0p5 lock is to control simultaneous access         *)
  110.         (* to a single window by multiple tasks.  This is a new         *)
  111.         (* feature, so the protection is implemented for some but not   *)
  112.         (* all operations at this stage.  I'm still working on whether  *)
  113.         (* access0p5 should be a Lock or a Semaphore.  From the         *)
  114.         (* viewpoint of the external caller it's better to make it a    *)
  115.         (* Lock, but that creates problems during shutdown.             *)
  116.         (*                                                              *)
  117.         (* CloseList points to a linked list of procedures to be        *)
  118.         (* called when this window is closed.                           *)
  119.         (*                                                              *)
  120.         (* The row and column values stored in this record are actual   *)
  121.         (* screen row and column, i.e. they are not window-relative.    *)
  122.         (*                                                              *)
  123.         (* The BufferPosition field is a subscript into the buffer      *)
  124.         (* array (see below).  It can be computed easily from the       *)
  125.         (* "row" and "column" fields, but it is more convenient to      *)
  126.         (* keep this technically redundant variable.                    *)
  127.         (*                                                              *)
  128.         (* A window's InputWaiting flag is set when it is waiting for   *)
  129.         (* a keyboard character.  When the character arrives it is      *)
  130.         (* stored in the InputChar field of this record, and we are     *)
  131.         (* notified of this by a Release(CharAvailable).  Because the   *)
  132.         (* requests for input are not necessarily satisfied in a FIFO   *)
  133.         (* order - because the input focus can change before the input  *)
  134.         (* arrives - we need a separate semaphore and InputChar field   *)
  135.         (* for each window, even though these fields remain unused for  *)
  136.         (* most windows.                                                *)
  137.         (*                                                              *)
  138.         (* A window with the "hidden" flag set is not part of the       *)
  139.         (* stack of windows, and is not visible on the screen.          *)
  140.         (*                                                              *)
  141.         (* The "obscured" field indicates whether this window is wholly *)
  142.         (* or partially obscured by another window on the screen.  By   *)
  143.         (* keeping track of this, we can avoid some unnecessary screen  *)
  144.         (* refreshing.                                                  *)
  145.         (*                                                              *)
  146.         (* The "blockcursor" field specifies what kind of cursor to     *)
  147.         (* display: a block cursor if TRUE, an underline cursor if      *)
  148.         (* FALSE.  Most of the time this is irrelevant, as we display   *)
  149.         (* a cursor only when the window's CursorWanted flag is TRUE.   *)
  150.         (*                                                              *)
  151.         (* The "buffer" array holds a copy of what is supposed to be    *)
  152.         (* transferred to the video buffer.                             *)
  153.         (*                                                              *)
  154.         (****************************************************************)
  155.  
  156.     WindowData = RECORD
  157.                     next, previous: Window;
  158.                     access0p5: Semaphore;
  159.                     CloseList: CloseHandlerList;
  160.                     frame: FrameType;  divider: DividerType;
  161.                     tabstops: ARRAY ColumnRange OF CHAR;
  162.                     ScrollRegion, DefaultScrollRegion: Rectangle;
  163.                     page: DisplayPage;
  164.                     FirstRow, LastRow, row: RowRange;
  165.                     FirstColumn, LastColumn: ColumnRange;
  166.                     column: [0..MAX(ColumnRange)+1];
  167.                     BufferPosition: ExtendedBufferSubscript;
  168.                     foreground, background: Colour;
  169.                     CurrentAttributes: CARD8;
  170.                     InputChar: CHAR;
  171.                     CharAvailable: Semaphore;
  172.                     InputWaiting, hidden, obscured,
  173.                         CursorWanted, blockcursor, WrapOption: BOOLEAN;
  174.                     buffer: ARRAY BufferSubscript OF ScreenChar;
  175.                  END (*RECORD*);
  176.  
  177. (************************************************************************)
  178. (*                 NOTE ON CRITICAL SECTION PROTECTION                  *)
  179. (* The potential deadlock problems in this module are surprisingly      *)
  180. (* subtle, arising from the fact that a procedure incorporating one     *)
  181. (* form of critical section protection may call other procedures which  *)
  182. (* themselves contain critical section protection.  To avoid these      *)
  183. (* problems, we use an ordered resource policy.  Each critical section  *)
  184. (* protection semaphore is given a "level", which for clarity is shown  *)
  185. (* as the last character of its name.  A piece of code is said to be    *)
  186. (* executing at level N if it is inside a critical section protected    *)
  187. (* by a semaphore whose level is N (and not inside a critical section   *)
  188. (* protected by any semaphore of any higher level).  The rule which     *)
  189. (* avoids deadlock is: to lock a semaphore at level N, we must be       *)
  190. (* executing at a level < N.                                            *)
  191. (* For the purposes of this analysis, Locks and Semaphores are treated  *)
  192. (* as being equivalent.                                                 *)
  193. (*                                                                      *)
  194. (*                      LATEST DEVELOPMENT                              *)
  195. (* Because I'm adding a new semaphore, but haven't yet updated the      *)
  196. (* notation, a new level 0p5 (meaning 0.5) has been added.              *)
  197. (************************************************************************)
  198.  
  199. VAR
  200.     (* BlackAndWhite is true if we have a monochrome display.           *)
  201.  
  202.     BlackAndWhite: BOOLEAN;
  203.  
  204.     (* ActivePage is the display page currently visible on the screen.  *)
  205.  
  206.     ActivePage: DisplayPage;
  207.  
  208.     (* A list of procedures to call whenever the current display page   *)
  209.     (* is changed, and a lock to control access to this list.           *)
  210.  
  211.     PageChangeProcs: PageChangeList;
  212.     PageChangeListAccess: Lock;
  213.  
  214.     (* A semaphore to say that somebody wants some keyboard input.      *)
  215.  
  216.     InputRequest: Semaphore;
  217.  
  218.     (* BlankRow is set up by the initialisation code as a row of space  *)
  219.     (* characters.  Note however that the attribute codes need to be    *)
  220.     (* filled in before each use.                                       *)
  221.  
  222.     BlankRow: ARRAY [0..CharsPerRow-1] OF ScreenChar;
  223.  
  224.     (* Access to BlankRow is a critical section, so we protect it with  *)
  225.     (* a Lock.                                                          *)
  226.  
  227.     BlankRowAccess1: Lock;
  228.  
  229.     (* StackAccess2 is used to protect access to the shared data        *)
  230.     (* structure which defines the stacks of windows.                   *)
  231.  
  232.     StackAccess2: Lock;
  233.  
  234.     (* TopWindow[p] is the current top of the stack of windows for      *)
  235.     (* display on physical display page p.                              *)
  236.  
  237.     TopWindow: ARRAY DisplayPage OF Window;
  238.  
  239.     (* ScreenAccess3 is used to protect access to memory in segment     *)
  240.     (* ScreenSeg, i.e. the memory belonging to the physical screen.     *)
  241.  
  242.     ScreenAccess3: Lock;
  243.  
  244.     (* PhysicalCursor keeps track of the blinking screen cursor.  The   *)
  245.     (* CursorWindow field shows which window, if any, currently "owns"  *)
  246.     (* the physical cursor.  CursorVisible[page] shows whether the      *)
  247.     (* cursor should be visible when "page" is the active display page. *)
  248.     (* ScreenPos and Attributes are the position and display attributes *)
  249.     (* that we expect for the next character to be physically written   *)
  250.     (* to the screen.                                                   *)
  251.     (* Lock access4 is used to protect these variables and the hardware *)
  252.     (* operations of turning the cursor on and off.                     *)
  253.     (* For now, access4 also protects alterations to the active         *)
  254.     (* display page.                                                    *)
  255.  
  256.     PhysicalCursor: RECORD
  257.                         access4: Lock;
  258.                         CursorWindow: ARRAY DisplayPage OF Window;
  259.                         CursorVisible: ARRAY DisplayPage OF BOOLEAN;
  260.                         ScreenPos: CARDINAL;
  261.                         Attributes: CARD8;
  262.                     END (*RECORD*);
  263.  
  264. (************************************************************************)
  265. (*                 TURNING THE SCREEN CURSOR ON AND OFF                 *)
  266. (************************************************************************)
  267.  
  268. PROCEDURE CursorOff;
  269.  
  270.     (* Turns the cursor off.  *)
  271.  
  272.     VAR CursorInfoBlock: OS2.VIOCURSORINFO;
  273.  
  274.     BEGIN
  275.         WITH CursorInfoBlock DO
  276.             yStart := 0;  cEnd := 0;  cx := 0;  attr := 0FFFFH;
  277.         END (*WITH*);
  278.         OS2.VioSetCurType (CursorInfoBlock, 0);
  279.     END CursorOff;
  280.  
  281. (************************************************************************)
  282.  
  283. PROCEDURE CursorOn (row, column: CARDINAL;  blockcursor: BOOLEAN);
  284.  
  285.     (* Displays a blinking screen cursor at the specified position.     *)
  286.  
  287.     CONST Minus90 = 0FFFFH - 89;  Minus100 = 0FFFFH - 99;
  288.  
  289.     VAR CursorInfoBlock: OS2.VIOCURSORINFO;
  290.  
  291.     BEGIN
  292.         WITH CursorInfoBlock DO
  293.             IF blockcursor THEN yStart := 0 ELSE yStart := Minus90 END (*IF*);
  294.             cEnd := Minus100;  cx := 1;  attr := 0;
  295.         END (*WITH*);
  296.         OS2.VioSetCurType (CursorInfoBlock, 0);
  297.         OS2.VioSetCurPos (row, column, 0);
  298.     END CursorOn;
  299.  
  300. (************************************************************************)
  301.  
  302. PROCEDURE UpdatePhysicalCursor;
  303.  
  304.     (* Turns the physical cursor on or off, as appropriate.  Also       *)
  305.     (* signals a new input request, if a window on the active page is   *)
  306.     (* waiting for input, in case the input task has gone idle.         *)
  307.     (* The caller must be running at level<4.                           *)
  308.  
  309.     VAR w: Window;
  310.  
  311.     BEGIN
  312.         WITH PhysicalCursor DO
  313.             Obtain (access4);
  314.             w := CursorWindow[ActivePage];
  315.             IF w <> NIL THEN
  316.                 Signal (InputRequest);
  317.             END (*IF*);
  318.             IF CursorVisible[ActivePage] THEN
  319.                 CursorOn (w^.row, w^.column, w^.blockcursor);
  320.             ELSE
  321.                 CursorOff;
  322.             END (*IF*);
  323.             Release (access4);
  324.         END (*WITH*);
  325.     END UpdatePhysicalCursor;
  326.  
  327. (************************************************************************)
  328. (*                  HARDWARE DISPLAY PAGE CHANGES                       *)
  329. (************************************************************************)
  330.  
  331. PROCEDURE SetActivePage (page: DisplayPage);
  332.  
  333.     (* Changes the active display page.  Remark: OS/2 apparently does   *)
  334.     (* not support multiple hardware text pages, at least in a VIO      *)
  335.     (* session, so we simulate them by displaying only those windows    *)
  336.     (* for which w^.page=ActivePage.                                    *)
  337.  
  338.     VAR PCL: PageChangeList;
  339.  
  340.     BEGIN
  341.         Obtain (PhysicalCursor.access4);
  342.         ActivePage := page;
  343.         Release (PhysicalCursor.access4);
  344.         RefreshDisplay;
  345.  
  346.         (* Call the procedures which want notification of the change.   *)
  347.  
  348.         Obtain (PageChangeListAccess);
  349.         PCL := PageChangeProcs;
  350.         WHILE PCL <> NIL DO
  351.             PCL^.proc (page);
  352.             PCL := PCL^.next;
  353.         END (*WHILE*);
  354.         Release (PageChangeListAccess);
  355.  
  356.         (* Turn the cursor off or on, as appropriate. *)
  357.  
  358.         UpdatePhysicalCursor;
  359.  
  360.     END SetActivePage;
  361.  
  362. (************************************************************************)
  363.  
  364. PROCEDURE RequestPageChangeNotification (Proc: PageChangeHandler);
  365.  
  366.     (* Sets up Proc as a procedure to be called on a page change.       *)
  367.  
  368.     VAR PCL: PageChangeList;
  369.  
  370.     BEGIN
  371.         NEW (PCL);
  372.         Obtain (PageChangeListAccess);
  373.         WITH PCL^ DO
  374.             next := PageChangeProcs;
  375.             proc := Proc;
  376.         END (*WITH*);
  377.         PageChangeProcs := PCL;
  378.         Release (PageChangeListAccess);
  379.     END RequestPageChangeNotification;
  380.  
  381. (************************************************************************)
  382.  
  383. PROCEDURE PageOf (w: Window): DisplayPage;
  384.  
  385.     (* Returns the display page on which window w resides. *)
  386.  
  387.     BEGIN
  388.         RETURN w^.page;
  389.     END PageOf;
  390.  
  391. (************************************************************************)
  392.  
  393. PROCEDURE CurrentPage(): DisplayPage;
  394.  
  395.     (* Returns the currently active display page. *)
  396.  
  397.     BEGIN
  398.         RETURN ActivePage;
  399.     END CurrentPage;
  400.  
  401. (************************************************************************)
  402. (*              MANIPULATION OF THE STACK OF WINDOWS                    *)
  403. (************************************************************************)
  404.  
  405. PROCEDURE UnLink (w: Window);
  406.  
  407.     (* Removes w^ from the stack, but otherwise leaves it unchanged.    *)
  408.     (* Caller must have locked StackAccess2.                            *)
  409.  
  410.     BEGIN
  411.         WITH w^ DO
  412.             IF previous <> NIL THEN previous^.next := next END (*IF*);
  413.             IF next <> NIL THEN next^.previous := previous END (*IF*);
  414.             IF TopWindow[page] = w THEN TopWindow[page] := next END (*IF*);
  415.             previous := NIL;  next := NIL;
  416.         END (*WITH*);
  417.     END UnLink;
  418.  
  419. (************************************************************************)
  420.  
  421. PROCEDURE IdentifyTopWindow (VAR (*OUT*) w: Window;  page: DisplayPage;
  422.                                 VAR (*INOUT*) row: RowRange;
  423.                                 VAR (*INOUT*) col: ColumnRange): BOOLEAN;
  424.  
  425.     (* On entry w is unspecified and (page,row,col) describes a         *)
  426.     (* position on the screen.  On exit w is equal to the top window    *)
  427.     (* containing this screen location, and (row,col) have been altered *)
  428.     (* to be window-relative coordinates.  Exception: if there is no    *)
  429.     (* visible window containing the given point, the function result   *)
  430.     (* is FALSE, the returned w is meaningless, and row and col are     *)
  431.     (* unchanged.                                                       *)
  432.  
  433.     BEGIN
  434.         Obtain (StackAccess2);
  435.         w := TopWindow[page];
  436.         LOOP
  437.             IF w = NIL THEN EXIT(*LOOP*);
  438.             ELSIF (col >= w^.FirstColumn) AND (col <= w^.LastColumn)
  439.                         AND (row >= w^.FirstRow) AND (row <= w^.LastRow) THEN
  440.                 DEC (row, w^.FirstRow);  DEC (col, w^.FirstColumn);
  441.                 EXIT (*LOOP*);
  442.             ELSE
  443.                 w := w^.next;
  444.             END (*IF*);
  445.         END (*LOOP*);
  446.         Release (StackAccess2);
  447.         RETURN w <> NIL;
  448.     END IdentifyTopWindow;
  449.  
  450. (************************************************************************)
  451.  
  452. PROCEDURE ComputeCursorWindow (page: DisplayPage);
  453.  
  454.     (* Rechecks which window on this page should have the physical      *)
  455.     (* screen cursor, and displays or turns off the cursor, as          *)
  456.     (* appropriate, if a change is needed.  This procedure should be    *)
  457.     (* called whenever there is a chance that the input focus might     *)
  458.     (* need to be shifted.                                              *)
  459.  
  460.     VAR w, wtop: Window;  visible: BOOLEAN;
  461.         row: RowRange;  col: ColumnRange;
  462.  
  463.     BEGIN
  464.         (* Find the top window that's waiting for input. *)
  465.  
  466.         Obtain (StackAccess2);
  467.         w := TopWindow[page];
  468.         LOOP
  469.             IF w = NIL THEN EXIT(*LOOP*) END(*IF*);
  470.             IF w^.InputWaiting THEN EXIT(*LOOP*) END(*IF*);
  471.             w := w^.next;
  472.         END (*LOOP*);
  473.         Release (StackAccess2);
  474.  
  475.         (* Check whether the cursor should be visible. *)
  476.  
  477.         IF w = NIL THEN
  478.             visible := FALSE;
  479.         ELSE
  480.             row := w^.row;  col := w^.column;
  481.             visible := IdentifyTopWindow (wtop, page, row, col) AND (wtop = w);
  482.         END (*IF*);
  483.  
  484.         (* Turn the cursor on or off, if necessary. *)
  485.  
  486.         WITH PhysicalCursor DO
  487.             Obtain (access4);
  488.             IF (w = NIL) OR (CursorWindow[page] <> w) THEN
  489.                 CursorWindow[page] := w;
  490.                 CursorVisible[page] := visible AND w^.CursorWanted;
  491.                 IF page = ActivePage THEN
  492.                     IF CursorVisible[page] THEN
  493.                         CursorOn (w^.row, w^.column, w^.blockcursor);
  494.                     ELSE
  495.                         CursorOff;
  496.                     END (*IF*);
  497.                 END (*IF*);
  498.             END (*IF*);
  499.             Release (access4);
  500.         END (*WITH*);
  501.     END ComputeCursorWindow;
  502.  
  503. (************************************************************************)
  504. (*                         SCREEN REFRESHING                            *)
  505. (************************************************************************)
  506.  
  507. PROCEDURE PartialRefresh (w: Window;  startrow, endrow: RowRange;
  508.                                         startcol, endcol: ColumnRange);
  509.  
  510.     (* Re-draws the image of window w on the screen, in the area        *)
  511.     (* bounded by the given absolute screen coordinates.  The ranges    *)
  512.     (* specified are inclusive limits.                                  *)
  513.     (* Caller must be executing at level <3.                            *)
  514.  
  515.     VAR i: RowRange;  bytecount: CARDINAL;  offset: BufferSubscript;
  516.  
  517.     BEGIN
  518.         WITH w^ DO
  519.  
  520.             (* Work out the overlap between the region and the window.  *)
  521.  
  522.             IF FirstRow > startrow THEN startrow := FirstRow END (*IF*);
  523.             IF LastRow < endrow THEN endrow := LastRow END (*IF*);
  524.             IF FirstColumn > startcol THEN startcol := FirstColumn END (*IF*);
  525.             IF LastColumn < endcol THEN endcol := LastColumn END (*IF*);
  526.  
  527.             (* Refresh that region, if it is nonempty.  *)
  528.  
  529.             IF (startrow <= endrow) AND (startcol <= endcol) THEN
  530.                 bytecount := BytesPerChar*(endcol - startcol + 1);
  531.                 FOR i := startrow TO endrow DO
  532.                     offset := CharsPerRow*i + startcol;
  533.                     Obtain (ScreenAccess3);
  534.                     OS2.VioWrtCellStr(w^.buffer[offset], bytecount, i, startcol, 0);
  535.                     Release (ScreenAccess3);
  536.                 END (*FOR*);
  537.             END (*IF*);
  538.         END (*WITH*);
  539.     END PartialRefresh;
  540.  
  541. (************************************************************************)
  542.  
  543. PROCEDURE Refresh (w: Window);
  544.  
  545.     (* Re-draws the image of window w on the screen.  (But there's no   *)
  546.     (* physical write if w^.page <> ActivePage.)                        *)
  547.     (* Caller must be executing at level <3.                            *)
  548.  
  549.     VAR i: RowRange;  bytecount: CARDINAL;  offset: BufferSubscript;
  550.  
  551.     BEGIN
  552.         WITH w^ DO
  553.             IF page = ActivePage THEN
  554.                 bytecount := BytesPerChar*(LastColumn - FirstColumn + 1);
  555.                 FOR i := FirstRow TO LastRow DO
  556.                     offset := CharsPerRow*i + FirstColumn;
  557.                     Obtain (ScreenAccess3);
  558.                     OS2.VioWrtCellStr(w^.buffer[offset], bytecount, i, FirstColumn, 0);
  559.                     Release (ScreenAccess3);
  560.                 END (*FOR*);
  561.             END (*IF*);
  562.             obscured := FALSE;
  563.         END (*WITH*);
  564.     END Refresh;
  565.  
  566. (************************************************************************)
  567.  
  568. PROCEDURE ComputeCollisions (w: Window);
  569.  
  570.     (* Updates the "obscured" field of all windows which are below this *)
  571.     (* one on the stack, and sets w^.obscured to FALSE.  Also updates   *)
  572.     (* the cursor visibility information, based on the assumption that  *)
  573.     (* w is the window on top of its stack.                             *)
  574.     (* Caller must have locked StackAccess2.                            *)
  575.     (* Caller must be executing at level <4.                            *)
  576.  
  577.     VAR left, right: ColumnRange;
  578.         top, bottom: RowRange;
  579.         w2: Window;  p: DisplayPage;
  580.  
  581.     BEGIN
  582.         (* Take note of the screen location of this window. *)
  583.  
  584.         WITH w^ DO
  585.             obscured := FALSE;
  586.             left := FirstColumn;  right := LastColumn;
  587.             top := FirstRow;  bottom := LastRow;
  588.             w2 := next;  p := page;
  589.         END (*WITH*);
  590.  
  591.         (* Update the cursor visibility information. *)
  592.  
  593.         WITH PhysicalCursor DO
  594.             Obtain (access4);
  595.             IF w^.InputWaiting THEN
  596.                 CursorWindow[p] := w;
  597.                 CursorVisible[p] := w^.CursorWanted;
  598.             ELSIF CursorVisible[p] THEN
  599.                 WITH CursorWindow[p]^ DO
  600.                     CursorVisible[p] := (row < top) OR (row > bottom)
  601.                                 OR (column < left) OR (column > right);
  602.                 END (*WITH*);
  603.             END (*IF*);
  604.             Release (access4);
  605.         END (*WITH*);
  606.  
  607.         (* Update the "obscured" information for all windows under      *)
  608.         (* the current window.  (For those which are already obscured   *)
  609.         (* by some other window, no further check is needed.)           *)
  610.  
  611.         WHILE w2 <> NIL DO
  612.             WITH w2^ DO
  613.                 IF NOT obscured THEN
  614.                     obscured := (LastColumn >= left) AND (FirstColumn <= right)
  615.                                 AND (LastRow >= top) AND (FirstRow <= bottom);
  616.                 END (*IF*);
  617.             END (*WITH*);
  618.             w2 := w2^.next;
  619.         END (*WHILE*);
  620.     END ComputeCollisions;
  621.  
  622. (************************************************************************)
  623.  
  624. PROCEDURE PutOnTopI (w: Window);
  625.  
  626.     (* Makes w the top of stack, and refreshes its image on the screen. *)
  627.     (* This procedure does the same job as PutOnTop (see below), but    *)
  628.     (* different entry assumptions.                                     *)
  629.     (* The caller must have locked StackAccess2.                        *)
  630.     (* The caller must be executing at level 2.                         *)
  631.  
  632.     BEGIN
  633.         UnLink (w);
  634.         IF TopWindow[w^.page] <> NIL THEN
  635.             TopWindow[w^.page]^.previous := w
  636.         END (*IF*);
  637.         w^.next := TopWindow[w^.page];  TopWindow[w^.page] := w;
  638.         Refresh (w);  ComputeCollisions (w);
  639.         IF w^.page = ActivePage THEN
  640.             UpdatePhysicalCursor;
  641.         END (*IF*);
  642.     END PutOnTopI;
  643.  
  644. (************************************************************************)
  645.  
  646. PROCEDURE PutOnTop (w: Window);
  647.  
  648.     (* Makes w the top of stack, and refreshes its image on the screen. *)
  649.     (* This also unhides w if it was hidden.                            *)
  650.     (* This is the externally callable version.                         *)
  651.     (* The caller must be executing at a level <0.5.                    *)
  652.  
  653.     BEGIN
  654.         Wait (w^.access0p5);
  655.         Obtain (StackAccess2);
  656.         w^.hidden := FALSE;
  657.         IF TopWindow[w^.page] <> w THEN
  658.             PutOnTopI (w);
  659.         ELSIF w^.page = ActivePage THEN
  660.             UpdatePhysicalCursor;
  661.         END (*IF*);
  662.         Release (StackAccess2);
  663.         Signal (w^.access0p5);
  664.     END PutOnTop;
  665.  
  666. (************************************************************************)
  667.  
  668. PROCEDURE Repaint (page: DisplayPage;  startrow, endrow: RowRange;
  669.                                         startcol, endcol: ColumnRange);
  670.  
  671.     (* Repaints the specified (inclusive) rectangular region on the     *)
  672.     (* screen, and sets the physical screen cursor as necessary.        *)
  673.     (* The caller must be executing at level <1.                        *)
  674.  
  675.     CONST NormalVideo = 07H;
  676.  
  677.     VAR i: RowRange;  k: ColumnRange;
  678.         count: CARDINAL;
  679.         p: Window;
  680.  
  681.     BEGIN
  682.         (* First, clear the region.     *)
  683.  
  684.         IF page = ActivePage THEN
  685.             count := endcol - startcol + 1;
  686.             Obtain (BlankRowAccess1);
  687.             FOR k := 0 TO count-1 DO
  688.                 BlankRow[k].attr := NormalVideo;
  689.             END (*FOR*);
  690.             count := BytesPerChar*count;
  691.             FOR i := startrow TO endrow DO
  692.                 Obtain (ScreenAccess3);
  693.                 OS2.VioWrtCellStr(BlankRow[0], count, i, startcol, 0);
  694.                 Release (ScreenAccess3);
  695.             END (*FOR*);
  696.             Release (BlankRowAccess1);
  697.         END (*IF*);
  698.  
  699.         (* Now refresh all open windows (or, more precisely, the parts  *)
  700.         (* of them which lie in the affected region).                   *)
  701.  
  702.         Obtain (StackAccess2);
  703.         WITH PhysicalCursor DO
  704.             Obtain (access4);
  705.             CursorWindow[page] := NIL;
  706.             CursorVisible[page] := FALSE;
  707.             Release (access4);
  708.         END (*WITH*);
  709.         IF TopWindow[page] <> NIL THEN
  710.             p := TopWindow[page];
  711.             WHILE p^.next <> NIL DO  p := p^.next  END (*WHILE*);
  712.             REPEAT
  713.                 IF page = ActivePage THEN
  714.                     PartialRefresh (p, startrow, endrow, startcol, endcol);
  715.                 END (*IF*);
  716.                 ComputeCollisions (p);  p := p^.previous;
  717.             UNTIL p = NIL;
  718.         END (*IF*);
  719.         IF page = ActivePage THEN
  720.             UpdatePhysicalCursor;
  721.         END (*IF*);
  722.         Release (StackAccess2);
  723.     END Repaint;
  724.  
  725. (************************************************************************)
  726.  
  727. PROCEDURE RefreshDisplay;
  728.  
  729.     (* Rewrites every open window.  Should not normally be needed, but  *)
  730.     (* available for use in cases the display is corrupted by, for      *)
  731.     (* example, software which bypasses this module and writes directly *)
  732.     (* to the screen.                                                   *)
  733.     (* The caller must be executing at level <1.                        *)
  734.  
  735.     VAR p: DisplayPage;
  736.  
  737.     BEGIN
  738.         FOR p := 0 TO MAX(DisplayPage) DO
  739.             Repaint (p, 0, MAX(RowRange), 0, MAX(ColumnRange));
  740.         END (*FOR*);
  741.     END RefreshDisplay;
  742.  
  743. (************************************************************************)
  744.  
  745. PROCEDURE Hide (w: Window);
  746.  
  747.     (* Makes this window invisible on the screen.  It is still possible *)
  748.     (* to write to the window, but the output will not appear until     *)
  749.     (* a PutOnTop(w) is executed.                                       *)
  750.     (* The caller must be executing at level <0.5.                      *)
  751.  
  752.     BEGIN
  753.         IF NOT w^.hidden THEN
  754.             Wait (w^.access0p5);
  755.             Obtain (StackAccess2);
  756.             w^.hidden := TRUE;
  757.             UnLink (w);
  758.             Release (StackAccess2);
  759.             Signal (w^.access0p5);
  760.  
  761.             (* Repaint the part of the screen which this window occupied.   *)
  762.  
  763.             WITH w^ DO
  764.                 Repaint (page, FirstRow, LastRow, FirstColumn, LastColumn);
  765.             END (*WITH*);
  766.         END (*IF*);
  767.     END Hide;
  768.  
  769. (************************************************************************)
  770.  
  771. PROCEDURE PutOnPage (w: Window;  p: DisplayPage);
  772.  
  773.     (* Moves window w to another display page.  The default is to put   *)
  774.     (* every window on page 0 when it is first opened.  To override     *)
  775.     (* the default, call this procedure after opening the window.       *)
  776.  
  777.     VAR wasvisible: BOOLEAN;
  778.  
  779.     BEGIN
  780.         wasvisible := NOT w^.hidden;
  781.         Hide (w);
  782.  
  783.         WITH w^ DO
  784.  
  785.             (* Change the page. *)
  786.  
  787.             page := p;
  788.             IF wasvisible THEN
  789.                 hidden := FALSE;  PutOnTop (w);
  790.             END(*IF*);
  791.  
  792.         END (*WITH*);
  793.  
  794.     END PutOnPage;
  795.  
  796. (************************************************************************)
  797. (*                      SETTING THE SCROLLING REGION                    *)
  798. (************************************************************************)
  799.  
  800. PROCEDURE InScrollingRegion (w: Window): BOOLEAN;
  801.  
  802.     (* Returns TRUE iff the current cursor position of window w is      *)
  803.     (* inside its scrolling region.                                     *)
  804.  
  805.     BEGIN
  806.         WITH w^ DO
  807.             WITH ScrollRegion DO
  808.                 RETURN (row >= top) AND (row <= bottom)
  809.                         AND (column >= left) AND (column <= right);
  810.             END (*WITH*);
  811.         END (*WITH*);
  812.     END InScrollingRegion;
  813.  
  814. (************************************************************************)
  815.  
  816. PROCEDURE InExtendedScrollingRegion (w: Window): BOOLEAN;
  817.  
  818.     (* Similar to InScrollingRegion, but also returns TRUE if we are    *)
  819.     (* just off the right edge of the scrolling region.                 *)
  820.  
  821.     BEGIN
  822.         WITH w^ DO
  823.             WITH ScrollRegion DO
  824.                 RETURN (row >= top) AND (row <= bottom)
  825.                         AND (column >= left) AND (column <= right+1);
  826.             END (*WITH*);
  827.         END (*WITH*);
  828.     END InExtendedScrollingRegion;
  829.  
  830. (************************************************************************)
  831.  
  832. PROCEDURE ChangeScrollingRegion (w: Window;  firstline, lastline: RowRange);
  833.  
  834.     (* Changes the scrolling region of window w to the new line         *)
  835.     (* boundaries given, and sets its cursor to the start of the new    *)
  836.     (* scrolling region.  The line numbers are window-relative.         *)
  837.  
  838.     VAR horizontal, vertical, leftT, rightT: CHAR;
  839.         j: ColumnRange;
  840.         place: BufferSubscript;
  841.  
  842.     BEGIN
  843.         (* Although the user specifies window-relative line numbers,    *)
  844.         (* we use screen-relative numbers internally.  Adjust the       *)
  845.         (* parameters to take this into account.                        *)
  846.  
  847.         INC (firstline, w^.FirstRow);
  848.         INC (lastline, w^.FirstRow);
  849.  
  850.         (* Work out what characters to use for the frame and divider.   *)
  851.  
  852.         horizontal := '─';  vertical := '│';
  853.         leftT := '├';  rightT := '┤';
  854.         WITH w^ DO
  855.             IF divider = doubledivider THEN
  856.                 horizontal := '═';
  857.             END (*IF*);
  858.             IF frame = doubleframe THEN
  859.                 vertical := '║';
  860.                 IF divider = doubledivider THEN
  861.                     leftT := '╠';  rightT := '╣';
  862.                 ELSE
  863.                     leftT := '╟';  rightT := '╢';
  864.                 END (*IF*);
  865.             ELSIF divider = doubledivider THEN
  866.                 leftT := '╞';  rightT := '╡';
  867.             END (*IF*);
  868.  
  869.             (* Clean up the frame. *)
  870.  
  871.             ScrollRegion := DefaultScrollRegion;
  872.             IF frame <> noframe THEN
  873.  
  874.                 (* Remove the left and right T belonging to the *)
  875.                 (* old divider bars, if necessary.              *)
  876.  
  877.                 IF ScrollRegion.top - 1 > FirstRow THEN
  878.                     place := CharsPerRow*(ScrollRegion.top - 1) + FirstColumn;
  879.                     buffer[place].val := vertical;
  880.                     buffer[place + LastColumn - FirstColumn].val := vertical;
  881.                 END (*IF*);
  882.  
  883.                 IF ScrollRegion.bottom + 1 < LastRow THEN
  884.                     place := CharsPerRow*(ScrollRegion.bottom+1) + FirstColumn;
  885.                     buffer[place].val := vertical;
  886.                     buffer[place + LastColumn - FirstColumn].val := vertical;
  887.                 END (*IF*);
  888.             END (*IF*);
  889.  
  890.             (* Put in the new divider bars.     *)
  891.  
  892.             IF divider <> nodivider THEN
  893.  
  894.                 (* Put in the top horizontal bar.       *)
  895.  
  896.                 IF firstline > FirstRow + 1 THEN
  897.                     place := CharsPerRow*(firstline-1) + FirstColumn;
  898.                     IF frame <> noframe THEN
  899.                         buffer[place].val := leftT;  INC (place);
  900.                     END (*IF*);
  901.                     FOR j := ScrollRegion.left TO ScrollRegion.right DO
  902.                         buffer[place].val := horizontal;  INC(place);
  903.                     END (*FOR*);
  904.                     IF frame <> noframe THEN
  905.                         buffer[place].val := rightT;
  906.                     END (*IF*);
  907.                 END (*IF*);
  908.  
  909.                 (* Put in the bottom horizontal bar.    *)
  910.  
  911.                 IF lastline < LastRow - 1 THEN
  912.                     place := CharsPerRow*(lastline+1) + FirstColumn;
  913.                     IF frame <> noframe THEN
  914.                         buffer[place].val := leftT;  INC (place);
  915.                     END (*IF*);
  916.                     FOR j := ScrollRegion.left TO ScrollRegion.right DO
  917.                         buffer[place].val := horizontal; INC (place);
  918.                     END (*FOR*);
  919.                     IF frame <> noframe THEN
  920.                         buffer[place].val := rightT;
  921.                     END (*IF*);
  922.                 END (*IF*);
  923.  
  924.             END (*IF*);
  925.  
  926.             (* Finally, update the scrolling region parameters. *)
  927.  
  928.             WITH ScrollRegion DO
  929.                 top := firstline;  bottom := lastline;
  930.             END (*WITH*);
  931.             DefaultScrollRegion := ScrollRegion;
  932.             SetCursor (w, firstline - FirstRow,
  933.                                 ScrollRegion.left - FirstColumn);
  934.             Obtain (StackAccess2);
  935.             IF NOT (hidden OR obscured) THEN
  936.                 Refresh (w);
  937.             END (*IF*);
  938.             Release (StackAccess2);
  939.         END (*WITH*);
  940.     END ChangeScrollingRegion;
  941.  
  942. (************************************************************************)
  943.  
  944. PROCEDURE NewScrollingRegion (w: Window;  firstline, lastline: RowRange;
  945.                                 firstcolumn, lastcolumn: ColumnRange);
  946.  
  947.     (* Changes the scrolling region of w to be the specified rectangle, *)
  948.     (* but unlike ChangeScrollingRegion this procedure does not redraw  *)
  949.     (* the dividers.  Furthermore the old scrolling region set by       *)
  950.     (* ChangeScrollingRegion is remembered and may be restored by a     *)
  951.     (* call to ResetScrollingRegion.                                    *)
  952.  
  953.     BEGIN
  954.         WITH w^ DO
  955.             WITH ScrollRegion DO
  956.                 top := FirstRow+firstline;  bottom := FirstRow+lastline;
  957.                 left := FirstColumn+firstcolumn;
  958.                 right := FirstColumn+lastcolumn;
  959.             END (*WITH*);
  960.         END (*WITH*);
  961.     END NewScrollingRegion;
  962.  
  963. (************************************************************************)
  964.  
  965. PROCEDURE ResetScrollingRegion (w: Window);
  966.  
  967.     (* Changes the scrolling region of w back to what it was the last   *)
  968.     (* time ChangeScrollingRegion was called.  If ChangeScrollingRegion *)
  969.     (* was never called, the scrolling region goes back to being the    *)
  970.     (* entire window minus the frame (if any).                          *)
  971.  
  972.     BEGIN
  973.         w^.ScrollRegion := w^.DefaultScrollRegion;
  974.     END ResetScrollingRegion;
  975.  
  976. (************************************************************************)
  977. (*                          OPENING A WINDOW                            *)
  978. (************************************************************************)
  979.  
  980. PROCEDURE FillInFrame (w: Window);
  981.  
  982.     (* Puts the box around the window into the window buffer.   *)
  983.  
  984.     VAR i: RowRange;  j: ColumnRange;
  985.         corner: ARRAY [1..4] OF CHAR;
  986.         horizontal, vertical: CHAR;
  987.         place, offset: BufferSubscript;
  988.  
  989.     BEGIN
  990.         IF w^.frame = simpleframe THEN
  991.             corner[1] := '┌';  corner[2] := '┐';
  992.             corner[3] := '└';  corner[4] := '┘';
  993.             horizontal := '─';  vertical := '│';
  994.         ELSE
  995.             corner[1] := '╔';  corner[2] := '╗';
  996.             corner[3] := '╚';  corner[4] := '╝';
  997.             horizontal := '═';  vertical := '║';
  998.         END (*IF*);
  999.  
  1000.         WITH w^ DO
  1001.  
  1002.             offset := LastColumn - FirstColumn;
  1003.             place := CharsPerRow*FirstRow + FirstColumn;
  1004.             buffer[place].val := corner[1];
  1005.             buffer[place+offset].val := corner[2];
  1006.             INC (place, CharsPerRow);
  1007.  
  1008.             FOR i := FirstRow + 1 TO LastRow - 1 DO
  1009.                 buffer[place].val := vertical;
  1010.                 buffer[place+offset].val := vertical;
  1011.                 INC (place, CharsPerRow);
  1012.             END (*FOR*);
  1013.  
  1014.             buffer[place].val := corner[3];
  1015.             buffer[place+offset].val := corner[4];
  1016.  
  1017.             offset := CharsPerRow*(LastRow - FirstRow);
  1018.             FOR j := FirstColumn + 1 TO LastColumn - 1 DO
  1019.                 INC (place);
  1020.                 buffer[place-offset].val := horizontal;
  1021.                 buffer[place].val := horizontal;
  1022.             END (*FOR*);
  1023.  
  1024.         END (*WITH*);
  1025.     END FillInFrame;
  1026.  
  1027. (************************************************************************)
  1028.  
  1029. PROCEDURE MakeMonochrome (VAR (*INOUT*) foreground, background: Colour);
  1030.  
  1031.     (* Changes the two given colours to a suitable B/W combination.     *)
  1032.  
  1033.     BEGIN
  1034.         IF (foreground = black) OR (foreground = darkgrey) THEN
  1035.             background := white
  1036.         ELSE
  1037.             IF foreground > white THEN
  1038.                 foreground := intensewhite
  1039.             ELSE
  1040.                 foreground := white;
  1041.             END (*IF*);
  1042.             background := black;
  1043.         END (*IF*);
  1044.     END MakeMonochrome;
  1045.  
  1046. (************************************************************************)
  1047.  
  1048. PROCEDURE Wrap64K (w: Window): BOOLEAN;
  1049.  
  1050.     (* Returns TRUE iff the data buffer crosses a 64Kbyte boundary -    *)
  1051.     (* a case that can give trouble because of a limitation of the      *)
  1052.     (* DMA hardware.                                                    *)
  1053.  
  1054.     VAR first: CARDINAL;
  1055.  
  1056.     BEGIN
  1057.         first := CAST(CARDINAL,ADR(w^.buffer));
  1058.         RETURN HighWord(first) <> HighWord(first + BytesInBuffer - 1);
  1059.     END Wrap64K;
  1060.  
  1061. (************************************************************************)
  1062.  
  1063. PROCEDURE OpenWindowHidden (VAR (*OUT*) w: Window;
  1064.                         ForegroundColour, BackgroundColour: Colour;
  1065.                         firstline, lastline: RowRange;
  1066.                         firstcol, lastcol: ColumnRange;
  1067.                         FrameDesired: FrameType;
  1068.                         DividerDesired: DividerType);
  1069.  
  1070.     (* Like OpenWindow, but the window does not appear on the screen    *)
  1071.     (* until the first PutOnTop(w).                                     *)
  1072.  
  1073.     VAR i: RowRange;  j: ColumnRange;  k: BufferSubscript;
  1074.         w2: Window;
  1075.  
  1076.     BEGIN
  1077.         (* Create the new window, and fill in all its fields.   *)
  1078.  
  1079.         NEW (w);
  1080.         IF Wrap64K(w) THEN
  1081.             w2 := w;  NEW (w);  DISPOSE (w2);
  1082.         END (*IF*);
  1083.         WITH w^ DO
  1084.             CreateSemaphore (access0p5, 0);
  1085.             CloseList := NIL;
  1086.             previous := NIL;  next := NIL;  blockcursor := FALSE;
  1087.             page := DefaultPage;  hidden := TRUE;
  1088.             InputWaiting := FALSE;  CursorWanted := FALSE;
  1089.             WrapOption := TRUE;
  1090.             CreateSemaphore (CharAvailable, 0);
  1091.             foreground := ForegroundColour;  background := BackgroundColour;
  1092.             IF BlackAndWhite OR ForcedMonochrome THEN
  1093.                 MakeMonochrome (foreground, background);
  1094.             END (*IF*);
  1095.             CurrentAttributes := 16*ORD(background) + ORD(foreground);
  1096.             frame := FrameDesired;  divider := DividerDesired;
  1097.             FirstRow := firstline;  LastRow := lastline;
  1098.             FirstColumn := firstcol;  LastColumn := lastcol;
  1099.             tabstops := DefaultTabs;
  1100.             IF frame <> noframe THEN
  1101.                 FOR j := MAX(ColumnRange) TO 1 BY -1 DO
  1102.                     tabstops[j] := tabstops[j-1];
  1103.                 END (*FOR*);
  1104.                 tabstops[0] := " ";
  1105.             END (*IF*);
  1106.  
  1107.             (* Set the window contents to all space characters. *)
  1108.  
  1109.             Obtain (BlankRowAccess1);
  1110.             FOR k := 0 TO CharsPerRow-1 DO
  1111.                 BlankRow[k].attr := CurrentAttributes;
  1112.             END (*FOR*);
  1113.             FOR i := 0 TO MaxRowNumber DO
  1114.                 Copy (ADR(BlankRow), ADR(buffer[i*CharsPerRow]), BytesPerRow);
  1115.             END (*FOR*);
  1116.             Release (BlankRowAccess1);
  1117.  
  1118.             (* Set up a default scrolling region.       *)
  1119.  
  1120.             WITH ScrollRegion DO
  1121.                 top := FirstRow;  bottom := LastRow;
  1122.                 left := FirstColumn;  right := LastColumn;
  1123.             END (*WITH*);
  1124.  
  1125.             (* Make the frame.  *)
  1126.  
  1127.             IF frame <> noframe THEN
  1128.                 FillInFrame(w);
  1129.                 WITH ScrollRegion DO
  1130.                     INC (top);  INC (left);  DEC (bottom);  DEC (right);
  1131.                 END (*WITH*);
  1132.             END (*IF*);
  1133.  
  1134.             DefaultScrollRegion := ScrollRegion;
  1135.             row := ScrollRegion.top;  column := ScrollRegion.left;
  1136.             BufferPosition := CharsPerRow*row + column;
  1137.             Signal (access0p5);
  1138.  
  1139.         END (*WITH*);
  1140.  
  1141.     END OpenWindowHidden;
  1142.  
  1143. (************************************************************************)
  1144.  
  1145. PROCEDURE OpenWindow (VAR (*OUT*) w: Window;
  1146.                         ForegroundColour, BackgroundColour: Colour;
  1147.                         firstline, lastline: RowRange;
  1148.                         firstcol, lastcol: ColumnRange;
  1149.                         FrameDesired: FrameType;
  1150.                         DividerDesired: DividerType);
  1151.  
  1152.     (* Creates a new window, and makes it the current window, filled    *)
  1153.     (* initially with space characters.                                 *)
  1154.     (* The caller must be executing at level <0.5.                      *)
  1155.  
  1156.     BEGIN
  1157.         OpenWindowHidden (w, ForegroundColour, BackgroundColour,
  1158.                         firstline, lastline, firstcol, lastcol,
  1159.                         FrameDesired, DividerDesired);
  1160.         PutOnTop (w);
  1161.     END OpenWindow;
  1162.  
  1163. (************************************************************************)
  1164.  
  1165. PROCEDURE OpenSimpleWindow (VAR (*OUT*) w: Window;
  1166.                         firstline, lastline: RowRange;
  1167.                         firstcol, lastcol: ColumnRange);
  1168.  
  1169.     (* Identical to OpenWindow, except that you don't get any choice    *)
  1170.     (* about the colours or frame.  The window is white-on-black with   *)
  1171.     (* a simple frame and no dividers for the scrolling region.  This   *)
  1172.     (* version of OpenWindow is useful for those with monochrome        *)
  1173.     (* displays who don't want to be bothered with importing the types  *)
  1174.     (* Colour, FrameType, and DividerType.                              *)
  1175.  
  1176.     BEGIN
  1177.         OpenWindow (w, white, black, firstline, lastline,
  1178.                         firstcol, lastcol, simpleframe, nodivider);
  1179.     END OpenSimpleWindow;
  1180.  
  1181. (************************************************************************)
  1182. (*                 CHANGING THE POSITION OF A WINDOW                    *)
  1183. (************************************************************************)
  1184.  
  1185. PROCEDURE ShiftWindowRel (w: Window;  rowchange, columnchange: INTEGER);
  1186.  
  1187.     (* Moves w on the screen.  The second and third arguments may be    *)
  1188.     (* negative.  The amount of move may be reduced to prevent a move   *)
  1189.     (* off the edge of the screen.                                      *)
  1190.     (* The caller must be executing at level <0.5.                      *)
  1191.  
  1192.     VAR byteshift: INTEGER;  wasvisible: BOOLEAN;
  1193.  
  1194.     BEGIN
  1195.         IF (rowchange <> 0) OR (columnchange <> 0) THEN
  1196.             WITH w^ DO
  1197.                 (* Temporarily remove the window from the stack of windows. *)
  1198.  
  1199.                 wasvisible := NOT hidden;  Hide(w);
  1200.  
  1201.                 Wait (access0p5);
  1202.  
  1203.                 (* Clip the shift amount to avoid going off the screen. *)
  1204.  
  1205.                 IF VAL(INTEGER,FirstRow)+rowchange < 0 THEN
  1206.                     rowchange := - VAL(INTEGER,FirstRow)
  1207.                 ELSIF VAL(INTEGER,LastRow)+rowchange > MaxRowNumber THEN
  1208.                     rowchange := MaxRowNumber - LastRow
  1209.                 END (*IF*);
  1210.  
  1211.                 IF VAL(INTEGER,FirstColumn)+columnchange < 0 THEN
  1212.                     columnchange := -VAL(INTEGER,FirstColumn)
  1213.                 ELSIF VAL(INTEGER,LastColumn)+columnchange > MaxColumnNumber THEN
  1214.                     columnchange := MaxColumnNumber - LastColumn
  1215.                 END (*IF*);
  1216.  
  1217.                 byteshift := BytesPerRow*rowchange + BytesPerChar*columnchange;
  1218.  
  1219.                 (* Shift the buffer contents.   *)
  1220.  
  1221.                 IF byteshift < 0 THEN
  1222.                     Copy (ADR(buffer[-byteshift DIV 2]), ADR(buffer[0]),
  1223.                                                 BytesInBuffer+byteshift);
  1224.                 ELSE
  1225.                     CopyUp (Far(ADR(buffer[0])), Far(ADR(buffer[byteshift DIV 2])),
  1226.                                                 BytesInBuffer-byteshift);
  1227.                 END (*IF*);
  1228.  
  1229.                 (* Adjust the affected window parameters.       *)
  1230.  
  1231.                 IF rowchange > 0 THEN
  1232.                     WITH ScrollRegion DO
  1233.                         INC (top, rowchange);  INC (bottom, rowchange);
  1234.                     END (*WITH*);
  1235.                     WITH DefaultScrollRegion DO
  1236.                         INC (top, rowchange);  INC (bottom, rowchange);
  1237.                     END (*WITH*);
  1238.                     INC (FirstRow, rowchange);  INC (LastRow, rowchange);
  1239.                     INC (row, rowchange);
  1240.                 ELSE
  1241.                     rowchange := -rowchange;
  1242.                     WITH ScrollRegion DO
  1243.                         DEC (top, rowchange);  DEC (bottom, rowchange);
  1244.                     END (*WITH*);
  1245.                     WITH DefaultScrollRegion DO
  1246.                         DEC (top, rowchange);  DEC (bottom, rowchange);
  1247.                     END (*WITH*);
  1248.                     DEC (FirstRow, rowchange);  DEC (LastRow, rowchange);
  1249.                     DEC (row, rowchange);
  1250.                 END (*IF*);
  1251.  
  1252.                 IF columnchange > 0 THEN
  1253.                     WITH ScrollRegion DO
  1254.                         INC (left, columnchange);  INC (right, columnchange);
  1255.                     END (*WITH*);
  1256.                     WITH DefaultScrollRegion DO
  1257.                         INC (left, columnchange);  INC (right, columnchange);
  1258.                     END (*WITH*);
  1259.                     INC (FirstColumn, columnchange);
  1260.                     INC (LastColumn, columnchange);
  1261.                     INC (column, columnchange);
  1262.                 ELSE
  1263.                     columnchange := -columnchange;
  1264.                     WITH ScrollRegion DO
  1265.                         DEC (left, columnchange);  DEC (right, columnchange);
  1266.                     END (*WITH*);
  1267.                     WITH DefaultScrollRegion DO
  1268.                         DEC (left, columnchange);  DEC (right, columnchange);
  1269.                     END (*WITH*);
  1270.                     DEC (FirstColumn, columnchange);
  1271.                     DEC (LastColumn, columnchange);
  1272.                     DEC (column, columnchange);
  1273.                 END (*IF*);
  1274.  
  1275.                 IF byteshift > 0 THEN
  1276.                     INC (BufferPosition, byteshift DIV BytesPerChar);
  1277.                 ELSE
  1278.                     DEC (BufferPosition, (-byteshift) DIV BytesPerChar);
  1279.                 END (*IF*);
  1280.  
  1281.                 Signal (access0p5);
  1282.  
  1283.                 (* Put w back onto the stack and onto the screen.       *)
  1284.  
  1285.                 IF wasvisible THEN
  1286.                     hidden := FALSE;  PutOnTop (w);
  1287.                 END (*IF*);
  1288.  
  1289.             END (*WITH*);
  1290.  
  1291.         END (*IF*);
  1292.  
  1293.     END ShiftWindowRel;
  1294.  
  1295. (************************************************************************)
  1296.  
  1297. PROCEDURE ShiftWindowAbs (w: Window;  top: RowRange;  left: ColumnRange);
  1298.  
  1299.     (* Like ShiftWindowRel, except that we directly specify the target  *)
  1300.     (* position of the top left corner in screen coordinates.           *)
  1301.  
  1302.     BEGIN
  1303.         ShiftWindowRel (w, VAL(INTEGER,top)-VAL(INTEGER,w^.FirstRow),
  1304.                                 VAL(INTEGER,left)-VAL(INTEGER,w^.FirstColumn));
  1305.     END ShiftWindowAbs;
  1306.  
  1307. (************************************************************************)
  1308.  
  1309. PROCEDURE WindowLocation (w: Window): Rectangle;
  1310.  
  1311.     (* Returns the current location of w on the screen. *)
  1312.  
  1313.     VAR result: Rectangle;
  1314.  
  1315.     BEGIN
  1316.         WITH w^ DO
  1317.             WITH result DO
  1318.                 top := FirstRow;  bottom := LastRow;
  1319.                 left := FirstColumn;  right := LastColumn;
  1320.             END (*WITH*);
  1321.         END (*WITH*);
  1322.         RETURN result;
  1323.     END WindowLocation;
  1324.  
  1325. (************************************************************************)
  1326. (*                          CLOSING A WINDOW                            *)
  1327. (************************************************************************)
  1328.  
  1329. PROCEDURE InstallCloseHandler (w: Window;  P: CloseHandlerProc);
  1330.  
  1331.     (* Sets up P as a procedure to be called when the window is closed. *)
  1332.     (* It is legal to define multiple handlers for the same window.     *)
  1333.  
  1334.     VAR HLP: CloseHandlerList;
  1335.  
  1336.     BEGIN
  1337.         NEW (HLP);
  1338.         WITH w^ DO
  1339.             Wait (access0p5);
  1340.             HLP^.next := CloseList;
  1341.             HLP^.proc := P;
  1342.             CloseList := HLP;
  1343.             Signal (access0p5);
  1344.         END (*WITH*);
  1345.     END InstallCloseHandler;
  1346.  
  1347. (************************************************************************)
  1348.  
  1349. PROCEDURE CloseWindow (w: Window);
  1350.  
  1351.     (* Reclaims the buffer space used for this window, and removes its  *)
  1352.     (* image on the screen.                                             *)
  1353.     (* The caller must be executing at level <0.5.                      *)
  1354.  
  1355.     VAR p: CloseHandlerList;
  1356.  
  1357.     BEGIN
  1358.         Hide (w);
  1359.         WITH w^ DO
  1360.             Wait (access0p5);
  1361.             WHILE CloseList <> NIL DO
  1362.                 p := CloseList^.next;
  1363.                 CloseList^.proc (w, w^.page);
  1364.                 DISPOSE (CloseList);
  1365.                 CloseList := p;
  1366.             END (*WHILE*);
  1367.             Signal (access0p5);
  1368.             DestroySemaphore (access0p5);
  1369.         END (*WITH*);
  1370.         DISPOSE (w);
  1371.     END CloseWindow;
  1372.  
  1373. (************************************************************************)
  1374. (*                        CHANGING OPTIONS                              *)
  1375. (************************************************************************)
  1376.  
  1377. PROCEDURE SetWrapOption (w: Window;  enabled: BOOLEAN);
  1378.  
  1379.     (* If the parameter is TRUE - this is the initial default - then    *)
  1380.     (* subsequent text written to the window will wrap to the next      *)
  1381.     (* line when it hits the right of the scrolling region.  Setting    *)
  1382.     (* the parameter to FALSE disables this feature.                    *)
  1383.  
  1384.     BEGIN
  1385.         w^.WrapOption := enabled;
  1386.     END SetWrapOption;
  1387.  
  1388. (************************************************************************)
  1389. (*              OPERATIONS ON CHARACTER ATTRIBUTES                      *)
  1390. (************************************************************************)
  1391.  
  1392. PROCEDURE SetColours (w: Window; r: RowRange; c: ColumnRange;
  1393.                                 nchar: CARDINAL;  fore, back: Colour);
  1394.  
  1395.     (* Sets a field of nchar characters, starting at (row,col), to      *)
  1396.     (* the specified foreground and background colours.  The location   *)
  1397.     (* is given in window-relative coordinates, not absolute screen     *)
  1398.     (* positions.  NOTE: This procedure will not wrap around to a new   *)
  1399.     (* row.  The caller must be executing at level <3.                  *)
  1400.  
  1401.     VAR k, start: BufferSubscript;  attributes: CARD8;
  1402.  
  1403.     BEGIN
  1404.         attributes := 16*ORD(back) + ORD(fore);
  1405.         WITH w^ DO
  1406.             INC (r, FirstRow);  INC (c, FirstColumn);
  1407.             start := CharsPerRow*r + c;
  1408.             FOR k := start TO start+nchar-1 DO
  1409.                 buffer[k].attr := attributes;
  1410.             END (*FOR*);
  1411.             IF NOT hidden THEN
  1412.                 Obtain (StackAccess2);
  1413.                 IF obscured THEN PutOnTopI(w)
  1414.                 ELSIF page = ActivePage THEN
  1415.                     OS2.VioWrtNAttr(attributes, nchar, r, c, 0);
  1416.                 END (*IF obscured*);
  1417.                 Release (StackAccess2);
  1418.             END (*IF NOT hidden*);
  1419.         END (*WITH*);
  1420.     END SetColours;
  1421.  
  1422. (************************************************************************)
  1423.  
  1424. PROCEDURE ColourSwap (w: Window; r: RowRange; c: ColumnRange;
  1425.                                                         nchar: CARDINAL);
  1426.  
  1427.     (* Switches the foreground and background colours for nchar         *)
  1428.     (* characters, starting at location (r,c).  The row and column      *)
  1429.     (* numbers are window-relative, not absolute screen coordinates.    *)
  1430.     (* This is our colour equivalent of the "reverse video" operation.  *)
  1431.     (* NOTE: This procedure will not wrap around to a new row.          *)
  1432.     (* The caller must be executing at level <3.                        *)
  1433.  
  1434.     VAR k, start: BufferSubscript;  oldattribute: CARD8;
  1435.  
  1436.     BEGIN
  1437.         WITH w^ DO
  1438.             start := CharsPerRow*(r+FirstRow) + c + FirstColumn;
  1439.             FOR k := start TO start+nchar-1 DO
  1440.                 oldattribute := buffer[k].attr;
  1441.                 buffer[k].attr := 16*(oldattribute MOD 16)
  1442.                                         + (oldattribute DIV 16);
  1443.             END (*FOR*);
  1444.             IF NOT hidden THEN
  1445.                 Obtain (StackAccess2);
  1446.                 IF obscured THEN PutOnTopI(w)
  1447.                 ELSIF page = ActivePage THEN
  1448.                     Obtain (ScreenAccess3);
  1449.                     OS2.VioWrtCellStr(w^.buffer[start], 2*nchar, r+FirstRow, c+FirstColumn, 0);
  1450.                     Release (ScreenAccess3);
  1451.                 END (*IF obscured*);
  1452.                 Release (StackAccess2);
  1453.             END (*IF NOT hidden*);
  1454.         END (*WITH*);
  1455.     END ColourSwap;
  1456.  
  1457. (************************************************************************)
  1458.  
  1459. PROCEDURE Blink (w: Window; r: RowRange; c: ColumnRange; nchar: CARDINAL);
  1460.  
  1461.     (* Toggles the blinking status - that is, turns blinking on if it   *)
  1462.     (* was off, and vice versa - for nchar characters, starting at      *)
  1463.     (* relative location (r,c) in window w.                             *)
  1464.     (* NOTE: This procedure will not wrap around to a new row.          *)
  1465.     (* The caller must be executing at level <3.                        *)
  1466.  
  1467.     VAR k, start: BufferSubscript;
  1468.  
  1469.     BEGIN
  1470.         WITH w^ DO
  1471.             start := CharsPerRow*(r+FirstRow) + c + FirstColumn;
  1472.             FOR k := start TO start+nchar-1 DO
  1473.                 buffer[k].attr := IXORB(buffer[k].attr, 80H);
  1474.             END (*FOR*);
  1475.             IF NOT hidden THEN
  1476.                 Obtain (StackAccess2);
  1477.                 IF obscured THEN PutOnTopI(w)
  1478.                 ELSIF page = ActivePage THEN
  1479.                     Obtain (ScreenAccess3);
  1480.                     OS2.VioWrtCellStr(w^.buffer[start], 2*nchar, r+FirstRow, c+FirstColumn, 0);
  1481.                     Release (ScreenAccess3);
  1482.                 END (*IF obscured*);
  1483.                 Release (StackAccess2);
  1484.             END (*IF NOT hidden*);
  1485.         END (*WITH*);
  1486.     END Blink;
  1487.  
  1488. (************************************************************************)
  1489. (*                          CURSOR OPERATIONS                           *)
  1490. (************************************************************************)
  1491.  
  1492. PROCEDURE SetCursor (w: Window; r: RowRange; c: ColumnRange);
  1493.  
  1494.     (* Sets the cursor for window w to relative row r, column c.        *)
  1495.     (* The caller must be executing at level <0.5.                      *)
  1496.  
  1497.     BEGIN
  1498.         WITH w^ DO
  1499.             Wait (access0p5);
  1500.             row := r + FirstRow;  column := c + FirstColumn;
  1501.             BufferPosition := CharsPerRow*row + column;
  1502.             Signal (access0p5);
  1503.         END (*WITH*);
  1504.     END SetCursor;
  1505.  
  1506. (************************************************************************)
  1507.  
  1508. PROCEDURE SaveCursor (w: Window; VAR (*OUT*) r, c: CARDINAL);
  1509.  
  1510.     (* Returns the current cursor position of window w.         *)
  1511.  
  1512.     BEGIN
  1513.         WITH w^ DO
  1514.             r := row - FirstRow;  c := column - FirstColumn;
  1515.         END (*WITH*);
  1516.     END SaveCursor;
  1517.  
  1518. (************************************************************************)
  1519.  
  1520. PROCEDURE CursorLeft (w: Window);
  1521.  
  1522.     (* Moves the window cursor one position left.  If it falls off the  *)
  1523.     (* left edge of the window, move to the right edge in the same row. *)
  1524.  
  1525.     BEGIN
  1526.         WITH w^ DO
  1527.             IF column = FirstColumn THEN
  1528.                 column := LastColumn;
  1529.                 BufferPosition := CharsPerRow*row + column;
  1530.             ELSE
  1531.                 DEC (column);  DEC (BufferPosition);
  1532.             END (*IF*);
  1533.         END (*WITH*);
  1534.     END CursorLeft;
  1535.  
  1536. (************************************************************************)
  1537.  
  1538. PROCEDURE CursorRight (w: Window);
  1539.  
  1540.     (* Moves the window cursor one position right.  If it falls off the *)
  1541.     (* right edge of the window, move to the left edge in the same row. *)
  1542.  
  1543.     BEGIN
  1544.         WITH w^ DO
  1545.             IF column = LastColumn THEN
  1546.                 column := FirstColumn;
  1547.                 BufferPosition := CharsPerRow*row + column;
  1548.             ELSE
  1549.                 INC (column);  INC (BufferPosition);
  1550.             END (*IF*);
  1551.         END (*WITH*);
  1552.     END CursorRight;
  1553.  
  1554. (************************************************************************)
  1555.  
  1556. PROCEDURE CursorUp (w: Window);
  1557.  
  1558.     (* Moves the window cursor one position up.  If it falls off the    *)
  1559.     (* top edge of the window, it moves to the bottom edge in the same  *)
  1560.     (* column.                                                          *)
  1561.  
  1562.     BEGIN
  1563.         WITH w^ DO
  1564.             IF row = FirstRow THEN
  1565.                 row := LastRow;
  1566.                 BufferPosition := CharsPerRow*row + column;
  1567.             ELSE
  1568.                 DEC (row);  DEC (BufferPosition, CharsPerRow);
  1569.             END (*IF*);
  1570.         END (*WITH*);
  1571.     END CursorUp;
  1572.  
  1573. (************************************************************************)
  1574.  
  1575. PROCEDURE CursorDown (w: Window);
  1576.  
  1577.     (* Moves the window cursor one position down.  If it falls off the  *)
  1578.     (* bottom edge of the window, it moves to the top edge in the same  *)
  1579.     (* column.                                                          *)
  1580.  
  1581.     BEGIN
  1582.         WITH w^ DO
  1583.             IF row = LastRow THEN
  1584.                 row := FirstRow;
  1585.                 BufferPosition := CharsPerRow*row + column;
  1586.             ELSE
  1587.                 INC (row);  INC (BufferPosition, CharsPerRow);
  1588.             END (*IF*);
  1589.         END (*WITH*);
  1590.     END CursorDown;
  1591.  
  1592. (************************************************************************)
  1593.  
  1594. PROCEDURE ScrollUpI (w: Window);
  1595.  
  1596.     (* The version of ScrollUp (see below) for internal use.            *)
  1597.     (* The caller must be executing at level <1.                        *)
  1598.  
  1599.     VAR rownum: RowRange;  count: CARDINAL;
  1600.         k: BufferSubscript;
  1601.         srcptr, destptr: FarPointer;
  1602.  
  1603.     BEGIN
  1604.         WITH w^ DO
  1605.             WITH ScrollRegion DO
  1606.                 k := CharsPerRow*top + left;
  1607.                 count := BytesPerChar*(right-left+1);
  1608.                 destptr := Far(ADR(buffer[k]));
  1609.  
  1610.                 (* Move the contents of the scrolling region up in the buffer. *)
  1611.  
  1612.                 FOR rownum := top TO bottom-1 DO
  1613.                     srcptr := FarAddOffset (destptr, BytesPerRow);
  1614.                     FarCopy (srcptr, destptr, count);
  1615.                     destptr := srcptr;
  1616.                 END (*FOR*);
  1617.                 Obtain (BlankRowAccess1);
  1618.  
  1619.                 (* Fill in the attributes of BlankRow. *)
  1620.  
  1621.                 FOR k := 0 TO CharsPerRow-1 DO
  1622.                     BlankRow[k].attr := CurrentAttributes;
  1623.                 END (*FOR*);
  1624.  
  1625.                 (* Blank the bottom line of scrolling region in the buffer. *)
  1626.  
  1627.                 FarCopy (Far(ADR(BlankRow)), destptr, count);
  1628.  
  1629.                 (* That's the buffer done, now scroll what's on the screen. *)
  1630.  
  1631.                 IF NOT(obscured OR hidden) AND (page = ActivePage) THEN
  1632.                     Obtain (ScreenAccess3);
  1633.                     OS2.VioScrollUp (top, left, bottom, right, 1, BlankRow[0], 0);
  1634.                     Release (ScreenAccess3);
  1635.                 END (*IF*);
  1636.  
  1637.                 Release (BlankRowAccess1);
  1638.  
  1639.             END (*WITH*);
  1640.  
  1641.             IF obscured AND NOT hidden THEN
  1642.                 Obtain (StackAccess2);
  1643.                 PutOnTopI(w);
  1644.                 Release (StackAccess2);
  1645.             END (*IF*);
  1646.  
  1647.         END (*WITH*);
  1648.  
  1649.     END ScrollUpI;
  1650.  
  1651. (************************************************************************)
  1652.  
  1653. PROCEDURE ScrollUp (w: Window);
  1654.  
  1655.     (* Scrolls window w up by one line, both on the screen and in its   *)
  1656.     (* buffer.  The last row is filled with spaces.                     *)
  1657.     (* The caller must be executing at level <0.5.                      *)
  1658.  
  1659.     BEGIN
  1660.         Wait (w^.access0p5);
  1661.         ScrollUpI (w);
  1662.         Signal (w^.access0p5);
  1663.     END ScrollUp;
  1664.  
  1665. (************************************************************************)
  1666.  
  1667. PROCEDURE ScrollDown (w: Window);
  1668.  
  1669.     (* Scrolls window w down by one line, both on the screen and in its *)
  1670.     (* buffer.  The first row is filled with spaces.                    *)
  1671.     (* The caller must be executing at level <0.5.                      *)
  1672.  
  1673.     VAR k: BufferSubscript;
  1674.  
  1675.     BEGIN
  1676.         WITH w^ DO
  1677.             Wait (access0p5);
  1678.             Obtain (BlankRowAccess1);
  1679.             FOR k := 0 TO CharsPerRow-1 DO
  1680.                 BlankRow[k].attr := CurrentAttributes;
  1681.             END (*FOR*);
  1682.             WITH ScrollRegion DO
  1683.                 k := CharsPerRow * top;
  1684.                 IF bottom > top THEN
  1685.                     CopyUp (Far(ADR(buffer[k])), Far(ADR(buffer[k+CharsPerRow])),
  1686.                                     BytesPerRow*(bottom-top));
  1687.                 END (*IF*);
  1688.                 Copy (ADR(BlankRow), ADR(buffer[k+left]),
  1689.                         BytesPerChar*(right-left+1));
  1690.             END (*WITH*);
  1691.             Release (BlankRowAccess1);
  1692.             IF NOT hidden THEN
  1693.                 Obtain (StackAccess2);
  1694.                 IF obscured THEN PutOnTopI(w) ELSE Refresh (w);
  1695.                 END (*IF*);
  1696.                 Release (StackAccess2);
  1697.             END (*IF*);
  1698.             Signal (access0p5);
  1699.         END (*WITH*);
  1700.     END ScrollDown;
  1701.  
  1702. (************************************************************************)
  1703. (*                          MAIN OUTPUT ROUTINES                        *)
  1704. (************************************************************************)
  1705.  
  1706. PROCEDURE WriteLnI (w: Window);
  1707.  
  1708.     (* The internal version of WriteLn (see below).     *)
  1709.     (* The caller must be executing at level <1.        *)
  1710.  
  1711.     BEGIN
  1712.         WITH w^ DO
  1713.             IF InExtendedScrollingRegion (w) THEN
  1714.                 column := ScrollRegion.left;
  1715.                 IF row = ScrollRegion.bottom THEN ScrollUpI (w)
  1716.                 ELSE INC (row);
  1717.                 END (*IF*);
  1718.             ELSE
  1719.                 column := DefaultScrollRegion.left;
  1720.                 IF row >= LastRow THEN row := LastRow
  1721.                 ELSE INC (row);
  1722.                 END (*IF*);
  1723.             END (*IF*);
  1724.             BufferPosition := CharsPerRow*row + column;
  1725.         END (*WITH*);
  1726.     END WriteLnI;
  1727.  
  1728. (************************************************************************)
  1729.  
  1730. PROCEDURE WriteLn (w: Window);
  1731.  
  1732.     (* Moves the cursor of window w to the start of the next row.  If   *)
  1733.     (* we are already at the last row, the window scrolls up.           *)
  1734.  
  1735.     BEGIN
  1736.         Wait (w^.access0p5);
  1737.         WriteLnI (w);
  1738.         Signal (w^.access0p5);
  1739.     END WriteLn;
  1740.  
  1741. (************************************************************************)
  1742.  
  1743. PROCEDURE WriteChar (w: Window; ch: CHAR);
  1744.  
  1745.     (* Writes one character to window w, and updates the cursor for     *)
  1746.     (* this window.  As a side-effect, this window becomes the          *)
  1747.     (* currently active window if it was obscured.  Wraps around to the *)
  1748.     (* next line if we are about to run off the end of the current      *)
  1749.     (* line.  This procedure does not recognise the concept of a        *)
  1750.     (* control character.  Every possible value of ch produces          *)
  1751.     (* something readable on the screen.                                *)
  1752.     (* The caller must be executing at level <0.5.                      *)
  1753.  
  1754.     BEGIN
  1755.         WITH w^ DO
  1756.             Wait (access0p5);
  1757.  
  1758.             (* Wrap to a new line if we about to leave the scrolling    *)
  1759.             (* region or if we are outside the legal writing region.    *)
  1760.  
  1761.             IF WrapOption AND ((column = ScrollRegion.right + 1)
  1762.                            OR (column > DefaultScrollRegion.right)) THEN
  1763.                 DEC (column);  WriteLnI (w);
  1764.             END (*IF*);
  1765.  
  1766.             buffer[BufferPosition].val := ch;
  1767.             buffer[BufferPosition].attr := CurrentAttributes;
  1768.  
  1769.             IF NOT hidden THEN
  1770.                 Obtain (StackAccess2);
  1771.                 IF obscured THEN PutOnTopI(w) END (*IF*);
  1772.                 IF page = ActivePage THEN
  1773.                     Obtain (ScreenAccess3);
  1774.                     OS2.VioWrtCellStr(w^.buffer[BufferPosition], 2, row, column, 0);
  1775.                     Release (ScreenAccess3);
  1776.                 END (*IF*);
  1777.                 Release (StackAccess2);
  1778.             END (*IF NOT hidden*);
  1779.  
  1780.             (* Note that the following statement may cause column to    *)
  1781.             (* go beyond the edge of the window; but this will be       *)
  1782.             (* picked up on the next call to WriteChar.  We prefer not  *)
  1783.             (* to do a WriteLn just yet, because that could cause an    *)
  1784.             (* unintended scroll operation when writing to the bottom   *)
  1785.             (* right of the window.                                     *)
  1786.  
  1787.             INC (column);  INC (BufferPosition);
  1788.  
  1789.             Signal (access0p5);
  1790.  
  1791.         END (*WITH*);
  1792.     END WriteChar;
  1793.  
  1794. (************************************************************************)
  1795.  
  1796. PROCEDURE WriteString (w: Window; text: ARRAY OF CHAR);
  1797.  
  1798.     (* Writes a sequence of characters, terminated either by NUL or by  *)
  1799.     (* the end of the array.                                            *)
  1800.  
  1801.     VAR j: CARDINAL;
  1802.  
  1803.     BEGIN
  1804.         j := 0;
  1805.         LOOP
  1806.             IF ORD (text[j]) = 0 THEN EXIT (*LOOP*)  END (*IF*);
  1807.             WriteChar (w, text[j]);  INC (j);
  1808.             IF j > HIGH (text) THEN EXIT (*LOOP*)  END (*IF*);
  1809.         END (*LOOP*);
  1810.     END WriteString;
  1811.  
  1812. (************************************************************************)
  1813.  
  1814. PROCEDURE Write (w: Window; ch: CHAR);
  1815.  
  1816.     (* A version of procedure WriteChar which looks after some of the   *)
  1817.     (* control characters.                                              *)
  1818.  
  1819.     BEGIN
  1820.         IF ch >= " " THEN WriteChar (w, ch)
  1821.         ELSIF ORD(ch) = 8 THEN          (* backspace *)
  1822.             CursorLeft(w)
  1823.         ELSIF ORD(ch) = 9 THEN          (* tab *)
  1824.             WITH w^ DO
  1825.                 REPEAT
  1826.                     WriteChar (w, " ");
  1827.                 UNTIL (column=MAX(ColumnRange)) OR (tabstops[column]="T");
  1828.             END (*WITH*);
  1829.         ELSIF ORD(ch) = 10 THEN         (* line feed - ignore *)
  1830.         ELSIF ORD(ch) = 13 THEN         (* carriage return *)
  1831.             WriteLn(w)
  1832.         ELSE                            (* other control character *)
  1833.             WriteChar (w, "^");  WriteChar (w, CHR(ORD(ch)+64))
  1834.         END (*IF*);
  1835.     END Write;
  1836.  
  1837. (************************************************************************)
  1838. (*                              INPUT                                   *)
  1839. (************************************************************************)
  1840.  
  1841. PROCEDURE ReadBack (w: Window;  r: RowRange;  c: ColumnRange): CHAR;
  1842.  
  1843.     (* Returns the character which currently occupies relative location *)
  1844.     (* (r,c) on the display of window w.                                *)
  1845.  
  1846.     BEGIN
  1847.         WITH w^ DO
  1848.             RETURN buffer[CharsPerRow*(r+FirstRow) + c + FirstColumn].val;
  1849.         END (*WITH*);
  1850.     END ReadBack;
  1851.  
  1852. (************************************************************************)
  1853.  
  1854. PROCEDURE KeyTask;
  1855.  
  1856.     (* Runs as a separate task, getting a character from the keyboard   *)
  1857.     (* as needed and making it available to the task which has input    *)
  1858.     (* focus.  If no task has input focus, the character is returned    *)
  1859.     (* to the keyboard module.                                          *)
  1860.  
  1861.     VAR ch: CHAR;  w: Window;
  1862.  
  1863.     BEGIN
  1864.         LOOP
  1865.             Wait (InputRequest);
  1866.             ch := InKey();
  1867.             WITH PhysicalCursor DO
  1868.                 Obtain (access4);
  1869.                 w := CursorWindow[ActivePage];
  1870.                 CursorWindow[ActivePage] := NIL;
  1871.                 CursorVisible[ActivePage] := FALSE;
  1872.                 Release (access4);
  1873.             END (*WITH*);
  1874.             IF w = NIL THEN
  1875.                 PutBack(ch);
  1876.             ELSE
  1877.                 WITH w^ DO
  1878.                     InputChar := ch;
  1879.                     InputWaiting := FALSE;
  1880.                     ComputeCursorWindow (ActivePage);
  1881.                     Signal (CharAvailable);
  1882.                 END (*WITH*);
  1883.             END (*IF*);
  1884.         END (*LOOP*);
  1885.     END KeyTask;
  1886.  
  1887. (************************************************************************)
  1888.  
  1889. PROCEDURE GetKey (w: Window): CHAR;
  1890.  
  1891.     (* Read one character, without any prompt to the user (unless the   *)
  1892.     (* caller has already set w^.CursorWanted to TRUE).  The reason for *)
  1893.     (* specifying a window parameter is to ensure that keyboard input   *)
  1894.     (* comes to us only when this window has input focus.               *)
  1895.  
  1896.     BEGIN
  1897.         w^.InputWaiting := TRUE;
  1898.         ComputeCursorWindow (w^.page);
  1899.         Signal (InputRequest);
  1900.         Wait (w^.CharAvailable);
  1901.         RETURN w^.InputChar;
  1902.     END GetKey;
  1903.  
  1904. (************************************************************************)
  1905.  
  1906. PROCEDURE ReadCharWithoutEcho (w: Window;  VAR (*OUT*) ch: CHAR);
  1907.  
  1908.     (* Read one character, with a blinking cursor in window w as a      *)
  1909.     (* prompt.                                                          *)
  1910.  
  1911.     VAR SaveCursorState: BOOLEAN;
  1912.  
  1913.     BEGIN
  1914.         SaveCursorState := w^.CursorWanted;
  1915.         w^.CursorWanted := TRUE;
  1916.         ch := GetKey (w);
  1917.         w^.CursorWanted := SaveCursorState;
  1918.     END ReadCharWithoutEcho;
  1919.  
  1920. (************************************************************************)
  1921.  
  1922. PROCEDURE ReadChar (w: Window;  VAR (*OUT*) ch: CHAR);
  1923.  
  1924.     (* Like ReadCharWithoutEcho, but the input character is echoed.     *)
  1925.  
  1926.     BEGIN
  1927.         ReadCharWithoutEcho (w, ch);  Write (w, ch);
  1928.     END ReadChar;
  1929.  
  1930. (************************************************************************)
  1931.  
  1932. PROCEDURE LookaheadChar (w: Window): CHAR;
  1933.  
  1934.     (* Reads a character without consuming it.  That is, the character  *)
  1935.     (* remains available to be read by ReadChar.  This allows the       *)
  1936.     (* caller to check whether the character is really wanted.          *)
  1937.  
  1938.     VAR ch: CHAR;
  1939.  
  1940.     BEGIN
  1941.         ch := GetKey(w);  PutBack (ch);
  1942.         RETURN ch;
  1943.     END LookaheadChar;
  1944.  
  1945. (************************************************************************)
  1946.  
  1947. PROCEDURE PressAnyKey (w: Window);
  1948.  
  1949.     (* Types a "Press any key to continue" message.     *)
  1950.  
  1951.     VAR dummy: CHAR;
  1952.  
  1953.     BEGIN
  1954.         WriteLn (w);
  1955.         WriteString (w, "Press any key to continue.");
  1956.         ReadCharWithoutEcho (w, dummy);
  1957.         IF ORD(dummy) = 0 THEN ReadCharWithoutEcho (w, dummy) END (*IF*);
  1958.         EraseLine (w, 0);
  1959.     END PressAnyKey;
  1960.  
  1961. (************************************************************************)
  1962.  
  1963. PROCEDURE ReadString (w: Window;  VAR (*OUT*) result: ARRAY OF CHAR);
  1964.  
  1965.     (* Reads a character string, terminated by carriage return.         *)
  1966.  
  1967.     VAR j: CARDINAL;  ch: CHAR;
  1968.  
  1969.     BEGIN
  1970.         FOR j := 0 TO HIGH(result) DO
  1971.             result[j] := " ";
  1972.         END (*FOR*);
  1973.         j := 0;
  1974.         LOOP
  1975.             ReadChar (w, ch);
  1976.             IF ORD(ch) = 13 THEN
  1977.                 result[j] := CHR(0);  EXIT(*LOOP*)
  1978.             ELSIF ORD(ch) = 8 THEN      (* backspace *)
  1979.                 IF j > 0 THEN
  1980.                     CursorLeft(w);  WriteChar(w, " ");  CursorLeft(w);
  1981.                     DEC (j);
  1982.                 END (*IF*);
  1983.             ELSE
  1984.                 result[j] := ch;
  1985.                 IF j = HIGH(result) THEN EXIT(*LOOP*) END(*IF*);
  1986.                 INC (j);
  1987.             END(*IF*);
  1988.         END (*LOOP*);
  1989.     END ReadString;
  1990.  
  1991. (************************************************************************)
  1992.  
  1993. PROCEDURE EditString (w: Window;  VAR (*INOUT*) result: ARRAY OF CHAR;
  1994.                                                 fieldsize: CARDINAL);
  1995.  
  1996.     (* Reads a character string, where a default result is supplied by  *)
  1997.     (* the caller.  The final result is the state of the string at the  *)
  1998.     (* time where the keyboard user types a carriage return or Esc, or  *)
  1999.     (* uses a cursor movement key to move out of the displayed field.   *)
  2000.     (* The terminating character remains available, via Keyboard.InKey, *)
  2001.     (* to the caller.  At most fieldsize characters of the string can   *)
  2002.     (* be edited, and perhaps fewer if the result array is smaller or   *)
  2003.     (* if there is insufficient space in the window.                    *)
  2004.  
  2005.     CONST Esc = CHR(01BH);  Space = " ";
  2006.  
  2007.     VAR place, k: CARDINAL;  ch: CHAR;  limit: ColumnRange;
  2008.         SavedAttributes: CARD8;
  2009.         startrow, startcolumn: CARDINAL;
  2010.         InsertMode, SavedCursorType: BOOLEAN;
  2011.  
  2012.     (********************************************************************)
  2013.  
  2014.     PROCEDURE RewriteString ();
  2015.  
  2016.         BEGIN
  2017.             SetCursor (w, startrow, startcolumn);
  2018.             WriteString (w, result);
  2019.             SetCursor (w, startrow, startcolumn+place);
  2020.         END RewriteString;
  2021.  
  2022.     (********************************************************************)
  2023.  
  2024.     PROCEDURE GoToEnd;
  2025.  
  2026.         (* Puts the cursor just after the last non-blank character.     *)
  2027.  
  2028.         BEGIN
  2029.             place := limit+1;
  2030.             WHILE (place > 0) AND (result[place-1] = Space) DO
  2031.                 DEC (place);
  2032.             END (*WHILE*);
  2033.             SetCursor (w, startrow, startcolumn+place);
  2034.         END GoToEnd;
  2035.  
  2036.     (********************************************************************)
  2037.  
  2038.     PROCEDURE HandleControlChar(): BOOLEAN;
  2039.  
  2040.         (* Called after detecting the CHR(0) which means that a control *)
  2041.         (* character has been typed.  Performs the appropriate actions, *)
  2042.         (* returns TRUE iff editing is finished.                        *)
  2043.  
  2044.         VAR k: CARDINAL;
  2045.  
  2046.         BEGIN
  2047.             ch := GetKey (w);
  2048.             IF ch = "K" THEN                            (* cursor left *)
  2049.                 IF place = 0 THEN
  2050.                     PutBack(ch);  PutBack(CHR(0));
  2051.                     RETURN TRUE;
  2052.                 END (*IF*);
  2053.                 CursorLeft(w);  DEC (place);
  2054.             ELSIF ch = "M" THEN                         (* cursor right *)
  2055.                 IF place > limit THEN
  2056.                     PutBack(ch);  PutBack(CHR(0));
  2057.                     RETURN TRUE;
  2058.                 END (*IF*);
  2059.                 CursorRight(w);  INC (place);
  2060.             ELSIF (ch = "H") OR (ch = "P") THEN         (* cursor up/down *)
  2061.                 PutBack(ch);  PutBack(CHR(0));
  2062.                 RETURN TRUE;
  2063.             ELSIF ch = "G" THEN                         (* home *)
  2064.                 place := 0;
  2065.                 SetCursor (w, startrow, startcolumn);
  2066.             ELSIF ch = "O" THEN                         (* end *)
  2067.                 GoToEnd;
  2068.             ELSIF ch = "R" THEN                         (* insert *)
  2069.                 w^.blockcursor := InsertMode;
  2070.                 InsertMode := NOT InsertMode;
  2071.             ELSIF ch = "S" THEN                         (* delete right *)
  2072.                 IF place < limit THEN
  2073.                     FOR k := place TO limit-1 DO
  2074.                         result[k] := result[k+1];
  2075.                     END (*FOR*);
  2076.                 END (*IF*);
  2077.                 IF place <= limit THEN
  2078.                     result[limit] := Space;
  2079.                     RewriteString ();
  2080.                 END (*IF*);
  2081.             END (*IF*);
  2082.             RETURN FALSE;
  2083.         END HandleControlChar;
  2084.  
  2085.     (********************************************************************)
  2086.  
  2087.     BEGIN       (* Body of EditString *)
  2088.  
  2089.         SaveCursor (w, startrow, startcolumn);
  2090.         SavedCursorType := w^.blockcursor;
  2091.         InsertMode := FALSE;  w^.blockcursor := TRUE;
  2092.  
  2093.         (* Compute a limit which stops us from running off the window.  *)
  2094.  
  2095.         WITH w^ DO
  2096.             IF InScrollingRegion(w) THEN
  2097.                 limit := ScrollRegion.right;
  2098.             ELSE
  2099.                 limit := DefaultScrollRegion.right;
  2100.             END (*IF*);
  2101.             DEC (limit, FirstColumn + startcolumn);
  2102.             SavedAttributes := CurrentAttributes;
  2103.         END (*WITH*);
  2104.         IF HIGH(result) < limit THEN
  2105.             limit := HIGH(result);
  2106.         END (*IF*);
  2107.         IF fieldsize <= limit THEN
  2108.             limit := fieldsize - 1;
  2109.         END (*IF*);
  2110.  
  2111.         (* Preprocessing: for a Nul-terminated string, remove the Nul   *)
  2112.         (* and pad out the string with spaces at the right.  Otherwise  *)
  2113.         (* we get problems if, for example, the Nul is deleted.         *)
  2114.  
  2115.         place := 0;
  2116.         LOOP
  2117.             IF result[place] = CHR(0) THEN
  2118.                 FOR k := place TO limit DO
  2119.                     result[k] := Space;
  2120.                 END (*FOR*);
  2121.                 EXIT (*LOOP*);
  2122.             END (*IF*);
  2123.             IF place = limit THEN EXIT(*LOOP*) END(*IF*);
  2124.             INC (place);
  2125.         END (*LOOP*);
  2126.         FOR k := limit+1 TO HIGH(result) DO
  2127.             result[k] := CHR(0);
  2128.         END (*FOR*);
  2129.  
  2130.         (* Write the string, using reverse video.       *)
  2131.  
  2132.         WriteString (w, result);
  2133.         ColourSwap (w, startrow, startcolumn, limit+1);
  2134.         WITH w^ DO
  2135.             CurrentAttributes := 16*ORD(foreground) + ORD(background);
  2136.         END (*WITH*);
  2137.         place := 0;
  2138.         SetCursor (w, startrow, startcolumn);
  2139.         PutOnTop(w);
  2140.  
  2141.         (* Now the main editing loop.   *)
  2142.  
  2143.         LOOP
  2144.             ReadCharWithoutEcho (w, ch);
  2145.             IF ORD(ch) = 0 THEN                         (* control char *)
  2146.                 IF HandleControlChar() THEN
  2147.                     EXIT (*LOOP*);
  2148.                 END (*IF*);
  2149.             ELSIF (ch = Esc) OR (ORD(ch) = 13) THEN     (* Esc or Return *)
  2150.                 PutBack(ch);  EXIT(*LOOP*);
  2151.             ELSIF ORD(ch) = 8 THEN                      (* delete left *)
  2152.                 IF place > 0 THEN
  2153.                     DEC (place);
  2154.                     IF place < limit THEN
  2155.                         FOR k := place TO limit-1 DO
  2156.                             result[k] := result[k+1];
  2157.                         END (*FOR*);
  2158.                     END (*IF*);
  2159.                     result[limit] := Space;
  2160.                     RewriteString ();
  2161.                 END (*IF*);
  2162.             ELSIF place <= limit THEN                   (* any other char *)
  2163.                 IF InsertMode THEN
  2164.                     FOR k := limit TO place+1 BY -1 DO
  2165.                         result[k] := result[k-1];
  2166.                     END (*FOR*);
  2167.                     RewriteString ();
  2168.                 END (*IF*);
  2169.                 result[place] := ch;  WriteChar (w, ch);
  2170.                 INC (place);
  2171.             END(*IF*);
  2172.         END (*LOOP*);
  2173.         ColourSwap (w, startrow, startcolumn, limit+1);
  2174.         w^.blockcursor := SavedCursorType;
  2175.         w^.CurrentAttributes := SavedAttributes;
  2176.     END EditString;
  2177.  
  2178. (************************************************************************)
  2179.  
  2180. PROCEDURE EditAborted (): BOOLEAN;
  2181.  
  2182.     (* Checks the next keyboard input.  Returns TRUE for Escape, FALSE  *)
  2183.     (* for anything else.  Escape or Carriage Return are consumed, any  *)
  2184.     (* other character is returned to the Keyboard module.              *)
  2185.  
  2186.     CONST Esc = CHR(01BH);  CR = CHR(0DH);
  2187.  
  2188.     VAR ch: CHAR;
  2189.  
  2190.     BEGIN
  2191.         ch := InKey();
  2192.         IF ch = Esc THEN RETURN TRUE
  2193.         ELSIF ch = CR THEN RETURN FALSE
  2194.         ELSE
  2195.             PutBack(ch);  RETURN FALSE;
  2196.         END (*IF*);
  2197.     END EditAborted;
  2198.  
  2199. (************************************************************************)
  2200. (*                  MISCELLANEOUS CONTROL OPERATIONS                    *)
  2201. (************************************************************************)
  2202.  
  2203. PROCEDURE EraseLine (w: Window;  option: CARDINAL);
  2204.  
  2205.     (* Replaces some or all of the current line, except for the border, *)
  2206.     (* with space characters.  The window cursor is moved to the        *)
  2207.     (* location of the first erased character.  The options are:        *)
  2208.     (*          0       the whole of the line, except for the border    *)
  2209.     (*          1       from the current cursor position onwards        *)
  2210.     (*          2       from the start to just before the cursor        *)
  2211.     (* If we are inside a scrolling region, then only that part of the  *)
  2212.     (* line inside the scrolling region is affected.                    *)
  2213.  
  2214.     VAR first, last: ColumnRange;
  2215.         k, firstk, lastk: BufferSubscript;
  2216.  
  2217.     BEGIN
  2218.         WITH w^ DO
  2219.             IF InScrollingRegion(w) THEN
  2220.                 first := ScrollRegion.left;  last := ScrollRegion.right;
  2221.             ELSE
  2222.                 first := DefaultScrollRegion.left;
  2223.                 last := DefaultScrollRegion.right;
  2224.             END (*IF*);
  2225.             IF option = 1 THEN first := column
  2226.             ELSIF option = 2 THEN last := column - 1
  2227.             END (*IF*);
  2228.             IF last >= first THEN
  2229.                 firstk := CharsPerRow*row + first;
  2230.                 lastk := CharsPerRow*row + last;
  2231.                 FOR k := firstk TO lastk DO
  2232.                     buffer[k].val := " ";
  2233.                     buffer[k].attr := CurrentAttributes;
  2234.                 END (*FOR*);
  2235.                 IF NOT(hidden OR obscured) AND (page = ActivePage) THEN
  2236.                     Obtain (ScreenAccess3);
  2237.                     OS2.VioWrtCellStr(w^.buffer[firstk], 2*(last-first+1), row, first, 0);
  2238.                     Release (ScreenAccess3);
  2239.                 END (*IF*);
  2240.                 column := first;  BufferPosition := firstk;
  2241.             END (*IF*);
  2242.         END (*WITH*);
  2243.     END EraseLine;
  2244.  
  2245. (************************************************************************)
  2246. (*                           TERMINATION                                *)
  2247. (************************************************************************)
  2248.  
  2249. (*
  2250. PROCEDURE DumpWindowLocks;
  2251.  
  2252.     (* For debugging: gives the current state of each lock belonging    *)
  2253.     (* to this module.                                                  *)
  2254.  
  2255.     BEGIN
  2256.         DumpString ("PageChangeListAccess: ");  DumpLockState (PageChangeListAccess);
  2257.         DumpEOL;
  2258.         DumpString ("BlankRowAccess1: ");  DumpLockState (BlankRowAccess1);
  2259.         DumpEOL;
  2260.         DumpString ("StackAccess2: ");  DumpLockState (StackAccess2);
  2261.         DumpEOL;
  2262.         DumpString ("ScreenAccess3: ");  DumpLockState (ScreenAccess3);
  2263.         DumpEOL;
  2264.         DumpString ("PhysicalCursor.access4: ");  DumpLockState (PhysicalCursor.access4);
  2265.         DumpEOL;
  2266.     END DumpWindowLocks;
  2267. *)
  2268.  
  2269. (************************************************************************)
  2270.  
  2271. PROCEDURE CloseAllWindows;
  2272.  
  2273.     (* Shutdown of this module is done in two phases.  This procedure   *)
  2274.     (* is phase 2, executed after all interrupt handlers have been      *)
  2275.     (* de-installed.  At this stage we can be confident that the only   *)
  2276.     (* possible task switches can be those triggered by an explicit     *)
  2277.     (* kernel call.                                                     *)
  2278.  
  2279.     VAR w: Window;  p: DisplayPage;
  2280.  
  2281.     BEGIN
  2282.         ReleaseAllLocks;
  2283.  
  2284.         FOR p := 0 TO MAX(DisplayPage) DO
  2285.             LOOP
  2286.                 w := TopWindow[p];
  2287.                 IF w = NIL THEN EXIT(*LOOP*) END(*IF*);
  2288.  
  2289.                 (* We locked access to this window in phase 1, so       *)
  2290.                 (* now we have to unlock it.                            *)
  2291.  
  2292.                 Signal (w^.access0p5);
  2293.                 CloseWindow (w);
  2294.             END (*LOOP*);
  2295.         END (*FOR*);
  2296.  
  2297.     END CloseAllWindows;
  2298.  
  2299. (************************************************************************)
  2300.  
  2301. PROCEDURE CleanUp;
  2302.  
  2303.     (* Phase 1 of module termination.  If termination was caused by an  *)
  2304.     (* error, displays the error and waits for the user to press a key. *)
  2305.     (* In order to ensure that the error message is not obscured, we    *)
  2306.     (* freeze all windows.                                              *)
  2307.  
  2308.     VAR w: Window;  p: DisplayPage;
  2309.         message: ARRAY [0..57] OF CHAR;
  2310.  
  2311.     BEGIN
  2312.         (* Enable phase 2 of the shutdown. *)
  2313.  
  2314.         SetTerminationProcedure (CloseAllWindows);
  2315.  
  2316.         (* Note that we cannot know which task is running the shutdown  *)
  2317.         (* code, or the point it was up to when termination was         *)
  2318.         (* triggered.  To avoid potential deadlocks, we must throw      *)
  2319.         (* away any locks we are holding.                               *)
  2320.  
  2321.         ReleaseAllLocks;
  2322.  
  2323.         (* Lock all open windows.       *)
  2324.  
  2325.         FOR p := 0 TO MAX(DisplayPage) DO
  2326.             Obtain (StackAccess2);
  2327.             w := TopWindow[p];
  2328.             Release (StackAccess2);
  2329.             WHILE w <> NIL DO
  2330.                 IF SemaphoreHolder (w^.access0p5) <> CurrentTaskID() THEN
  2331.                     Wait (w^.access0p5);
  2332.                 END (*IF*);
  2333.                 w := w^.next;
  2334.             END (*WHILE*);
  2335.         END (*FOR*);
  2336.  
  2337.         (* For abnormal termination, write the error message, and wait  *)
  2338.         (* until the user has responded with a keystroke.               *)
  2339.  
  2340.         SetActivePage (0);
  2341.         IF TerminationMessage(message) THEN
  2342.             OpenSimpleWindow (w, 10, 13, 10, 69);
  2343.             WriteString (w, message);
  2344.             PressAnyKey (w);
  2345.             CloseWindow (w);
  2346.         END (*IF*);
  2347.  
  2348.     END CleanUp;
  2349.  
  2350. (************************************************************************)
  2351. (*                          INITIALISATION                              *)
  2352. (************************************************************************)
  2353.  
  2354. VAR j: BufferSubscript;  p: DisplayPage;
  2355.  
  2356. BEGIN
  2357.  
  2358.     FOR j := 0 TO CharsPerRow-1 DO
  2359.         BlankRow[j].val := " ";
  2360.     END (*FOR*);
  2361.     CreateLock (BlankRowAccess1);
  2362.  
  2363.     FOR p := 0 TO MAX(DisplayPage) DO
  2364.         TopWindow[p] := NIL;
  2365.         PhysicalCursor.CursorWindow[p] := NIL;
  2366.         PhysicalCursor.CursorVisible[p] := FALSE;
  2367.     END (*FOR*);
  2368.     PhysicalCursor.ScreenPos := 0;
  2369.     PhysicalCursor.Attributes := 0;
  2370.  
  2371.     CreateLock (StackAccess2);
  2372.     CreateLock (ScreenAccess3);
  2373.     CreateLock (PhysicalCursor.access4);
  2374.     CreateSemaphore (InputRequest, 0);
  2375.     CreateTask (KeyTask, 5, "keyboard/windows");
  2376.  
  2377.     PageChangeProcs := NIL;
  2378.     CreateLock (PageChangeListAccess);
  2379.  
  2380.     (* Blank the screen, to erase otherwise annoying background stuff   *)
  2381.     (* left by other programs.                                          *)
  2382.  
  2383.     OS2.VioSetAnsi (0, 0);
  2384.     SetActivePage (DefaultPage);
  2385.     Repaint (DefaultPage, 0,MaxRowNumber,0,MaxColumnNumber);
  2386.  
  2387. FINALLY
  2388.  
  2389.     CleanUp;
  2390.  
  2391. END Windows.
  2392.  
  2393.