{
  System independent low-level video interface for OS/2

  $Id: video.inc,v 1.1.2.2 2000/11/26 18:58:29 hajny Exp $

}

uses
{$IFDEF PPC_FPC}
 DosCalls, VioCalls;
{$ELSE}
 {$IFDEF PPC_VIRTUAL}
 Os2Base;
 {$ENDIF}
{$ENDIF}

{$IFNDEF FPC}
type
    cardinal = longint;
{$ENDIF}

const
    InitVideoCalled: boolean = false;
    LastCursorType: word = crUnderline;
    EmptyCell: cardinal = $0720;
    OrigScreen: PVideoBuf = nil;
    OrigScreenSize: cardinal = 0;

{$IFDEF PPC_VIRTUAL}
type
    TVioCursorInfo = VioCursorInfo;
    TVioModeInfo = VioModeInfo;
    TVioIntensity = VioIntensity;
{$ENDIF}

var OrigCurType: TVioCursorInfo;
    OrigVioMode: TVioModeInfo;
    OrigHighBit: TVioIntensity;
    OrigCurRow: word;
    OrigCurCol: word;
    CellHeight: byte;
    OldVideoBuf: PVideoBuf;

procedure TargetEntry;

var P: PVideoModeList;
    PScr: pointer;

begin
{Remember original video mode, cursor type and high bit behaviour setting}
    OrigVioMode.cb := SizeOf (OrigVioMode);
    VioGetMode (OrigVioMode, 0);
    VioGetCurType (OrigCurType, 0);
    VioGetCurPos (OrigCurRow, OrigCurCol, 0);
    with OrigHighBit do
        begin
            cb := 6;
            rType := 2;
        end;
    VioGetState (OrigHighBit, 0);
{Register the curent video mode in Modes if not there yet}
    with OrigVioMode do
        begin
            P := Modes;
            while (P <> nil) and ((P^.Row <> Row) or (P^.Col <> Col)
                                      or (P^.Color <> (Color >= Colors_16))) do
                P := P^.Next;
            if P = nil then
{Assume we have at least 16 colours available in "colour" modes}
                RegisterVideoMode (Col, Row, Color >= Colors_16,
{$IFDEF FPC}
                                                 @DefaultVideoModeSelector, 0);
{$ELSE}
                                                  DefaultVideoModeSelector, 0);
{$ENDIF}
        end;
{Get the address of the original videobuffer and size.}
    if VioGetBuf (PScr, PWord (@OrigScreenSize)^, 0) = 0 then
        begin
{$IFDEF BIT_32}
 {$IFDEF PPC_VIRTUAL}
            SelToFlat (PScr);
 {$ELSE}
            PScr := SelToFlat (TFarPtr (PScr));
 {$ENDIF}
{$ENDIF}
            GetMem (OrigScreen, OrigScreenSize);
            Move (PScr^, OrigScreen^, OrigScreenSize);
        end;
end;

procedure TargetExit;

begin
end;


procedure CheckCellHeight;

var OldCD, CD: TVioCursorInfo;

begin
    VioGetCurType (OldCD, 0);
    Move (OldCD, CD, SizeOf (CD));
    with CD do
        begin
            Attr := 0;
            yStart := word (-90);
            cEnd := word (-100);
        end;
    VioSetCurType (CD, 0);
    VioGetCurType (CD, 0);
    CellHeight := CD.cEnd;
    VioSetCurType (OldCD, 0);
end;


procedure RegisterVideoModes;
begin
{ BW modes are rejected on my (colour) configuration. I can't imagine
  OS/2 running on MCGA anyway... ;-)

    RegisterVideoMode (40, 25, False, @DefaultVideoModeSelector, 0);
    RegisterVideoMode (80, 25, False, @DefaultVideoModeSelector, 0);
}
{$IFDEF FPC}
    RegisterVideoMode (40, 25, True, @DefaultVideoModeSelector, 0);
    RegisterVideoMode (80, 25, True, @DefaultVideoModeSelector, 0);
    RegisterVideoMode (80, 30, True, @DefaultVideoModeSelector, 0);
    RegisterVideoMode (80, 43, True, @DefaultVideoModeSelector, 0);
    RegisterVideoMode (80, 50, True, @DefaultVideoModeSelector, 0);
{$ELSE}
    RegisterVideoMode (40, 25, True, DefaultVideoModeSelector, 0);
    RegisterVideoMode (80, 25, True, DefaultVideoModeSelector, 0);
    RegisterVideoMode (80, 30, True, DefaultVideoModeSelector, 0);
    RegisterVideoMode (80, 43, True, DefaultVideoModeSelector, 0);
    RegisterVideoMode (80, 50, True, DefaultVideoModeSelector, 0);
{$ENDIF}

