Viddriver
Writing a custom video driver
Writing a custom video driver is not difficult, and generally means implementing a couple of functions, which whould be registered with the SetVideoDriver function. The various functions that can be implemented are located in the TVideoDriver record:
TVideoDriver = Record
InitDriver : Procedure;
DoneDriver : Procedure;
UpdateScreen : Procedure(Force : Boolean);
ClearScreen : Procedure;
SetVideoMode : Function (Const Mode : TVideoMode) : Boolean;
GetVideoModeCount : Function : Word;
GetVideoModeData : Function(Index : Word; Var Data : TVideoMode) : Boolean;
SetCursorPos : procedure (NewCursorX, NewCursorY: Word);
GetCursorType : function : Word;
SetCursorType : procedure (NewType: Word);
GetCapabilities : Function : Word;
end;
Not all of these functions must be implemented. In fact, the only absolutely necessary function to write a functioning driver is the UpdateScreen function. The general calls in the Video unit will check which functionality is implemented by the driver.
The functionality of these calls is the same as the functionality of the calls in the video unit, so the expected behaviour can be found in the previous section. Some of the calls, however, need some additional remarks.
- InitDriver
- Called by InitVideo, this function should initialize any data structures needed for the functionality of the driver, maybe do some screen initializations. The function is guaranteed to be called only once; It can only be called again after a call to DoneVideo. The variables ScreenWidth and ScreenHeight should be initialized correctly after a call to this function, as the InitVideo call will initialize the VideoBuf and OldVideoBuf arrays based on their values.
- DoneDriver
- This should clean up any structures that have been initialized in the InitDriver function. It should possibly also restore the screen as it was before the driver was initialized. The VideoBuf and OldVideoBuf arrays will be disposed of by the general DoneVideo call.
- UpdateScreen
- This is the only required function of the driver. It should update the screen based on the VideoBuf array's contents. It can optimize this process by comparing the values with values in the OldVideoBuf array. After updating the screen, the UpdateScreen procedure should update the OldVideoBuf by itself. If the Force parameter is True, the whole screen should be updated, not just the changed values.
- ClearScreen
- If there is a faster way to clear the screen than to write spaces in all character cells, then it can be implemented here. If the driver does not implement this function, then the general routines will write spaces in all video cells, and will call UpdateScreen(True).
- SetVideoMode
- Should set the desired video mode, if available. It should return True if the mode was set, False if not.
- GetVideoModeCount
- Should return the number of supported video modes. If no modes are supported, this function should not be implemented; the general routines will return 1. (for the current mode)
- GetVideoModeData
- Should return the data for the Index-th mode; Index is zero based. The function should return true if the data was returned correctly, false if Index contains an invalid index. If this is not implemented, then the general routine will return the current video mode when Index equals 0.
- GetCapabilities
- If this function is not implemented, zero (i.e. no capabilities) will be returned by the general function.
The following unit shows how to override a video driver, with a driver that writes debug information to a file. The unit can be used in any of the demonstration programs, by simply including it in the uses clause. Setting DetailedVideoLogging to True will create a more detailed log (but will also slow down functioning)
Example
unit viddbg;
Interface
uses video;
Procedure StartVideoLogging;
Procedure StopVideoLogging;
Function IsVideoLogging : Boolean;
Procedure SetVideoLogFileName(FileName : String);
Const
DetailedVideoLogging : Boolean = False;
Implementation
uses sysutils,keyboard;
var
NewVideoDriver,
OldVideoDriver : TVideoDriver;
Active,Logging : Boolean;
LogFileName : String;
VideoLog : Text;
Function TimeStamp : String;
begin
TimeStamp:=FormatDateTime('hh:nn:ss',Time());
end;
Procedure StartVideoLogging;
begin
Logging:=True;
Writeln(VideoLog,'Start logging video operations at: ',TimeStamp);
end;
Procedure StopVideoLogging;
begin
Writeln(VideoLog,'Stop logging video operations at: ',TimeStamp);
Logging:=False;
end;
Function IsVideoLogging : Boolean;
begin
IsVideoLogging:=Logging;
end;
Var
ColUpd,RowUpd : Array[0..1024] of Integer;
Procedure DumpScreenStatistics(Force : Boolean);
Var
I,Count : Integer;
begin
If Force then
Write(VideoLog,'forced ');
Writeln(VideoLog,'video update at ',TimeStamp,' : ');
FillChar(Colupd,SizeOf(ColUpd),#0);
FillChar(Rowupd,SizeOf(RowUpd),#0);
Count:=0;
For I:=0 to VideoBufSize div SizeOf(TVideoCell) do
begin
If VideoBuf^[i]<>OldVideoBuf^[i] then
begin
Inc(Count);
Inc(ColUpd[I mod ScreenWidth]);
Inc(RowUpd[I div ScreenHeight]);
end;
end;
Write(VideoLog,Count,' videocells differed divided over ');
Count:=0;
For I:=0 to ScreenWidth-1 do
If ColUpd[I]<>0 then
Inc(Count);
Write(VideoLog,Count,' columns and ');
Count:=0;
For I:=0 to ScreenHeight-1 do
If RowUpd[I]<>0 then
Inc(Count);
Writeln(VideoLog,Count,' rows.');
If DetailedVideoLogging Then
begin
For I:=0 to ScreenWidth-1 do
If (ColUpd[I]<>0) then
Writeln(VideoLog,'Col ',i,' : ',ColUpd[I]:3,' rows changed');
For I:=0 to ScreenHeight-1 do
If (RowUpd[I]<>0) then
Writeln(VideoLog,'Row ',i,' : ',RowUpd[I]:3,' colums changed');
end;
end;
Procedure LogUpdateScreen(Force : Boolean);
begin
If Logging then
DumpScreenStatistics(Force);
OldVideoDriver.UpdateScreen(Force);
end;
Procedure LogInitVideo;
begin
OldVideoDriver.InitDriver();
Assign(VideoLog,logFileName);
Rewrite(VideoLog);
Active:=True;
StartVideoLogging;
end;
Procedure LogDoneVideo;
begin
StopVideoLogging;
Close(VideoLog);
Active:=False;
OldVideoDriver.DoneDriver();
end;
Procedure SetVideoLogFileName(FileName : String);
begin
If Not Active then
LogFileName:=FileName;
end;
Initialization
GetVideoDriver(OldVideoDriver);
NewVideoDriver:=OldVideoDriver;
NewVideoDriver.UpdateScreen:=@LogUpdateScreen;
NewVideoDriver.InitDriver:=@LogInitVideo;
NewVideoDriver.DoneDriver:=@LogDoneVideo;
LogFileName:='Video.log';
Logging:=False;
SetVideoDriver(NewVideoDriver);
end.