Writing a keyboard driver
Writing a keyboard driver means that hooks must be created for most of the keyboard unit functions. The TKeyBoardDriver record contains a field for each of the possible hooks:
TKeyboardDriver = Record
InitDriver : Procedure;
DoneDriver : Procedure;
GetKeyEvent : Function : TKeyEvent;
PollKeyEvent : Function : TKeyEvent;
GetShiftState : Function : Byte;
TranslateKeyEvent : Function (KeyEvent: TKeyEvent): TKeyEvent;
TranslateKeyEventUniCode: Function (KeyEvent: TKeyEvent): TKeyEvent;
end;
The meaning of these hooks is explained below:
- InitDriver
- Called to initialize and enable the driver. Guaranteed to be called only once. This should initialize all needed things for the driver.
- DoneDriver
- Called to disable and clean up the driver. Guaranteed to be called after a call to initDriver. This should clean up all things initialized by InitDriver.
- GetKeyEvent
- Called by GetKeyEvent . Must wait for and return the next key event. It should NOT store keys.
- PollKeyEvent
- Called by PollKeyEvent . It must return the next key event if there is one. Should not store keys.
- GetShiftState
- Called by PollShiftStateEvent . Must return the current shift state.
- TranslateKeyEvent
- Should translate a raw key event to a correct key event, i.e. should fill in the shiftstate and convert function key scancodes to function key keycodes. If the TranslateKeyEvent is not filled in, a default translation function will be called which converts the known scancodes from the tables in the previous section to a correct keyevent.
- TranslateKeyEventUniCode
- Should translate a key event to a UNICode key representation.
Strictly speaking, only the GetKeyEvent and PollKeyEvent hooks must be implemented for the driver to function correctly.
The example unit demonstrates how a keyboard driver can be installed. It takes the installed driver, and hooks into the GetKeyEvent function to register and log the key events in a file. This driver can work on top of any other driver, as long as it is inserted in the uses clause after the real driver unit, and the real driver unit should set the driver record in its initialization section.
Note that with a simple extension of this unit could be used to make a driver that is capable of recording and storing a set of keyboard strokes, and replaying them at a later time, so a 'keyboard macro' capable driver. This driver could sit on top of any other driver.
Example
unit logkeys;
interface
Procedure StartKeyLogging;
Procedure StopKeyLogging;
Function IsKeyLogging : Boolean;
Procedure SetKeyLogFileName(FileName : String);
implementation
uses sysutils,keyboard;
var
NewKeyBoardDriver,
OldKeyBoardDriver : TKeyboardDriver;
Active,Logging : Boolean;
LogFileName : String;
KeyLog : Text;
Function TimeStamp : String;
begin
TimeStamp:=FormatDateTime('hh:nn:ss',Time());
end;
Procedure StartKeyLogging;
begin
Logging:=True;
Writeln(KeyLog,'Start logging keystrokes at: ',TimeStamp);
end;
Procedure StopKeyLogging;
begin
Writeln(KeyLog,'Stop logging keystrokes at: ',TimeStamp);
Logging:=False;
end;
Function IsKeyLogging : Boolean;
begin
IsKeyLogging:=Logging;
end;
Function LogGetKeyEvent : TKeyEvent;
Var
K : TKeyEvent;
begin
K:=OldkeyboardDriver.GetKeyEvent();
If Logging then
begin
Write(KeyLog,TimeStamp,': Key event: ');
Writeln(KeyLog,KeyEventToString(TranslateKeyEvent(K)));
end;
LogGetKeyEvent:=K;
end;
Procedure LogInitKeyBoard;
begin
OldKeyBoardDriver.InitDriver();
Assign(KeyLog,logFileName);
Rewrite(KeyLog);
Active:=True;
StartKeyLogging;
end;
Procedure LogDoneKeyBoard;
begin
StopKeyLogging;
Close(KeyLog);
Active:=False;
OldKeyBoardDriver.DoneDriver();
end;
Procedure SetKeyLogFileName(FileName : String);
begin
If Not Active then
LogFileName:=FileName;
end;
Initialization
GetKeyBoardDriver(OldKeyBoardDriver);
NewKeyBoardDriver:=OldKeyBoardDriver;
NewKeyBoardDriver.GetKeyEvent:=@LogGetKeyEvent;
NewKeyBoardDriver.InitDriver:=@LogInitKeyboard;
NewKeyBoardDriver.DoneDriver:=@LogDoneKeyboard;
LogFileName:='keyboard.log';
Logging:=False;
SetKeyboardDriver(NewKeyBoardDriver);
end.
Example
program example9;
{ This program demonstrates the logkeys unit }
uses keyboard,logkeys;
Var
K : TKeyEvent;
begin
InitKeyBoard;
Writeln('Press keys, press "q" to end, "s" toggles logging.');
Repeat
K:=GetKeyEvent;
K:=TranslateKeyEvent(K);
Writeln('Got key : ',KeyEventToString(K));
if GetKeyEventChar(K)='s' then
if IsKeyLogging then
StopKeyLogging
else
StartKeyLogging;
Until (GetKeyEventChar(K)='q');
DoneKeyBoard;
end.