{ The following modes wouldn't work on plain VGA; is it useful to check
  for their availability on the program startup?

    RegisterVideoMode (132, 25, True, @DefaultVideoModeSelector, 0);
    RegisterVideoMode (132, 30, True, @DefaultVideoModeSelector, 0);
    RegisterVideoMode (132, 43, True, @DefaultVideoModeSelector, 0);
    RegisterVideoMode (132, 50, True, @DefaultVideoModeSelector, 0);
}
end;


procedure SetHighBitBlink (Blink: boolean);

var VI: TVioIntensity;

begin
    with VI do
        begin
            cb := 6;
            rType := 2;
            fs := byte (not (Blink));
        end;
    VioSetState (VI, 0);
end;


procedure InitVideo;

var MI: TVioModeInfo;

begin
    if InitVideoCalled then
        FreeMem (OldVideoBuf, VideoBufSize);
    OldVideoBuf := nil;
    InitVideoCalled := true;
    VideoBufSize := 0;
    MI.cb := SizeOf (MI);
    VioGetMode (MI, 0);
    with MI do
        begin
            ScreenWidth := Col;
            ScreenHeight := Row;
            ScreenColor := Color >= Colors_16;
        end;
    VioGetCurPos (CursorY, CursorX, 0);
    LowAscii := true;
    SetCursorType (LastCursorType);
{Get the address of the videobuffer.}
{$IFDEF PPC_VIRTUAL}
    if VioGetBuf (pointer (VideoBuf), PWord (@VideoBufSize)^, 0) = 0 then
        begin
            SelToFlat (pointer (VideoBuf));
{$ELSE}
    if VioGetBuf (VideoBuf, PWord (@VideoBufSize)^, 0) = 0 then
        begin
 {$IFDEF BIT_32}
            VideoBuf := SelToFlat (TFarPtr (VideoBuf));
 {$ENDIF}
{$ENDIF}
            SetHighBitBlink (true);
            GetMem (OldVideoBuf, VideoBufSize);
            Move (VideoBuf^, OldVideoBuf^, VideoBufSize);
        end
    else
        ErrorHandler (errVioInit, nil);
end;


procedure SetCursorPos (NewCursorX, NewCursorY: word);

begin
    if VioSetCurPos (NewCursorY, NewCursorX, 0) = 0 then
        begin
            CursorX := NewCursorX;
            CursorY := NewCursorY;
        end
    else
        {Do not set an error code; people should fix invalid NewCursorX
         or NewCursorY values when designing, there is no need for detecting
         these errors at runtime.}
        RunError (225);
end;


function GetCursorType: word;

var CD: TVioCursorInfo;

