(*----------------------------------------------------------------------*) (* PIBSCREN.PAS --- Screen Handling Routines for Turbo Pascal *) (*----------------------------------------------------------------------*) (* *) (* Author: Philip R. Burns *) (* *) (* Date: Version 1.0: January, 1985 (Part of PibMenus) *) (* Version 1.1: March, 1985 (Part of PibMenus) *) (* Version 1.2: May, 1985 (Part of PibMenus) *) (* Version 2.0: June, 1985 (Split from PibMenus) *) (* *) (* Systems: For MS-DOS on IBM PCs and close compatibles only. *) (* Note: I have checked these on Zenith 151s under *) (* MSDOS 2.1 and IBM PCs under PCDOS 2.0. *) (* *) (* History: These routines provide a simple windowing facility for *) (* Turbo Pascal as well as routines for direct access to the *) (* screen memory area. *) (* *) (* The windowing facility provides windows similar to those *) (* implemented in QMODEM by John Friel III. *) (* *) (* Version 1.0 of these routines formed part of the *) (* PIBMENUS.PAS include file. These routines were split off *) (* into a separate PIBSCREN.PAS file at version 2.0. *) (* *) (* Thanks to Mike Harrington for an elegant way of finding *) (* the current upper left corner of a window without using *) (* the kludge implemented in version 1.1. *) (* *) (* Suggestions for improvements or corrections are welcome. *) (* Please leave messages on Gene Plantz's BBS (312) 882 4145 *) (* or Ron Fox's BBS (312) 940 6496. *) (* *) (* If you use this code in your own programs, please be nice *) (* and give all of us credit. *) (* *) (*----------------------------------------------------------------------*) (* *) (* Needs: These routines need the include files MINMAX.PAS, *) (* GLOBTYPE.PAS, ASCII.PAS, and INT24.PAS. These files are not *) (* included here, since Turbo regrettably does not allow *) (* nested includes. *) (* *) (*----------------------------------------------------------------------*) (* *) (* Note that code for stacked windows is available here. You may *) (* want to modify this to use compile-time window spaces, or remove *) (* the current push-down stack structure. *) (* *) (*----------------------------------------------------------------------*) (*----------------------------------------------------------------------*) (* Constants, Types, and Variables for Screen Access *) (*----------------------------------------------------------------------*) CONST Color_Screen_Address = $B800; (* Address of color screen *) Mono_Screen_Address = $B000; (* Address of mono screen *) Screen_Length = 4000; (* 80 x 25 x 2 = screen area length *) Max_Saved_Screen = 5; (* Maximum no. of saved screens *) TYPE (* A screen image *) Screen_Type = ARRAY[ 1 .. Screen_Length ] OF BYTE; Screen_Ptr = ^Screen_Image_Type; Screen_Image_Type = RECORD Screen_Image: Screen_Type; END; (* Screen stack entries *) Saved_Screen_Ptr = ^Saved_Screen_Type; Saved_Screen_Type = RECORD Screen_Image : Screen_Type; Screen_Row : INTEGER; Screen_Column : INTEGER; Screen_X1 : INTEGER; Screen_Y1 : INTEGER; Screen_X2 : INTEGER; Screen_Y2 : INTEGER; END; VAR (* Memory-mapped screen area *) Actual_Screen : Screen_Ptr; (* Saves screen behind menus *) Saved_Screen : Saved_Screen_Ptr; (* Stack of saved screens *) Saved_Screen_List : ARRAY[ 1 .. Max_Saved_Screen ] OF Saved_Screen_Ptr; (* STRUCTURED *) CONST (* Depth of saved screen stack *) Current_Saved_Screen : 0 .. Max_Saved_Screen = 0; (*----------------------------------------------------------------------*) (* Color_Screen_Active --- Determine if color or mono screen *) (*----------------------------------------------------------------------*) FUNCTION Color_Screen_Active : BOOLEAN; (*----------------------------------------------------------------------*) (* *) (* Function: Color_Screen_Active *) (* *) (* Purpose: Determines if color or mono screen active *) (* *) (* Calling Sequence: *) (* *) (* Color_Active := Color_Screen_Active : BOOLEAN; *) (* *) (* Color_Active --- set to TRUE if the color screen is *) (* active, FALSE if the mono screen is *) (* active. *) (* *) (* Calls: INTR *) (* *) (*----------------------------------------------------------------------*) VAR Regs : RegPack; BEGIN (* Color_Screen_Active *) Regs.Ax := 15 SHL 8; INTR( $10 , Regs ); Color_Screen_Active := ( Regs.Al <> 7 ); End (* Color_Screen_Active *); (*----------------------------------------------------------------------*) (* Current_Video_Mode --- Determine current video mode setting *) (*----------------------------------------------------------------------*) FUNCTION Current_Video_Mode: INTEGER; (*----------------------------------------------------------------------*) (* *) (* Function: Current_Video_Mode *) (* *) (* Purpose: Gets current video mode setting from system *) (* *) (* Calling Sequence: *) (* *) (* Current_Mode := Current_Video_Mode : INTEGER; *) (* *) (* Current_Mode --- set to integer representing current *) (* video mode inherited from system. *) (* *) (* Calls: INTR *) (* *) (*----------------------------------------------------------------------*) VAR Regs : RegPack; BEGIN (* Current_Video_Mode *) Regs.Ax := 15 SHL 8; INTR( $10 , Regs ); Current_Video_Mode := Regs.Al; End (* Current_Video_Mode *); (*----------------------------------------------------------------------*) (* Get_Screen_Address --- Get address of current screen *) (*----------------------------------------------------------------------*) PROCEDURE Get_Screen_Address( VAR Actual_Screen : Screen_Ptr ); (*----------------------------------------------------------------------*) (* *) (* Procedure: Get_Screen_Address *) (* *) (* Purpose: Gets screen address for current type of display *) (* *) (* Calling Sequence: *) (* *) (* Get_Screen_Address( Var Actual_Screen : Screen_Ptr ); *) (* *) (* Actual_Screen --- pointer whose value receives the *) (* current screen address. *) (* *) (* Calls: Color_Screen_Active *) (* PTR *) (* *) (*----------------------------------------------------------------------*) BEGIN (* Get_Screen_Address *) IF Color_Screen_Active THEN Actual_Screen := PTR( Color_Screen_Address , 0 ) ELSE Actual_Screen := PTR( Mono_Screen_Address , 0 ); END (* Get_Screen_Address *); (*----------------------------------------------------------------------*) (* Video Display Control Routines *) (*----------------------------------------------------------------------*) (* *) (* RvsVideoOn --- Turn On Reverse Video *) (* RvsVideoOff --- Turn Off Reverse Video *) (* *) (*----------------------------------------------------------------------*) PROCEDURE RvsVideoOn( Foreground_Color, Background_Color : INTEGER ); BEGIN (* RvsVideoOn *) TextColor ( Background_color ); TextBackGround( Foreground_color ); END (* RvsVideoOn *); (*----------------------------------------------------------------------*) PROCEDURE RvsVideoOff( Foreground_Color, Background_Color : INTEGER ); BEGIN (* RvsVideoOff *) TextColor ( Foreground_color ); TextBackGround( Background_color ); END (* RvsVideoOff *); (*----------------------------------------------------------------------*) (* TURBO Pascal Window Location Routines *) (*----------------------------------------------------------------------*) (* *) (* These routines and constants give the four corners of the current *) (* Turbo window: *) (* *) (* Lower right-hand corner: (Lower_Right_Column, Lower_Right_Row) *) (* Upper left_hand corner: Upper_Left( Column, Row ) *) (* *) (*----------------------------------------------------------------------*) (* Lower right corner of *) (* current TURBO window *) VAR Lower_Right_Column : Byte ABSOLUTE Cseg:$016A; Lower_Right_Row : Byte ABSOLUTE Cseg:$016B; (*----------------------------------------------------------------------*) (* Upper_Left --- Upper Positions of current window *) (*----------------------------------------------------------------------*) PROCEDURE Upper_Left( VAR X1, Y1 : INTEGER ); (*----------------------------------------------------------------------*) (* *) (* Procedure: Upper_Left *) (* *) (* Purpose: Returns upper positions of current TURBO window *) (* *) (* Calling Sequence: *) (* *) (* Upper_Left( VAR X1, Y1 : INTEGER ); *) (* *) (* X1 --- returned upper left column *) (* Y1 --- returned upper left row *) (* *) (* Calls: INTR *) (* WhereX *) (* WhereY *) (* GoToXY *) (* *) (*----------------------------------------------------------------------*) VAR TempX : INTEGER; TempY : INTEGER; Reg : RegPack; BEGIN (* Upper_Left *) TempX := WhereX; (* Save Current Cursor Pos. *) TempY := WhereY; GoToXY( 1 , 1 ); (* Goto Upper Left corner of window *) Reg.Ax := $0300; (* Set up reg's for INTR *) Reg.Bx := 0; INTR( $10 , Reg ); (* Call BIOS Read Cursor Position *) Y1 := Reg.Dh + 1; (* get Row *) X1 := Reg.Dl + 1; (* get Column *) GoToXY( TempX , TempY ); (* Return to orig. position *) END (* Upper_Left *); (*----------------------------------------------------------------------*) (* Set/Reset Text Color Routines *) (*----------------------------------------------------------------------*) (* *) (* These routines set and reset the global text foreground and *) (* background colors. *) (* *) (*----------------------------------------------------------------------*) (* Global Text Color Variables *) VAR Global_ForeGround_Color : INTEGER; Global_BackGround_Color : INTEGER; (*----------------------------------------------------------------------*) (* Set_Global_Colors --- Reset global foreground, background cols. *) (*----------------------------------------------------------------------*) PROCEDURE Set_Global_Colors( ForeGround, BackGround : INTEGER ); (*----------------------------------------------------------------------*) (* *) (* Procedure: Set_Global_Colors *) (* *) (* Purpose: Sets global text foreground, background colors. *) (* *) (* Calling Sequence: *) (* *) (* Set_Global_Colors( ForeGround, BackGround : INTEGER ); *) (* *) (* ForeGround --- Default foreground color *) (* BackGround --- Default background color *) (* *) (* Calls: TextColor *) (* TextBackGround *) (* *) (*----------------------------------------------------------------------*) BEGIN (* Set_Global_Colors *) Global_ForeGround_Color := ForeGround; GLobal_BackGround_Color := BackGround; TextColor ( Global_ForeGround_Color ); TextBackground( Global_BackGround_Color ); END (* Set_Global_Colors *); (*----------------------------------------------------------------------*) (* Reset_Global_Colors --- Reset global foreground, background cols. *) (*----------------------------------------------------------------------*) PROCEDURE Reset_Global_Colors; (*----------------------------------------------------------------------*) (* *) (* Procedure: Reset_Global_Colors *) (* *) (* Purpose: Resets text foreground, background colors to global *) (* defaults. *) (* *) (* Calling Sequence: *) (* *) (* Reset_Global_Colors; *) (* *) (* Calls: TextColor *) (* TextBackGround *) (* *) (*----------------------------------------------------------------------*) BEGIN (* Reset_Global_Colors *) TextColor ( Global_ForeGround_Color ); TextBackground( Global_BackGround_Color ); END (* Reset_Global_Colors *); (*----------------------------------------------------------------------*) (* Screen Manipulation Routines *) (*----------------------------------------------------------------------*) (* *) (* These routines save and restore screen images in support of the *) (* windowing facility. Also, the current screen image can be printed *) (* and text extracted from the screen memory. *) (* *) (*----------------------------------------------------------------------*) (*----------------------------------------------------------------------*) (* Get_Screen_Text_Line --- Extract text from screen image *) (*----------------------------------------------------------------------*) PROCEDURE Get_Screen_Text_Line( VAR Text_Line : AnyStr; Screen_Line : INTEGER; Screen_Column : INTEGER ); (*----------------------------------------------------------------------*) (* *) (* Procedure: Get_Screen_Text_Line *) (* *) (* Purpose: Extracts text from current screen image *) (* *) (* Calling Sequence: *) (* *) (* Get_Screen_Text_Line( Var Text_Line : AnyStr; *) (* Screen_Line : INTEGER; *) (* Screen_Column : INTEGER ); *) (* *) (* Text_Line --- receives text extracted from screen *) (* Screen_Line --- line on screen to extract *) (* Screen_Column --- starting column to extract *) (* *) (* Calls: None *) (* *) (* Remarks: *) (* *) (* Only the text -- not attributes -- from the screen is *) (* returned. *) (* *) (*----------------------------------------------------------------------*) VAR First_Pos : INTEGER; Last_Pos : INTEGER; I : INTEGER; BEGIN (* Get_Screen_Text_Line *) Screen_Line := Max( Min( Screen_Line , 25 ) , 1 ); Screen_Column := Max( Min( Screen_Column , 80 ) , 1 ); Text_Line := ''; First_Pos := ( ( Screen_Line - 1 ) * 80 + Screen_Column ) * 2 - 1; Last_Pos := First_Pos + ( 80 - Screen_Column ) * 2 + 1; REPEAT Text_Line := Text_Line + CHR( Actual_Screen^.Screen_Image[ First_Pos ] ); First_Pos := First_Pos + 2; UNTIL ( First_Pos > Last_Pos ); END (* Get_Screen_Text_Line *); (*----------------------------------------------------------------------*) (* Print_Screen --- Print current screen image *) (*----------------------------------------------------------------------*) PROCEDURE Print_Screen; (*----------------------------------------------------------------------*) (* *) (* Procedure: Print_Screen *) (* *) (* Purpose: Prints current screen image (memory mapped area) *) (* *) (* Calling Sequence: *) (* *) (* Print_Screen; *) (* *) (* Calls: None *) (* *) (* Remarks: *) (* *) (* Only the text from the screen is printed, not the attributes. *) (* *) (*----------------------------------------------------------------------*) VAR I : INTEGER; Text_Line : STRING[80]; BEGIN (* Print_Screen *) FOR I := 1 TO 25 DO BEGIN Get_Screen_Text_Line( Text_Line, I, 1 ); WRITELN( Lst , Text_Line ); END; END (* Print_Screen *); (*----------------------------------------------------------------------*) (* Write_Screen --- Write current screen image to file *) (*----------------------------------------------------------------------*) PROCEDURE Write_Screen( Fname : AnyStr ); (*----------------------------------------------------------------------*) (* *) (* Procedure: Write_Screen *) (* *) (* Purpose: Write current screen image (memory mapped area) to *) (* a file. *) (* *) (* Calling Sequence: *) (* *) (* Write_Screen( Fname : AnyStr ); *) (* *) (* Fname --- Name of file to write screen to *) (* *) (* Calls: None *) (* *) (* Remarks: *) (* *) (* Only the text from the screen is written, not the attributes. *) (* If the file already exists, then the new screen is appended *) (* to the end of the file. *) (* *) (*----------------------------------------------------------------------*) VAR I : INTEGER; Text_Line : STRING[80]; F : TEXT [512]; BEGIN (* Write_Screen *) (*$I-*) ASSIGN( F , Fname ); RESET ( F ); IF Int24Result = 0 THEN BEGIN CLOSE( F ); APPEND( F ); END ELSE BEGIN CLOSE ( F ); REWRITE( F ); END; FOR I := 1 TO 25 DO BEGIN Get_Screen_Text_Line( Text_Line, I, 1 ); WRITELN( F , Text_Line ); END; CLOSE( F ); (*$I+*) END (* Write_Screen *); (*----------------------------------------------------------------------*) (* WriteSLin --- Write text string to screen *) (*----------------------------------------------------------------------*) PROCEDURE WriteSLin( S: AnyStr; Color: INTEGER ); (*----------------------------------------------------------------------*) (* *) (* Procedure: WriteSLin *) (* *) (* Purpose: Writes text string to current line in screen memory *) (* *) (* Calling Sequence: *) (* *) (* WriteSLin( S: AnyStr; Color: INTEGER ); *) (* *) (* S --- String to be written *) (* Color --- Color in which to write string *) (* *) (* Calls: None *) (* *) (*----------------------------------------------------------------------*) VAR Length_S : INTEGER; S_Column : INTEGER; S_Row : INTEGER; I : INTEGER; BEGIN (* WriteSLin *) Length_S := LENGTH( S ); S_Column := 1; S_Row := ( WhereY - 1 ) * 160; FOR I := 1 TO Length_S DO WITH Actual_Screen^ DO BEGIN Screen_Image[ S_Column + S_Row ] := ORD( COPY( S, I, 1 ) ); Screen_Image[ S_Column + S_Row + 1 ] := Color; S_Column := S_Column + 2; END; S_Row := S_Row + 160; IF S_Row > 3800 THEN InsLine; END (* WriteSLin *); (*----------------------------------------------------------------------*) (* WriteSXY --- Write text string to specified row/column *) (*----------------------------------------------------------------------*) PROCEDURE WriteSXY( S: AnyStr; X: INTEGER; Y: INTEGER; Color: INTEGER ); (*----------------------------------------------------------------------*) (* *) (* Procedure: WriteSXY *) (* *) (* Purpose: Writes text string at specified row and column *) (* position on screen. *) (* *) (* Calling Sequence: *) (* *) (* WriteSXY( S: AnyStr; X: INTEGER; Y: INTEGER; Color: INTEGER );*) (* *) (* S --- String to be written *) (* X --- Column position to write string *) (* Y --- Column position to write string *) (* Color --- Color in which to write string *) (* *) (* Calls: None *) (* *) (*----------------------------------------------------------------------*) VAR Length_S : INTEGER; S_Column : INTEGER; S_Row : INTEGER; I : INTEGER; S_Pos : INTEGER; BEGIN (* WriteSXY *) Length_S := LENGTH( S ); S_Pos := 0; FOR I := 1 TO Length_S DO WITH Actual_Screen^ DO IF S_Pos < 4001 THEN BEGIN S_Pos := ( ( Y - 1 ) * 80 + X ) * 2 - 1; Screen_Image[ S_Pos ] := ORD( COPY( S, I, 1 ) ); Screen_Image[ S_Pos + 1 ] := Color; X := X + 1; END; END (* WriteSXY *); (*----------------------------------------------------------------------*) (* WriteCXY --- Write character to screen at specified row/column *) (*----------------------------------------------------------------------*) PROCEDURE WriteCXY( C: CHAR; X: INTEGER; Y: INTEGER; Color: INTEGER ); (*----------------------------------------------------------------------*) (* *) (* Procedure: WriteCXY *) (* *) (* Purpose: Writes a character at specified row and column *) (* position on screen. *) (* *) (* Calling Sequence: *) (* *) (* WriteCXY( C: CHAR; X: INTEGER; Y: INTEGER; Color: INTEGER ); *) (* *) (* C --- Character to be written *) (* X --- Column position to write string *) (* Y --- Column position to write string *) (* Color --- Color in which to write string *) (* *) (* Calls: None *) (* *) (*----------------------------------------------------------------------*) VAR S_Pos : INTEGER; BEGIN (* WriteCXY *) WITH Actual_Screen^ DO BEGIN S_Pos := ( ( Y - 1 ) * 80 + X ) * 2 - 1; Screen_Image[ S_Pos ] := ORD( C ); Screen_Image[ S_Pos + 1 ] := Color; END; END (* WriteCXY *); (*----------------------------------------------------------------------*) (* Save_Screen --- Save current screen image *) (*----------------------------------------------------------------------*) PROCEDURE Save_Screen( VAR Saved_Screen_Pointer : Saved_Screen_Ptr ); (*----------------------------------------------------------------------*) (* *) (* Procedure: Save_Screen *) (* *) (* Purpose: Saves current screen image (memory mapped area) *) (* *) (* Calling Sequence: *) (* *) (* Save_Screen( Var Saved_Screen_Pointer : Saved_Screen_Ptr ); *) (* *) (* Saved_Screen_Pointer --- pointer to record receiving *) (* screen image, window location, *) (* and current cursor location. *) (* *) (* Calls: Move *) (* Upper_Left *) (* *) (* Remarks: *) (* *) (* This version checks for stack overflow. *) (* *) (*----------------------------------------------------------------------*) BEGIN (* Save_Screen *) (* Overwrite last screen if no room *) IF Current_Saved_Screen >= Max_Saved_Screen THEN Saved_Screen_Pointer := Saved_Screen_List[ Max_Saved_Screen ] ELSE BEGIN Current_Saved_Screen := Current_Saved_Screen + 1; NEW( Saved_Screen_Pointer ); Saved_Screen_List[ Current_Saved_Screen ] := Saved_Screen_Pointer; END; WITH Saved_Screen_Pointer^ DO BEGIN Upper_Left( Screen_X1, Screen_Y1 ); Screen_X2 := Lower_Right_Column; Screen_Y2 := Lower_Right_Row; Screen_Row := WhereY; Screen_Column := WhereX; MOVE( Actual_Screen^.Screen_Image, Screen_Image, Screen_Length ); END; END (* Save_Screen *); (*----------------------------------------------------------------------*) (* Restore_Screen --- Restore saved screen image *) (*----------------------------------------------------------------------*) PROCEDURE Restore_Screen( VAR Saved_Screen_Pointer : Saved_Screen_Ptr ); (*----------------------------------------------------------------------*) (* *) (* Procedure: Restore_Screen *) (* *) (* Purpose: Restores previously saved screen image. *) (* *) (* Calling Sequence: *) (* *) (* Restore_Screen( Var Saved_Screen_Pointer: Saved_Screen_Ptr ); *) (* *) (* Saved_Screen_Pointer --- pointer to record with saved *) (* screen image, window location, *) (* and cursor location. *) (* *) (* Calls: Window *) (* Move *) (* GoToXY *) (* *) (* Remarks: *) (* *) (* All saved screen pointers from the last saved down to the *) (* argument pointer are popped from the saved screen list. *) (* *) (*----------------------------------------------------------------------*) BEGIN (* Restore_Screen *) WITH Saved_Screen_Pointer^ DO BEGIN Window( Screen_X1, Screen_Y1, Screen_X2, Screen_Y2 ); MOVE( Screen_Image, Actual_Screen^.Screen_Image, Screen_Length ); GoToXY( Screen_Column, Screen_Row ); END; WHILE( Saved_Screen_List[ Current_Saved_Screen ] <> Saved_Screen_Pointer ) DO BEGIN DISPOSE( Saved_Screen_List[ Current_Saved_Screen ] ); Current_Saved_Screen := Current_Saved_Screen - 1; END; IF Current_Saved_Screen > 0 THEN Current_Saved_Screen := Current_Saved_Screen - 1; DISPOSE( Saved_Screen_Pointer ); Saved_Screen_Pointer := NIL; END (* Restore_Screen *);