DEFINITION MODULE Windows; (****************************************************************) (* *) (* Text-mode screen windows. *) (* *) (* Programmer: P. Moylan *) (* Last edited: 10 December 1994 *) (* Status: Working *) (* *) (****************************************************************) (************************************************************************) (* *) (* REMARK: There are two versions of the basic 'write' procedure. *) (* Procedure WriteChar treats every character as a printable character, *) (* so it can write any character in the standard character set. This *) (* is the output procedure recommended for general use. Control *) (* operations are supplied by separate procedures such as WriteLn, *) (* SetCursor, etc., so there is no need to resort to the BASIC *) (* programmers' trick of writing long strings of obscure control codes. *) (* But for people who can't break this bad habit, procedure Write is *) (* supplied; this treats character codes 0..31 as control codes. *) (* Its main intended uses are to echo keyboard input, and to print a *) (* file which contains embedded control characters; but it may be *) (* used for other purposes if desired. *) (* *) (* Warning: I have not bothered to look after all the control codes, *) (* just the ones I found a use for. Most of them print as a *) (* two-character sequence ^. *) (* *) (************************************************************************) (************************************************************************) (* *) (* NOTE ON CRITICAL SECTIONS: When using this module in a multitasking *) (* environment, there are numerous critical sections because several *) (* tasks may be using the same physical screen. These sections are *) (* protected by semaphores. (For the case where we are not using the *) (* multitasking kernel, there is a version of module Semaphores which *) (* contains dummy procedures). The general philosophy is to assume *) (* that critical section protection is needed for inter-window *) (* conflicts, but that no protection is needed for operations on a *) (* single window because the most common situation is that each window *) (* is used by only one task. If there happen to be windows which are *) (* shared by two or more tasks, the associated synchronization problems *) (* must be resolved by the caller; they are not resolved in module *) (* Windows, on the grounds that the extra overhead is not justified *) (* just to support a case which rarely happens in practice. *) (* *) (* *) (* LATEST DEVELOPMENT *) (* *) (* Because of the addition of the MouseControl module, it can happen in *) (* practice, so I've added partial protection. The following *) (* procedures are indivisible with respect to one another, as far as *) (* shared uses of a window are concerned: *) (* OpenWindow, CloseWindow, ShiftWindowRel, ScrollUp, ScrollDown, *) (* WriteChar, WriteLn, SetCursor, Hide, PutOnTop. *) (* Minor technicality: in some cases there is an implied PutOnTop, or *) (* similar side-effect, and this is not always part of the indivisible *) (* section. I can't think of any case where this would matter to the *) (* user. I don't like making protected sections too large, since this *) (* can degrade overall system responsiveness. *) (* *) (* Note: procedures WriteString and Write, while not indivisible in *) (* the above sense, are protected at the "write a character" level. *) (* *) (************************************************************************) CONST MaxRowNumber = 24; MaxColumnNumber = 79; TYPE Window; (* is private *) Colour = (black, blue, green, cyan, red, magenta, brown, white, darkgrey, lightblue, lightgreen, lightcyan, lightred, lightmagenta, yellow, intensewhite); (* Note: Any of these colours may be used as a foreground colour, *) (* but in most applications only [black..white] are suitable as *) (* background colours. The others may be used, but they can give *) (* strange effects such as a blinking display. Use with care! *) RowRange = [0..MaxRowNumber]; ColumnRange = [0..MaxColumnNumber]; Rectangle = RECORD top, bottom: RowRange; left, right: ColumnRange; END (*RECORD*); FrameType = (noframe, simpleframe, doubleframe); DividerType = (nodivider, simpledivider, doubledivider); DisplayPage = [0..3]; PROCEDURE OpenWindow (VAR (*OUT*) w: Window; ForegroundColour, BackgroundColour: Colour; firstline, lastline: RowRange; firstcol, lastcol: ColumnRange; FrameDesired: FrameType; DividerDesired: DividerType); (* Create a new window. Note that row and column numbers start *) (* from 0. NOTE: If the window has a box drawn around it (the case *) (* FrameDesired <> noframe), this subtracts from the space *) (* available for text. *) PROCEDURE OpenSimpleWindow (VAR (*OUT*) w: Window; firstline, lastline: RowRange; firstcol, lastcol: ColumnRange); (* Identical to OpenWindow, except that you don't get any choice *) (* about the colours or frame. The window is white-on-black with *) (* a simple frame and no dividers for the scrolling region. This *) (* version of OpenWindow is useful for those with monochrome *) (* displays who don't want to be bothered with importing the types *) (* Colour, FrameType, and DividerType. *) PROCEDURE ChangeScrollingRegion (w: Window; firstline, lastline: RowRange); (* Changes the scrolling region of window w to the new line *) (* boundaries given, and sets the cursor of this window to the *) (* start of the scrolling region. Row numbers are window-relative; *) (* that is, line 0 is the top line of the window (which is where *) (* the border is, unless you have no border). *) PROCEDURE NewScrollingRegion (w: Window; firstline, lastline: RowRange; firstcolumn, lastcolumn: ColumnRange); (* Changes the scrolling region of w to be the specified rectangle, *) (* but unlike ChangeScrollingRegion this procedure does not redraw *) (* the dividers. Furthermore the old scrolling region set by *) (* ChangeScrollingRegion is remembered and may be restored by a *) (* call to ResetScrollingRegion. *) PROCEDURE ResetScrollingRegion (w: Window); (* Changes the scrolling region of w back to what it was the last *) (* time ChangeScrollingRegion was called. If ChangeScrollingRegion *) (* was never called, the scrolling region goes back to being the *) (* entire window minus the frame (if any). *) PROCEDURE ShiftWindowRel (w: Window; rowchange, columnchange: INTEGER); (* Moves w on the screen. The second and third arguments may be *) (* negative. The amount of move may be reduced to prevent a move *) (* off the edge of the screen. *) PROCEDURE ShiftWindowAbs (w: Window; top: RowRange; left: ColumnRange); (* Like ShiftWindowRel, except that we directly specify the target *) (* position of the top left corner in screen coordinates. *) PROCEDURE CloseWindow (w: Window); (* Destroys the window. Note that this could have side-effects *) (* if procedure InstallCloseHandler (see below) has been called. *) PROCEDURE Hide (w: Window); (* Makes this window invisible on the screen. It is still possible *) (* to write to the window, but the output will not appear until *) (* a PutOnTop(w) is executed. *) PROCEDURE PutOnPage (w: Window; p: DisplayPage); (* Moves window w to another display page. The default is to put *) (* every window on page 0 when it is first opened. To override *) (* the default, call this procedure after opening the window. *) PROCEDURE PageOf (w: Window): DisplayPage; (* Returns the display page on which window w resides. *) PROCEDURE PutOnTop (w: Window); (* Makes sure that window w is fully visible on the screen. (This *) (* also cancels the effect of a Hide(w).) Rarely needed, since *) (* many window operations automatically put their window on top. *) PROCEDURE IdentifyTopWindow (VAR (*OUT*) w: Window; VAR (*INOUT*) row: RowRange; VAR (*INOUT*) col: ColumnRange): BOOLEAN; (* On entry w is unspecified and (row,col) describes a position on *) (* the screen. On exit w is equal to the top window containing *) (* this screen location, and (row,col) have been altered to be *) (* window-relative coordinates. Exception: if there is no visible *) (* window containing the given point, the function result is FALSE, *) (* the returned w is meaningless, and row and col are unchanged. *) PROCEDURE RefreshDisplay; (* Rewrites every open window. Should not normally be needed, but *) (* available for use in cases the display is corrupted by, for *) (* example, software which bypasses this module and writes directly *) (* to the screen. *) PROCEDURE SetActivePage (page: DisplayPage); (* Changes the active display page. Not needed unless you are *) (* switching among multiple pages. *) TYPE PageChangeHandler = PROCEDURE (DisplayPage); PROCEDURE RequestPageChangeNotification (Proc: PageChangeHandler); (* Sets up Proc as a procedure to be called on a page change. *) TYPE CloseHandlerProc = PROCEDURE (Window, DisplayPage); PROCEDURE InstallCloseHandler (w: Window; P: CloseHandlerProc); (* Sets up P as a procedure to be called when the window is closed. *) (* It is legal to define multiple handlers for the same window. *) PROCEDURE WindowLocation (w: Window): Rectangle; (* Returns the current location of w on the screen. *) PROCEDURE SetCursor (w: Window; row: RowRange; column: ColumnRange); (* Sets the cursor for window w to the given row and column. The *) (* coordinates are window-relative; that is, they start at (0,0) at *) (* the top left of the window. *) PROCEDURE SaveCursor (w: Window; VAR (*OUT*) row, column: CARDINAL); (* Returns the current cursor position. The coordinates are *) (* window-relative; that is, they start at (0,0) at the top left of *) (* the window. *) PROCEDURE CursorLeft (w: Window); (* Moves the window cursor one position left. If it falls off the *) (* left edge of the window, it moves to the right edge in the same *) (* row. *) PROCEDURE CursorRight (w: Window); (* Moves the window cursor one position right. If it falls off the *) (* right edge of the window, it moves to the left edge in the same *) (* row. *) PROCEDURE CursorUp (w: Window); (* Moves the window cursor one position up. If it falls off the *) (* top edge of the window, it moves to the bottom edge in the same *) (* column. *) PROCEDURE CursorDown (w: Window); (* Moves the window cursor one position down. If it falls off the *) (* bottom edge of the window, it moves to the top edge in the same *) (* column. *) PROCEDURE ScrollUp (w: Window); (* Scrolls the scrolling region of window w up by one row, filling *) (* the vacated row with spaces. *) PROCEDURE ScrollDown (w: Window); (* Scrolls the scrolling region of window w down by one row, *) (* filling the vacated row with spaces. *) PROCEDURE WriteLn (w: Window); (* Go to next line in window, scrolling if necessary. N.B. The *) (* window does not scroll if you are not in the scrolling region *) (* at the time of the WriteLn. *) PROCEDURE WriteChar (w: Window; ch: CHAR); (* Write one character. Control characters are not given special *) (* treatment; they produce something visible just like any other *) (* character. Wraps to the next line before writing if the write *) (* would put us on or beyond the right border of w. *) PROCEDURE Write (w: Window; ch: CHAR); (* Like WriteChar, but codes in the range 0..31 are treated as *) (* control characters. This procedure is not recommended for *) (* general use, as it leads to obscure programs. (Instead, do the *) (* control operations by direct calls to the cursor control *) (* procedures which are also supplied in this module). It is *) (* supplied mainly to help those who are used to the conventions of *) (* the "standard" Modula-2 I/O modules such as InOut. *) PROCEDURE WriteString (w: Window; text: ARRAY OF CHAR); (* Write a string of characters, stopping at the first NUL *) (* character or the end of the array, whichever comes first. *) PROCEDURE ReadBack (w: Window; r: RowRange; c: ColumnRange): CHAR; (* Returns the character which currently occupies relative location *) (* (r,c) on the display of window w. *) PROCEDURE ReadChar (w: Window; VAR (*OUT*) ch: CHAR); (* Read one character, and echo it. *) PROCEDURE LookaheadChar (w: Window): CHAR; (* Reads a character without consuming it. That is, the character *) (* remains available to be read by ReadChar. This allows the *) (* caller to check whether the character is really wanted. *) PROCEDURE ReadCharWithoutEcho (w: Window; VAR (*OUT*) ch: CHAR); (* Read one character, but don't echo it. However, a blinking *) (* cursor is still displayed to prompt for the character. (If you *) (* don't want the blinking cursor, use procedure GetKey). *) PROCEDURE GetKey (w: Window): CHAR; (* Read one character, without any prompt to the user. The reason *) (* for specifying a window parameter is to ensure that keyboard *) (* input comes to us only when this window has input focus. *) (* (If you want the input regardless of input focus, use procedure *) (* Keyboard.InKey). *) PROCEDURE PressAnyKey (w: Window); (* Types a "Press any key to continue" message. *) PROCEDURE ReadString (w: Window; VAR (*OUT*) result: ARRAY OF CHAR); (* Reads a character string, terminated by carriage return. *) PROCEDURE EditString (w: Window; VAR (*INOUT*) result: ARRAY OF CHAR; fieldsize: CARDINAL); (* Reads a character string, where a default result is supplied by *) (* the caller. The final result is the state of the string at the *) (* time where the keyboard user types a carriage return or Esc, or *) (* uses a cursor movement key to move out of the displayed field. *) (* The terminating character remains available, via Keyboard.InKey, *) (* to the caller. At most fieldsize characters of the string can *) (* be edited, and perhaps fewer if the result array is smaller or *) (* if there is insufficient space in the window. *) PROCEDURE EditAborted (): BOOLEAN; (* Checks the next keyboard input. Returns TRUE for Escape, FALSE *) (* for anything else. Escape or Carriage Return are consumed, any *) (* other character is returned to the Keyboard module. *) PROCEDURE SetColours (w: Window; row: RowRange; col: ColumnRange; nchar: CARDINAL; foreground, background: Colour); (* Sets a field of nchar characters, starting at (row,col), to *) (* the specified foreground and background colours. The location *) (* is given in window-relative coordinates, not absolute screen *) (* positions. NOTE: Do not assume that this procedure can wrap *) (* around to a new line. It normally cannot. *) PROCEDURE ColourSwap (w: Window; row: RowRange; col: ColumnRange; nchar: CARDINAL); (* Sets a field of nchar characters, starting at (row,col), to *) (* "reverse video" by swapping the foreground and background *) (* colours. Notice that the process is reversible: you get back to *) (* "normal video" by calling this procedure again. The location is *) (* given in window-relative coordinates, not absolute screen *) (* positions. NOTE: Do not assume that this procedure can wrap *) (* around to a new line. It normally cannot. *) PROCEDURE Blink (w: Window; r: RowRange; c: ColumnRange; nchar: CARDINAL); (* Toggles the blinking status - that is, turns blinking on if it *) (* was off, and vice versa - for nchar characters, starting at *) (* relative location (r,c) in window w. *) (* NOTE: This procedure will not wrap around to a new row. *) PROCEDURE EraseLine (w: Window; option: CARDINAL); (* Erases some or all of the current line (but never the border). *) (* The erased characters are replaced by space characters. The *) (* window cursor is moved to the location of the first erased *) (* character. If w is not the currently active window, the changes *) (* will not be visible until w is on top again. The options are: *) (* 0 the whole of the line, except for the border *) (* 1 from the current cursor position onwards *) (* 2 from the start to just before the cursor *) (* If we are inside a scrolling region, then only that part of the *) (* line inside the scrolling region is affected. *) END Windows.