{
From: ANDREW KEY
Subj: Screen Save
}

unit Scrnsavr;
{$F+}
(*************************************************************************)
(*                        Screen Saver                                   *)
(*                                                                       *)
(*  Written by Jay A. Key -- Oct 1993                                    *)
(*  Code may be modified and used freely.  Please mention my name        *)
(*  somewhere in your docs or in the program itself.                     *)
(*                                                                       *)
(*  Self contained unit to install a text-mode screen saver in Turbo     *)
(*  Pascal programs.  Simply include the following line in your code.    *)
(*    uses ScrnSavr;                                                     *)
(*                                                                       *)
(*  It will initialize itself automatically, and will remove itself      *)
(*  upon exit from your program, graceful exit or not.  Functions        *)
(*  SetTimeOut and SetDelay are included if you wish to modify the       *)
(*  default values.                                                      *)
(*                                                                       *)
(*  Warning: will not properly save and restore screens while running    *)
(*  under the Turbo Pascal IDE.  Runs great from DOS.                    *)
(*************************************************************************)

interface

uses Dos,Crt;

function NumRows: byte;          {Returns number of rows in current screen}
function ColorAdaptor: boolean;  {TRUE if color video card installed}
procedure SetTimeOut(T: integer); {Delay(seconds) before activation}
procedure SetDelay(T: integer);  {Interval between iterations}

(************************************)

implementation

type
  VideoArray = array[1..2000] of word;  {buffer to save video screen}

var
  Timer: word;
  Waiting: boolean;
  OldInt15,                  {Keyboard interrupt}
  OldInt1C,                  {Timer interrupt}
  OldInt23,                  {Cntl-C/Cntl-Break handler}
  ExitSave: pointer;
  Position, Cursor: integer; {save and restore cursor positions}
  VideoSave: VideoArray;
  VideoMem: ^VideoArray;
  TimeOut, Delay: integer;

procedure JumpToPriorIsr(p: pointer);
{Originally written by Brook Monroe, "An ISR Clock", pg. 64,
 PC Techniques Aug/Sep 1992}
  inline($5b/$58/$87/$5e/$0e/$87/$46/$10/$89/$ec/$5d/$07/$1f/
         $5f/$5e/$5a/$59/$cb);

function ColorAdaptor: boolean; assembler;
  asm
    int 11                   {BIOS call - get equipment list}
    and al,$0010             {mask off all but bit 4}
    xor al,$0010             {flip bit 4 - return val is in al}
  end;

function NumRows: byte; assembler;  {returns number of displayable rows}
  asm
    mov ax,$40
    mov es,ax
    mov ax,$84
    mov di,ax
    mov al,[es:di]           {byte at [$40:$84] is number of rows in display}
  end;

procedure HideCursor; assembler;
  asm
    mov ah,$03
    xor bh,bh
    int $10               {video interrupt}
    mov Position,dx       {save cursor position}
    mov Cursor,cx         {and type}
    mov ah,$01
    mov ch,$20
    int $10               {video interrupt - hide cursor}
  end;

procedure RestoreCursor; assembler;
  asm
    mov ah,$02
    xor bh,bh
    mov dx,Position       {get old position}
    int $10               {video interrupt - restore cursor position}
    mov cx,Cursor         {get old cursor type}
    mov ah,$01
    int $10               {video interrupt - restore cursor type}
  end;

procedure RestoreScreen;
  begin
    VideoMem^ := VideoSave;  {Copy saved image back onto video memory}
    RestoreCursor;
  end;

procedure SaveScreen;
  begin
    VideoSave := VideoMem^;  {Copy video memory to array}
    HideCursor;
  end;

procedure DispMsg;  {simple stub-out for displaying YOUR message(s),
                     pictures, etc...use your imagination!!!}
  begin
    ClrScr;
    GotoXY(random(50),random(23));
    writeln('This would normally be something witty!');
  end;

procedure NewInt15(Flags,CS,IP,AX,BX,CX,DX,
                   SI,DI,DS,ES,BP:WORD); interrupt; {keyboard handler}
  begin
    Timer:=0;                     {Reset timer}
    if Waiting then               {Screen saver activated?}
      begin
        RestoreScreen;            {Restore saved screen image}
        Waiting:= FALSE;          {De-activate screen saver}
        Flags:=(Flags and $FFFE); {Tell BIOS to ignore current keystroke}
      end
    else
      JumpToPriorISR(OldInt15);   {call original int 15}
  end;

procedure NewInt1C; interrupt;    {timer interrupt}
  begin
    Inc(Timer);                   {Increment timer}
    if Timer>TimeOut then         {No key hit for TimeOut seconds?}
      begin
        Waiting := TRUE;          {Activate screen saver}
        SaveScreen;               {Save image of video memory}
        DispMsg;                  {Display your own message}
        Timer := 0;               {Reset timer}
      end;
    if waiting then               {Is saver already active?}
      begin
        if Timer>Delay then       {Time for next message?}
          begin
            Timer := 0;           {Reset timer}
            DispMsg;              {Display next message}
          end;
      end;
    JumpToPriorISR(OldInt1C);     {Chain to old timer interrupt}
  end;

procedure ResetIntVectors;        {Restores Intrrupt vectors to orig. values}
  begin
    SetIntVec($15,OldInt15);
    SetIntVec($1C,OldInt1C);
    SetIntVec($23,OldInt23);
  end;

procedure NewInt23; interrupt;    {Called to handle cntl-c/brk}
  begin
    ResetIntVectors;              {Restore old interrupt vectors}
    JumpToPriorISR(OldInt23);     {Chain to original int 23h}
  end;

procedure MyExit; far;            {exit code for unit}
  begin
    ResetIntVectors;              {Restore old interrupt vectors}
    ExitProc:=ExitSave;           {Restore old exit code}
  end;

procedure SetVideoAddress;        {Returns pointer to text video memory}
  begin
    if ColorAdaptor then
      VideoMem := ptr($B000,$0000)
    else
      VideoMem := ptr($B800,$0000);
  end;

procedure SetTimeOut(T: integer); {Set delay(seconds) before activation}
  begin
    TimeOut:=Round(T*18.2);
  end;

procedure SetDelay(T: integer);  {Set interval between iterations}
  begin
    Delay:=Round(T*18.2);
  end;

{Initialize unit}
begin
  SetVideoAddress;             {Set up address for video memory}
  Waiting := FALSE;            {Screen saver initially OFF}
  Timer := 0;                  {Reset timer}
  ExitSave := ExitProc;        {Save old exit routine}
  ExitProc := @MyExit;         {Install own exit routine}
{Install user defined int vectors}
  GetIntVec($15,OldInt15);     {Keyboard handler}
  SetIntVec($15,@NewInt15);
  GetIntVec($1c,OldInt1C);     {Timer int}
  SetIntVec($1c,@NewInt1C);
  GetIntVec($23,OldInt23);     {Cntl-C/Brk handler}
  SetIntVec($23,@NewInt23);
  SetTimeOut(120);
  SetDelay(15);
end.