begin
    VioGetCurType (CD, 0);    {Never fails, because handle is default handle.}
    with CD do
        begin
            CursorLines := Succ (cEnd) - yStart;
            if Attr = word (-1) then
                GetCursorType := crHidden
            else
{Because the cursor's start and end lines are returned, we'll have
 to guess heuristically what cursor type we have.}
                if CursorLines = 0 then
{Probably this does not occur, but you'll never know.}
                    GetCursorType := crHidden
                else if CursorLines <= Succ (CellHeight div 4) then
                    GetCursorType := crUnderline
                else if CursorLines <= Succ (CellHeight div 2) then
                    GetCursorType := crHalfBlock
                else
                    GetCursorType := crBlock;
        end;
end;


procedure SetCursorType (NewType: word);

var CD: TVioCursorInfo;

begin
    VioGetCurType (CD, 0);
    with CD do
        begin
            case NewType of
                crHidden: Attr := word (-1);
                crUnderline:
                    begin
                        Attr := 0;
                        yStart := word (-90);
                        cEnd := word (-100);
                    end;
                crHalfBlock:
                    begin
                        Attr := 0;
                        yStart := word (-50);
                        cEnd := word (-100);
                    end;
                crBlock:
                    begin
                        Attr := 0;
                        yStart := 0;
                        cEnd := word (-100);
                    end;
            end;
            VioSetCurType (CD, 0);
            VioGetCurType (CD, 0);
            CursorLines := Succ (cEnd) - yStart;
        end;
end;


procedure DoneVideo;

var PScr: pointer;
    ScrSize: cardinal;

begin
    if InitVideoCalled then
        begin
            LastCursorType := GetCursorType;
            ClearScreen;
{Restore original settings}
            VioSetMode (OrigVioMode, 0);
            CheckCellHeight;
{Set CursorX and CursorY}
            SetCursorPos (0, 0);
            VioSetState (OrigHighBit, 0);
            VioSetCurType (OrigCurType, 0);
            VioSetCurPos (OrigCurRow, OrigCurCol, 0);
            FreeMem (OldVideoBuf, VideoBufSize);
            OldVideoBuf := nil;
            VideoBufSize := 0;
            InitVideoCalled := false;
            if (OrigScreenSize <> 0) and (OrigScreen <> nil) then
                begin
                    ScrSize := 0;
                    if (VioGetBuf (PScr, PWord (@ScrSize)^, 0) = 0)
                                            and (ScrSize = OrigScreenSize) then
                        begin
{$IFDEF BIT_32}
 {$IFDEF PPC_VIRTUAL}
                            SelToFlat (PScr);
 {$ELSE}
                            PScr := SelToFlat (TFarPtr (PScr));
 {$ENDIF}
{$ENDIF}
                            Move (OrigScreen^, PScr^, OrigScreenSize);
                            VioShowBuf (0, ScrSize, 0);
                        end;
                end;
        end;
end;


function GetCapabilities: word;

begin
    GetCapabilities := $3F;
end;


function DefaultVideoModeSelector (const VideoMode: TVideoMode; Params: longint): boolean;

var OldMI, MI: TVioModeInfo;

begin
    OldMI.cb := SizeOf (OldMI);
    if VioGetMode (OldMI, 0) <> 0 then
        DefaultVideoModeSelector := false
    else
        begin
            with MI do
                begin
                    cb := 8;
                    fbType := 1;
                    if VideoMode.Color then
                        Color := Colors_16
                    else
                        Color := Colors_2;
                    Col := VideoMode.Col;
                    Row := VideoMode.Row;
                end;
            if VioSetMode (MI, 0) = 0 then
{$IFDEF PPC_VIRTUAL}
                if VioGetBuf (pointer (VideoBuf),
                                            PWord (@VideoBufSize)^, 0) = 0 then
                    begin
                        SelToFlat (pointer (VideoBuf));
{$ELSE}
                if VioGetBuf (VideoBuf, PWord (@VideoBufSize)^, 0) = 0 then
                    begin
 {$IFDEF BIT_32}
                        VideoBuf := SelToFlat (TFarPtr (VideoBuf));
 {$ENDIF}
{$ENDIF}
                        DefaultVideoModeSelector := true;
                        SetHighBitBlink (true);
                        CheckCellHeight;
                        SetCursorType (LastCursorType);
                        ClearScreen;
                    end
                else
                    begin
                        DefaultVideoModeSelector := false;
                        VioSetMode (OldMI, 0);
{$IFDEF PPC_VIRTUAL}
                        VioGetBuf (pointer (VideoBuf),
                                                    PWord (@VideoBufSize)^, 0);
                        SelToFlat (pointer (VideoBuf));
{$ELSE}
                        VioGetBuf (VideoBuf, PWord (@VideoBufSize)^, 0);
 {$IFDEF BIT_32}
                        VideoBuf := SelToFlat (TFarPtr (VideoBuf));
 {$ENDIF}
{$ENDIF}
                        SetHighBitBlink (true);
                        CheckCellHeight;
                        SetCursorType (LastCursorType);
                        ClearScreen;
                    end
            else
                begin
                    DefaultVideoModeSelector := false;
{$IFDEF PPC_VIRTUAL}
                    VioGetBuf (pointer (VideoBuf), PWord (@VideoBufSize)^, 0);
                    SelToFlat (pointer (VideoBuf));
{$ELSE}
                    VioGetBuf (VideoBuf, PWord (@VideoBufSize)^, 0);
 {$IFDEF BIT_32}
                    VideoBuf := SelToFlat (TFarPtr (VideoBuf));
 {$ENDIF}
{$ENDIF}
                    SetHighBitBlink (true);
                    SetCursorType (LastCursorType);
                end;
        end;
end;


procedure ClearScreen;

begin
    VioScrollDn (0, 0, word (-1), word (-1), word (-1), PWord (@EmptyCell)^, 0);
    Move (VideoBuf^, OldVideoBuf^, VideoBufSize);
end;


{$IFDEF PPC_FPC}
 {$ASMMODE INTEL}
{$ENDIF}

procedure UpdateScreen (Force: boolean);

{$IFDEF BIT_32}
var SOfs, CLen: cardinal;
{$ELSE}
var SOfs, CLen: word;
{$ENDIF}

begin
    if LockUpdateScreen = 0 then
        begin
            if not (Force) then
                begin
{$IFDEF BIT_32}
                    asm
                        cld
                        mov esi, VideoBuf
                        mov edi, OldVideoBuf
                        mov eax, VideoBufSize
                        mov ecx, eax
                        shr ecx
                        shr ecx
                        repe
                        cmpsd
                        inc cx
                        mov SOfs, ecx
                        or ecx, ecx
                        jz @no_update
                        mov Force, 1
                        std
                        mov edi, eax
                        mov esi, VideoBuf
                        add eax, esi
                        sub eax, 4
                        mov esi, eax
                        mov eax, OldVideoBuf
                        add eax, edi
                        sub eax, 4
                        mov edi, eax
                        repe
                        cmpsd
                        inc ecx
                        shl ecx
                        shl ecx
                        mov CLen, ecx
                        cld
@no_update:
                    end;
                    SOfs := VideoBufSize - (SOfs shl 2);
{$ELSE}
                    asm
                        cld
                        push ds
                        lds si, VideoBuf
                        les di, OldVideoBuf
                        mov ax, word ptr VideoBufSize
                        mov cx, ax
                        shr cx
                        repe
                        cmpsw
                        inc cx
                        mov SOfs, cx
                        or cx, cx
                        jz @no_update
                        mov Force, 1
                        std
                        mov di, ax
                        mov si, offset VideoBuf
                        add ax, si
                        dec ax
                        dec ax
                        mov si, ax
                        mov ax, offset OldVideoBuf
                        add ax, di
                        dec ax
                        dec ax
                        mov di, ax
                        repe
                        cmpsw
                        inc cx
                        shl cx
                        mov CLen, cx
                        cld
@no_update:
                        pop ds
                    end;
                    Inc (SOfs);
                    SOfs := VideoBufSize - (SOfs shl 1);
{$ENDIF}
                end else
                    begin
                        SOfs := 0;
                        CLen := VideoBufSize;
                    end;
            if Force then
                begin
                    VioShowBuf (SOfs, CLen, 0);
                    Move (VideoBuf^ [SOfs div SizeOf (TVideoCell)],
                            OldVideoBuf^ [SOfs div SizeOf (TVideoCell)], CLen);
                end;
        end;
end;


{

  $Log: video.inc,v $
  Revision 1.1.2.2  2000/11/26 18:58:29  hajny
    * merging working versions from the main branch

  Revision 1.1.2.1  2000/10/04 11:44:33  pierre
   add TargetEntry and TargetExit procedures (needed for linux)

  Revision 1.1  2000/07/13 06:29:40  michael
  + Initial import

  Revision 1.2  2000/07/09 07:56:09  hajny
    * proper units added

  Revision 1.1  2000/02/23 22:44:22  daniel
  * Video.inc for os/2

  Revision 1.4  1998/12/15 17:17:17  peter
    + cursor at 1,1 at the end

  Revision 1.3  1998/12/12 19:13:01  peter
    * keyboard updates
    * make test target, make all only makes units

  Revision 1.2  1998/12/10 11:41:50  florian
    * cursor is properly restored in DoneVideo

  Revision 1.1  1998/12/04 12:48:27  peter
    * moved some dirs

  Revision 1.4  1998/11/01 20:29:11  peter
    + lockupdatescreen counter to not let updatescreen() update

  Revision 1.3  1998/10/28 21:18:26  peter
    * more fixes

  Revision 1.2  1998/10/28 00:02:08  peter
    + mouse
    + video.clearscreen, video.videobufsize

  Revision 1.1  1998/10/26 11:31:47  peter
    + inital include files

}