fpc/rtl/unix/keyboard.pp

1555 lines
40 KiB
ObjectPascal

{
$Id$
This file is part of the Free Pascal run time library.
Copyright (c) 1999-2000 by Florian Klaempfl
member of the Free Pascal development team
Keyboard unit for linux
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
unit Keyboard;
interface
{$i keybrdh.inc}
Const
AltPrefix : byte = 0;
ShiftPrefix : byte = 0;
CtrlPrefix : byte = 0;
Function RawReadKey:char;
Function RawReadString : String;
Function KeyPressed : Boolean;
{$ifndef NotUseTree}
Procedure AddSequence(Const St : String; AChar,AScan :byte);
Function FindSequence(Const St : String;var AChar, Ascan : byte) : boolean;
{$endif NotUseTree}
procedure RestoreStartMode;
implementation
uses
Mouse,
{$ifndef NotUseTree}
Strings,
TermInfo,
{$endif NotUseTree}
Unix,baseUnix;
{$i keyboard.inc}
var
OldIO,StartTio : TermIos;
{$ifdef logging}
f : text;
{$endif logging}
{$i keyscan.inc}
{$ifdef Unused}
type
TKeyState = Record
Normal, Shift, Ctrl, Alt : word;
end;
Const
KeyStates : Array[0..255] of TKeyState
(
);
{$endif Unused}
Procedure SetRawMode(b:boolean);
Var
Tio : Termios;
Begin
TCGetAttr(1,Tio);
if b then
begin
OldIO:=Tio;
Tio.c_iflag:=Tio.c_iflag and (not (IGNBRK or BRKINT or PARMRK or ISTRIP or
INLCR or IGNCR or ICRNL or IXON));
Tio.c_lflag:=Tio.c_lflag and (not (ECHO or ECHONL or ICANON or ISIG or IEXTEN));
end
else
Tio := OldIO;
TCSetAttr(1,TCSANOW,Tio);
End;
type
chgentry=packed record
tab,
idx,
oldtab,
oldidx : byte;
oldval,
newval : word;
end;
kbentry=packed record
kb_table,
kb_index : byte;
kb_value : word;
end;
const
kbdchanges=10;
kbdchange:array[1..kbdchanges] of chgentry=(
(tab:8; idx:$3b; oldtab:0; oldidx:$3b; oldval:0; newval:0),
(tab:8; idx:$3c; oldtab:0; oldidx:$3c; oldval:0; newval:0),
(tab:8; idx:$3d; oldtab:0; oldidx:$3d; oldval:0; newval:0),
(tab:8; idx:$3e; oldtab:0; oldidx:$3e; oldval:0; newval:0),
(tab:8; idx:$3f; oldtab:0; oldidx:$3f; oldval:0; newval:0),
(tab:8; idx:$40; oldtab:0; oldidx:$40; oldval:0; newval:0),
(tab:8; idx:$41; oldtab:0; oldidx:$41; oldval:0; newval:0),
(tab:8; idx:$42; oldtab:0; oldidx:$42; oldval:0; newval:0),
(tab:8; idx:$43; oldtab:0; oldidx:$43; oldval:0; newval:0),
(tab:8; idx:$44; oldtab:0; oldidx:$44; oldval:0; newval:0)
);
KDGKBENT=$4B46;
KDSKBENT=$4B47;
KDGKBMETA=$4B62;
KDSKBMETA=$4B63;
K_ESCPREFIX=$4;
K_METABIT=$3;
const
oldmeta : longint = 0;
meta : longint = 0;
procedure PatchKeyboard;
var
e : ^chgentry;
entry : kbentry;
i : longint;
begin
fpIoctl(stdinputhandle,KDGKBMETA,@oldmeta);
meta:=K_ESCPREFIX;
fpIoctl(stdinputhandle,KDSKBMETA,@meta);
for i:=1 to kbdchanges do
begin
e:=@kbdchange[i];
entry.kb_table:=e^.tab;
entry.kb_index:=e^.idx;
fpIoctl(stdinputhandle,KDGKBENT,@entry);
e^.oldval:=entry.kb_value;
entry.kb_table:=e^.oldtab;
entry.kb_index:=e^.oldidx;
fpioctl(stdinputhandle,KDGKBENT,@entry);
e^.newval:=entry.kb_value;
end;
for i:=1 to kbdchanges do
begin
e:=@kbdchange[i];
entry.kb_table:=e^.tab;
entry.kb_index:=e^.idx;
entry.kb_value:=e^.newval;
fpioctl(stdinputhandle,KDSKBENT,@entry);
end;
end;
procedure UnpatchKeyboard;
var
e : ^chgentry;
entry : kbentry;
i : longint;
begin
if oldmeta in [K_ESCPREFIX,K_METABIT] then
fpioctl(stdinputhandle,KDSKBMETA,@oldmeta);
for i:=1 to kbdchanges do
begin
e:=@kbdchange[i];
entry.kb_table:=e^.tab;
entry.kb_index:=e^.idx;
entry.kb_value:=e^.oldval;
fpioctl(stdinputhandle,KDSKBENT,@entry);
end;
end;
{ Buffered Input routines }
const
InSize=256;
var
InBuf : array [0..InSize-1] of char;
InCnt,
InHead,
InTail : longint;
function ttyRecvChar:char;
var
Readed,i : longint;
begin
{Buffer Empty? Yes, Input from StdIn}
if (InHead=InTail) then
begin
{Calc Amount of Chars to Read}
i:=InSize-InHead;
if InTail>InHead then
i:=InTail-InHead;
{Read}
Readed:=fpRead(StdInputHandle,InBuf[InHead],i);
{Increase Counters}
inc(InCnt,Readed);
inc(InHead,Readed);
{Wrap if End has Reached}
if InHead>=InSize then
InHead:=0;
end;
{Check Buffer}
if (InCnt=0) then
ttyRecvChar:=#0
else
begin
ttyRecvChar:=InBuf[InTail];
dec(InCnt);
inc(InTail);
if InTail>=InSize then
InTail:=0;
end;
end;
Const
KeyBufferSize = 20;
var
KeyBuffer : Array[0..KeyBufferSize-1] of Char;
KeyPut,
KeySend : longint;
Procedure PushKey(Ch:char);
Var
Tmp : Longint;
Begin
Tmp:=KeyPut;
Inc(KeyPut);
If KeyPut>=KeyBufferSize Then
KeyPut:=0;
If KeyPut<>KeySend Then
KeyBuffer[Tmp]:=Ch
Else
KeyPut:=Tmp;
End;
Function PopKey:char;
Begin
If KeyPut<>KeySend Then
Begin
PopKey:=KeyBuffer[KeySend];
Inc(KeySend);
If KeySend>=KeyBufferSize Then
KeySend:=0;
End
Else
PopKey:=#0;
End;
Procedure PushExt(b:byte);
begin
PushKey(#0);
PushKey(chr(b));
end;
const
AltKeyStr : string[38]='qwertyuiopasdfghjklzxcvbnm1234567890-=';
AltCodeStr : string[38]=#016#017#018#019#020#021#022#023#024#025#030#031#032#033#034#035#036#037#038+
#044#045#046#047#048#049#050#120#121#122#123#124#125#126#127#128#129#130#131;
Function FAltKey(ch:char):byte;
var
Idx : longint;
Begin
Idx:=Pos(ch,AltKeyStr);
if Idx>0 then
FAltKey:=byte(AltCodeStr[Idx])
else
FAltKey:=0;
End;
{ This one doesn't care about keypresses already processed by readkey }
{ and waiting in the KeyBuffer, only about waiting keypresses at the }
{ TTYLevel (including ones that are waiting in the TTYRecvChar buffer) }
function sysKeyPressed: boolean;
var
fdsin : tfdSet;
begin
if (InCnt>0) then
sysKeyPressed:=true
else
begin
fpFD_ZERO(fdsin);
fpFD_SET(StdInputHandle,fdsin);
sysKeypressed:=(fpSelect(StdInputHandle+1,@fdsin,nil,nil,0)>0);
end;
end;
Function KeyPressed:Boolean;
Begin
Keypressed := (KeySend<>KeyPut) or sysKeyPressed;
End;
Function IsConsole : Boolean;
var
ThisTTY: String[30];
begin
IsConsole:=false;
{ check for tty }
if IsATTY(stdinputhandle) then
begin
{ running on a tty, find out whether locally or remotely }
ThisTTY:=TTYName(stdinputhandle);
if (Copy(ThisTTY, 1, 8) = '/dev/tty') and
(ThisTTY[9] >= '0') and (ThisTTY[9] <= '9') then
IsConsole:=true;
end;
end;
Const
LastMouseEvent : TMouseEvent =
(
Buttons : 0;
X : 0;
Y : 0;
Action : 0;
);
{$ifndef NotUseTree}
procedure GenMouseEvent;
var MouseEvent: TMouseEvent;
ch : char;
fdsin : tfdSet;
begin
fpFD_ZERO(fdsin);
fpFD_SET(StdInputHandle,fdsin);
Fillchar(MouseEvent,SizeOf(TMouseEvent),#0);
if InCnt=0 then
fpSelect(StdInputHandle+1,@fdsin,nil,nil,10);
ch:=ttyRecvChar;
{ Other bits are used for Shift, Meta and Ctrl modifiers PM }
case (ord(ch)-ord(' ')) and 3 of
0 : {left button press}
MouseEvent.buttons:=1;
1 : {middle button pressed }
MouseEvent.buttons:=2;
2 : { right button pressed }
MouseEvent.buttons:=4;
3 : { no button pressed };
end;
if InCnt=0 then
fpSelect(StdInputHandle+1,@fdsin,nil,nil,10);
ch:=ttyRecvChar;
MouseEvent.x:=Ord(ch)-ord(' ')-1;
if InCnt=0 then
fpSelect(StdInputHandle+1,@fdsin,nil,nil,10);
ch:=ttyRecvChar;
MouseEvent.y:=Ord(ch)-ord(' ')-1;
if (MouseEvent.buttons<>0) then
MouseEvent.action:=MouseActionDown
else
begin
if (LastMouseEvent.Buttons<>0) and
((LastMouseEvent.X<>MouseEvent.X) or (LastMouseEvent.Y<>MouseEvent.Y)) then
begin
MouseEvent.Action:=MouseActionMove;
MouseEvent.Buttons:=LastMouseEvent.Buttons;
{$ifdef DebugMouse}
Writeln(system.stderr,' Mouse Move (',MouseEvent.X,',',MouseEvent.Y,')');
{$endif DebugMouse}
PutMouseEvent(MouseEvent);
MouseEvent.Buttons:=0;
end;
MouseEvent.Action:=MouseActionUp;
end;
PutMouseEvent(MouseEvent);
{$ifdef DebugMouse}
if MouseEvent.Action=MouseActionDown then
Write(system.stderr,'Button down : ')
else
Write(system.stderr,'Button up : ');
Writeln(system.stderr,'buttons = ',MouseEvent.Buttons,' (',MouseEvent.X,',',MouseEvent.Y,')');
{$endif DebugMouse}
LastMouseEvent:=MouseEvent;
end;
type
TProcedure = procedure;
PTreeElement = ^TTreeElement;
TTreeElement = record
Next,Parent,Child : PTreeElement;
CanBeTerminal : boolean;
char : byte;
ScanValue : byte;
CharValue : byte;
SpecialHandler : TProcedure;
end;
var
RootTree : Array[0..255] of PTreeElement;
procedure FreeElement (PT:PTreeElement);
var next : PTreeElement;
begin
while PT <> nil do
begin
FreeElement(PT^.Child);
next := PT^.Next;
dispose(PT);
PT := next;
end;
end;
procedure FreeTree;
var i : integer;
begin
for i := low(RootTree) to high(RootTree) do
begin
FreeElement(RootTree[i]);
RootTree[i] := nil;
end;
end;
function NewPTree(ch : byte;Pa : PTreeElement) : PTreeElement;
var PT : PTreeElement;
begin
New(PT);
FillChar(PT^,SizeOf(TTreeElement),#0);
PT^.char:=ch;
PT^.Parent:=Pa;
if Assigned(Pa) and (Pa^.Child=nil) then
Pa^.Child:=PT;
NewPTree:=PT;
end;
function DoAddSequence(Const St : String; AChar,AScan :byte) : PTreeElement;
var
CurPTree,NPT : PTreeElement;
c : byte;
i : longint;
begin
if St='' then
begin
DoAddSequence:=nil;
exit;
end;
CurPTree:=RootTree[ord(st[1])];
if CurPTree=nil then
begin
CurPTree:=NewPTree(ord(st[1]),nil);
RootTree[ord(st[1])]:=CurPTree;
end;
for i:=2 to Length(St) do
begin
NPT:=CurPTree^.Child;
c:=ord(St[i]);
if NPT=nil then
NPT:=NewPTree(c,CurPTree);
CurPTree:=nil;
while assigned(NPT) and (NPT^.char<c) do
begin
CurPTree:=NPT;
NPT:=NPT^.Next;
end;
if assigned(NPT) and (NPT^.char=c) then
CurPTree:=NPT
else
begin
if CurPTree=nil then
begin
NPT^.Parent^.child:=NewPTree(c,NPT^.Parent);
CurPTree:=NPT^.Parent^.Child;
CurPTree^.Next:=NPT;
end
else
begin
CurPTree^.Next:=NewPTree(c,CurPTree^.Parent);
CurPTree:=CurPTree^.Next;
CurPTree^.Next:=NPT;
end;
end;
end;
if CurPTree^.CanBeTerminal then
begin
{ here we have a conflict !! }
{ maybe we should claim }
with CurPTree^ do
begin
{$ifdef DEBUG}
if (ScanValue<>AScan) or (CharValue<>AChar) then
Writeln(system.stderr,'key "',st,'" changed value');
if (ScanValue<>AScan) then
Writeln(system.stderr,'Scan was ',ScanValue,' now ',AScan);
if (CharValue<>AChar) then
Writeln(system.stderr,'Char was ',chr(CharValue),' now ',chr(AChar));
{$endif DEBUG}
ScanValue:=AScan;
CharValue:=AChar;
end;
end
else with CurPTree^ do
begin
CanBeTerminal:=True;
ScanValue:=AScan;
CharValue:=AChar;
end;
DoAddSequence:=CurPTree;
end;
procedure AddSequence(Const St : String; AChar,AScan :byte);
begin
DoAddSequence(St,AChar,AScan);
end;
{ Returns the Child that as c as char if it exists }
Function FindChild(c : byte;Root : PTreeElement) : PTreeElement;
var
NPT : PTreeElement;
begin
if not assigned(Root) then
begin
FindChild:=nil;
exit;
end;
NPT:=Root^.Child;
while assigned(NPT) and (NPT^.char<c) do
NPT:=NPT^.Next;
if assigned(NPT) and (NPT^.char=c) then
FindChild:=NPT
else
FindChild:=nil;
end;
Function AddSpecialSequence(Const St : string;Proc : TProcedure) : PTreeElement;
var
NPT : PTreeElement;
begin
NPT:=DoAddSequence(St,0,0);
NPT^.SpecialHandler:=Proc;
AddSpecialSequence:=NPT;
end;
function FindSequence(Const St : String;var AChar,AScan :byte) : boolean;
var
NPT : PTreeElement;
I : longint;
begin
FindSequence:=false;
AChar:=0;
AScan:=0;
if St='' then
exit;
NPT:=RootTree[ord(St[1])];
if not assigned(NPT) then
exit;
for i:=2 to Length(St) do
begin
NPT:=FindChild(ord(St[i]),NPT);
if not assigned(NPT) then
exit;
end;
if not NPT^.CanBeTerminal then
exit
else
begin
FindSequence:=true;
AScan:=NPT^.ScanValue;
AChar:=NPT^.CharValue;
end;
end;
Procedure LoadDefaultSequences;
begin
AddSpecialSequence(#27'[M',@GenMouseEvent);
{ linux default values, the next setting is
compatible with xterms from XFree 4.x }
DoAddSequence(#127,8,0);
{ all Esc letter }
DoAddSequence(#27'A',0,kbAltA);
DoAddSequence(#27'a',0,kbAltA);
DoAddSequence(#27'B',0,kbAltB);
DoAddSequence(#27'b',0,kbAltB);
DoAddSequence(#27'C',0,kbAltC);
DoAddSequence(#27'c',0,kbAltC);
DoAddSequence(#27'D',0,kbAltD);
DoAddSequence(#27'd',0,kbAltD);
DoAddSequence(#27'E',0,kbAltE);
DoAddSequence(#27'e',0,kbAltE);
DoAddSequence(#27'F',0,kbAltF);
DoAddSequence(#27'f',0,kbAltF);
DoAddSequence(#27'G',0,kbAltG);
DoAddSequence(#27'g',0,kbAltG);
DoAddSequence(#27'H',0,kbAltH);
DoAddSequence(#27'h',0,kbAltH);
DoAddSequence(#27'I',0,kbAltI);
DoAddSequence(#27'i',0,kbAltI);
DoAddSequence(#27'J',0,kbAltJ);
DoAddSequence(#27'j',0,kbAltJ);
DoAddSequence(#27'K',0,kbAltK);
DoAddSequence(#27'k',0,kbAltK);
DoAddSequence(#27'L',0,kbAltL);
DoAddSequence(#27'l',0,kbAltL);
DoAddSequence(#27'M',0,kbAltM);
DoAddSequence(#27'm',0,kbAltM);
DoAddSequence(#27'N',0,kbAltN);
DoAddSequence(#27'n',0,kbAltN);
DoAddSequence(#27'O',0,kbAltO);
DoAddSequence(#27'o',0,kbAltO);
DoAddSequence(#27'P',0,kbAltP);
DoAddSequence(#27'p',0,kbAltP);
DoAddSequence(#27'Q',0,kbAltQ);
DoAddSequence(#27'q',0,kbAltQ);
DoAddSequence(#27'R',0,kbAltR);
DoAddSequence(#27'r',0,kbAltR);
DoAddSequence(#27'S',0,kbAltS);
DoAddSequence(#27's',0,kbAltS);
DoAddSequence(#27'T',0,kbAltT);
DoAddSequence(#27't',0,kbAltT);
DoAddSequence(#27'U',0,kbAltU);
DoAddSequence(#27'u',0,kbAltU);
DoAddSequence(#27'V',0,kbAltV);
DoAddSequence(#27'v',0,kbAltV);
DoAddSequence(#27'W',0,kbAltW);
DoAddSequence(#27'w',0,kbAltW);
DoAddSequence(#27'X',0,kbAltX);
DoAddSequence(#27'x',0,kbAltX);
DoAddSequence(#27'Y',0,kbAltY);
DoAddSequence(#27'y',0,kbAltY);
DoAddSequence(#27'Z',0,kbAltZ);
DoAddSequence(#27'z',0,kbAltZ);
DoAddSequence(#27'-',0,kbAltMinus);
DoAddSequence(#27'=',0,kbAltEqual);
DoAddSequence(#27'0',0,kbAlt0);
DoAddSequence(#27'1',0,kbAlt1);
DoAddSequence(#27'2',0,kbAlt2);
DoAddSequence(#27'3',0,kbAlt3);
DoAddSequence(#27'4',0,kbAlt4);
DoAddSequence(#27'5',0,kbAlt5);
DoAddSequence(#27'6',0,kbAlt6);
DoAddSequence(#27'7',0,kbAlt7);
DoAddSequence(#27'8',0,kbAlt8);
DoAddSequence(#27'9',0,kbAlt9);
{ vt100 default values }
DoAddSequence(#27'[[A',0,kbF1);
DoAddSequence(#27'[[B',0,kbF2);
DoAddSequence(#27'[[C',0,kbF3);
DoAddSequence(#27'[[D',0,kbF4);
DoAddSequence(#27'[[E',0,kbF5);
DoAddSequence(#27'[17~',0,kbF6);
DoAddSequence(#27'[18~',0,kbF7);
DoAddSequence(#27'[19~',0,kbF8);
DoAddSequence(#27'[20~',0,kbF9);
DoAddSequence(#27'[21~',0,kbF10);
DoAddSequence(#27'[23~',0,kbF11);
DoAddSequence(#27'[24~',0,kbF12);
DoAddSequence(#27'[25~',0,kbShiftF3);
DoAddSequence(#27'[26~',0,kbShiftF4);
DoAddSequence(#27'[28~',0,kbShiftF5);
DoAddSequence(#27'[29~',0,kbShiftF6);
DoAddSequence(#27'[31~',0,kbShiftF7);
DoAddSequence(#27'[32~',0,kbShiftF8);
DoAddSequence(#27'[33~',0,kbShiftF9);
DoAddSequence(#27'[34~',0,kbShiftF10);
DoAddSequence(#27#27'[[A',0,kbAltF1);
DoAddSequence(#27#27'[[B',0,kbAltF2);
DoAddSequence(#27#27'[[C',0,kbAltF3);
DoAddSequence(#27#27'[[D',0,kbAltF4);
DoAddSequence(#27#27'[[E',0,kbAltF5);
DoAddSequence(#27#27'[17~',0,kbAltF6);
DoAddSequence(#27#27'[18~',0,kbAltF7);
DoAddSequence(#27#27'[19~',0,kbAltF8);
DoAddSequence(#27#27'[20~',0,kbAltF9);
DoAddSequence(#27#27'[21~',0,kbAltF10);
DoAddSequence(#27#27'[23~',0,kbAltF11);
DoAddSequence(#27#27'[24~',0,kbAltF12);
DoAddSequence(#27'[A',0,kbUp);
DoAddSequence(#27'[B',0,kbDown);
DoAddSequence(#27'[C',0,kbRight);
DoAddSequence(#27'[D',0,kbLeft);
DoAddSequence(#27'[F',0,kbEnd);
DoAddSequence(#27'[H',0,kbHome);
DoAddSequence(#27'[Z',0,kbShiftTab);
DoAddSequence(#27'[5~',0,kbPgUp);
DoAddSequence(#27'[6~',0,kbPgDn);
DoAddSequence(#27'[4~',0,kbEnd);
DoAddSequence(#27'[1~',0,kbHome);
DoAddSequence(#27'[2~',0,kbIns);
DoAddSequence(#27'[3~',0,kbDel);
DoAddSequence(#27#27'[A',0,kbAltUp);
DoAddSequence(#27#27'[B',0,kbAltDown);
DoAddSequence(#27#27'[D',0,kbAltLeft);
DoAddSequence(#27#27'[C',0,kbAltRight);
DoAddSequence(#27#27'[5~',0,kbAltPgUp);
DoAddSequence(#27#27'[6~',0,kbAltPgDn);
DoAddSequence(#27#27'[4~',0,kbAltEnd);
DoAddSequence(#27#27'[1~',0,kbAltHome);
DoAddSequence(#27#27'[2~',0,kbAltIns);
DoAddSequence(#27#27'[3~',0,kbAltDel);
DoAddSequence(#27'OP',0,kbF1);
DoAddSequence(#27'OQ',0,kbF2);
DoAddSequence(#27'OR',0,kbF3);
DoAddSequence(#27'OS',0,kbF4);
DoAddSequence(#27'Ot',0,kbF5);
DoAddSequence(#27'Ou',0,kbF6);
DoAddSequence(#27'Ov',0,kbF7);
DoAddSequence(#27'Ol',0,kbF8);
DoAddSequence(#27'Ow',0,kbF9);
DoAddSequence(#27'Ox',0,kbF10);
DoAddSequence(#27'Oy',0,kbF11);
DoAddSequence(#27'Oz',0,kbF12);
DoAddSequence(#27#27'OP',0,kbAltF1);
DoAddSequence(#27#27'OQ',0,kbAltF2);
DoAddSequence(#27#27'OR',0,kbAltF3);
DoAddSequence(#27#27'OS',0,kbAltF4);
DoAddSequence(#27#27'Ot',0,kbAltF5);
DoAddSequence(#27#27'Ou',0,kbAltF6);
DoAddSequence(#27#27'Ov',0,kbAltF7);
DoAddSequence(#27#27'Ol',0,kbAltF8);
DoAddSequence(#27#27'Ow',0,kbAltF9);
DoAddSequence(#27#27'Ox',0,kbAltF10);
DoAddSequence(#27#27'Oy',0,kbAltF11);
DoAddSequence(#27#27'Oz',0,kbAltF12);
DoAddSequence(#27'OA',0,kbUp);
DoAddSequence(#27'OB',0,kbDown);
DoAddSequence(#27'OC',0,kbRight);
DoAddSequence(#27'OD',0,kbLeft);
DoAddSequence(#27#27'OA',0,kbAltUp);
DoAddSequence(#27#27'OB',0,kbAltDown);
DoAddSequence(#27#27'OC',0,kbAltRight);
DoAddSequence(#27#27'OD',0,kbAltLeft);
{ xterm default values }
{ xterm alternate default values }
{ ignored sequences }
DoAddSequence(#27'[?1;0c',0,0);
DoAddSequence(#27'[?1l',0,0);
DoAddSequence(#27'[?1h',0,0);
DoAddSequence(#27'[?1;2c',0,0);
DoAddSequence(#27'[?7l',0,0);
DoAddSequence(#27'[?7h',0,0);
end;
function EnterEscapeSeqNdx(Ndx: Word;Char,Scan : byte) : PTreeElement;
var
P,pdelay: PChar;
St : string;
begin
EnterEscapeSeqNdx:=nil;
P:=cur_term_Strings^[Ndx];
if assigned(p) then
begin { Do not record the delays }
pdelay:=strpos(p,'$<');
if assigned(pdelay) then
pdelay^:=#0;
St:=StrPas(p);
EnterEscapeSeqNdx:=DoAddSequence(St,Char,Scan);
if assigned(pdelay) then
pdelay^:='$';
end;
end;
Procedure LoadTermInfoSequences;
var
err : longint;
begin
if not assigned(cur_term) then
setupterm(nil, stdoutputhandle, err);
if not assigned(cur_term_Strings) then
exit;
EnterEscapeSeqNdx(key_f1,0,kbF1);
EnterEscapeSeqNdx(key_f2,0,kbF2);
EnterEscapeSeqNdx(key_f3,0,kbF3);
EnterEscapeSeqNdx(key_f4,0,kbF4);
EnterEscapeSeqNdx(key_f5,0,kbF5);
EnterEscapeSeqNdx(key_f6,0,kbF6);
EnterEscapeSeqNdx(key_f7,0,kbF7);
EnterEscapeSeqNdx(key_f8,0,kbF8);
EnterEscapeSeqNdx(key_f9,0,kbF9);
EnterEscapeSeqNdx(key_f10,0,kbF10);
EnterEscapeSeqNdx(key_f11,0,kbF11);
EnterEscapeSeqNdx(key_f12,0,kbF12);
EnterEscapeSeqNdx(key_up,0,kbUp);
EnterEscapeSeqNdx(key_down,0,kbDown);
EnterEscapeSeqNdx(key_left,0,kbLeft);
EnterEscapeSeqNdx(key_right,0,kbRight);
EnterEscapeSeqNdx(key_ppage,0,kbPgUp);
EnterEscapeSeqNdx(key_npage,0,kbPgDn);
EnterEscapeSeqNdx(key_end,0,kbEnd);
EnterEscapeSeqNdx(key_home,0,kbHome);
EnterEscapeSeqNdx(key_ic,0,kbIns);
EnterEscapeSeqNdx(key_dc,0,kbDel);
EnterEscapeSeqNdx(key_stab,0,kbShiftTab);
{ EnterEscapeSeqNdx(key_,0,kb);
EnterEscapeSeqNdx(key_,0,kb); }
end;
{$endif not NotUseTree}
Function RawReadKey:char;
Var
fdsin : tfdSet;
Begin
{Check Buffer first}
if KeySend<>KeyPut then
begin
RawReadKey:=PopKey;
exit;
end;
{Wait for Key}
if not sysKeyPressed then
begin
fpFD_ZERO (fdsin);
fpFD_SET (StdInputHandle,fdsin);
fpSelect (StdInputHandle+1,@fdsin,nil,nil,nil);
end;
RawReadKey:=ttyRecvChar;
end;
Function RawReadString : String;
Var
ch : char;
fdsin : tfdSet;
St : String;
Begin
St:=RawReadKey;
fpFD_ZERO (fdsin);
fpFD_SET (StdInputHandle,fdsin);
Repeat
if InCnt=0 then
fpSelect(StdInputHandle+1,@fdsin,nil,nil,10);
if SysKeyPressed then
ch:=ttyRecvChar
else
ch:=#0;
if ch<>#0 then
St:=St+ch;
Until ch=#0;
RawReadString:=St;
end;
Function ReadKey(var IsAlt : boolean):char;
Var
ch : char;
{$ifdef NotUseTree}
OldState : longint;
State : longint;
{$endif NotUseTree}
is_delay : boolean;
fdsin : tfdSet;
store : array [0..8] of char;
arrayind : byte;
{$ifndef NotUseTree}
NPT,NNPT : PTreeElement;
{$else NotUseTree}
procedure GenMouseEvent;
var MouseEvent: TMouseEvent;
begin
Fillchar(MouseEvent,SizeOf(TMouseEvent),#0);
case ch of
#32 : {left button pressed }
MouseEvent.buttons:=1;
#33 : {middle button pressed }
MouseEvent.buttons:=2;
#34 : { right button pressed }
MouseEvent.buttons:=4;
#35 : { no button pressed };
end;
if InCnt=0 then
fpSelect(StdInputHandle+1,@fdsin,nil,nil,10);
ch:=ttyRecvChar;
MouseEvent.x:=Ord(ch)-ord(' ')-1;
if InCnt=0 then
fpSelect(StdInputHandle+1,@fdsin,nil,nil,10);
ch:=ttyRecvChar;
MouseEvent.y:=Ord(ch)-ord(' ')-1;
if (MouseEvent.buttons<>0) then
MouseEvent.action:=MouseActionDown
else
begin
if (LastMouseEvent.Buttons<>0) and
((LastMouseEvent.X<>MouseEvent.X) or (LastMouseEvent.Y<>MouseEvent.Y)) then
begin
MouseEvent.Action:=MouseActionMove;
MouseEvent.Buttons:=LastMouseEvent.Buttons;
PutMouseEvent(MouseEvent);
MouseEvent.Buttons:=0;
end;
MouseEvent.Action:=MouseActionUp;
end;
PutMouseEvent(MouseEvent);
LastMouseEvent:=MouseEvent;
end;
{$endif NotUseTree}
procedure RestoreArray;
var
i : byte;
begin
for i:=0 to arrayind-1 do
PushKey(store[i]);
end;
Begin
IsAlt:=false;
{Check Buffer first}
if KeySend<>KeyPut then
begin
ReadKey:=PopKey;
exit;
end;
{Wait for Key}
if not sysKeyPressed then
begin
fpFD_ZERO (fdsin);
fpFD_SET (StdInputHandle,fdsin);
fpSelect (StdInputHandle+1,@fdsin,nil,nil,nil);
end;
ch:=ttyRecvChar;
{$ifndef NotUseTree}
NPT:=RootTree[ord(ch)];
if not assigned(NPT) then
PushKey(ch)
else
begin
fpFD_ZERO(fdsin);
fpFD_SET(StdInputHandle,fdsin);
store[0]:=ch;
arrayind:=1;
while assigned(NPT) and syskeypressed do
begin
if (InCnt=0) then
fpSelect(StdInputHandle+1,@fdsin,nil,nil,10);
ch:=ttyRecvChar;
NNPT:=FindChild(ord(ch),NPT);
if assigned(NNPT) then
Begin
NPT:=NNPT;
if NPT^.CanBeTerminal and
assigned(NPT^.SpecialHandler) then
break;
End;
if ch<>#0 then
begin
store[arrayind]:=ch;
inc(arrayind);
end;
if not assigned(NNPT) then
begin
if ch<>#0 then
begin
{ Put that unused char back into InBuf }
If InTail=0 then
InTail:=InSize-1
else
Dec(InTail);
InBuf[InTail]:=ch;
inc(InCnt);
end;
break;
end;
end;
if assigned(NPT) and NPT^.CanBeTerminal then
begin
if assigned(NPT^.SpecialHandler) then
begin
NPT^.SpecialHandler;
PushExt(0);
end
else if NPT^.CharValue<>0 then
PushKey(chr(NPT^.CharValue))
else if NPT^.ScanValue<>0 then
PushExt(NPT^.ScanValue);
end
else
RestoreArray;
{$else NotUseTree}
{Esc Found ?}
If (ch=#27) then
begin
fpFD_ZERO(fdsin);
fpFD_SET(StdInputHandle,fdsin);
State:=1;
store[0]:=#27;
arrayind:=1;
{$ifdef logging}
write(f,'Esc');
{$endif logging}
if InCnt=0 then
fpSelect(StdInputHandle+1,@fdsin,nil,nil,10);
while (State<>0) and (sysKeyPressed) do
begin
ch:=ttyRecvChar;
store[arrayind]:=ch;
inc(arrayind);
{$ifdef logging}
if ord(ch)>31 then
write(f,ch)
else
write(f,'#',ord(ch):2);
{$endif logging}
OldState:=State;
State:=0;
case OldState of
1 : begin {Esc}
case ch of
'a'..'z',
'0'..'9',
'-','=' : PushExt(FAltKey(ch));
'A'..'N',
'P'..'Z' : PushExt(FAltKey(chr(ord(ch)+ord('a')-ord('A'))));
#10 : PushKey(#10);
#13 : PushKey(#10);
#27 : begin
IsAlt:=True;
State:=1;
end;
#127 : PushExt(kbAltDel);
'[' : State:=2;
'O' : State:=6;
else
RestoreArray;
end;
end;
2 : begin {Esc[}
case ch of
'[' : State:=3;
'A' : PushExt(kbUp);
'B' : PushExt(kbDown);
'C' : PushExt(kbRight);
'D' : PushExt(kbLeft);
'F' : PushExt(kbEnd);
'G' : PushKey('5');
'H' : PushExt(kbHome);
'K' : PushExt(kbEnd);
'M' : State:=13;
'1' : State:=4;
'2' : State:=5;
'3' : State:=12;{PushExt(kbDel)}
'4' : PushExt(kbEnd);
'5' : PushExt(73);
'6' : PushExt(kbPgDn);
'?' : State:=7;
else
RestoreArray;
end;
if ch in ['4'..'6'] then
State:=255;
end;
3 : begin {Esc[[}
case ch of
'A' : PushExt(kbF1);
'B' : PushExt(kbF2);
'C' : PushExt(kbF3);
'D' : PushExt(kbF4);
'E' : PushExt(kbF5);
else
RestoreArray;
end;
end;
4 : begin {Esc[1}
case ch of
'~' : PushExt(kbHome);
'7' : PushExt(kbF6);
'8' : PushExt(kbF7);
'9' : PushExt(kbF8);
else
RestoreArray;
end;
if (Ch<>'~') then
State:=255;
end;
5 : begin {Esc[2}
case ch of
'~' : PushExt(kbIns);
'0' : pushExt(kbF9);
'1' : PushExt(kbF10);
'3' : PushExt($85){F11, but ShiftF1 also !!};
'4' : PushExt($86){F12, but Shift F2 also !!};
'5' : PushExt($56){ShiftF3};
'6' : PushExt($57){ShiftF4};
'8' : PushExt($58){ShiftF5};
'9' : PushExt($59){ShiftF6};
else
RestoreArray;
end;
if (Ch<>'~') then
State:=255;
end;
12 : begin {Esc[3}
case ch of
'~' : PushExt(kbDel);
'1' : PushExt($5A){ShiftF7};
'2' : PushExt($5B){ShiftF8};
'3' : PushExt($5C){ShiftF9};
'4' : PushExt($5D){ShiftF10};
else
RestoreArray;
end;
if (Ch<>'~') then
State:=255;
end;
6 : begin {EscO Function keys in vt100 mode PM }
case ch of
'P' : {F1}PushExt(kbF1);
'Q' : {F2}PushExt(kbF2);
'R' : {F3}PushExt(kbF3);
'S' : {F4}PushExt(kbF4);
't' : {F5}PushExt(kbF5);
'u' : {F6}PushExt(kbF6);
'v' : {F7}PushExt(kbF7);
'l' : {F8}PushExt(kbF8);
'w' : {F9}PushExt(kbF9);
'x' : {F10}PushExt(kbF10);
'D' : {keyLeft}PushExt($4B);
'C' : {keyRight}PushExt($4D);
'A' : {keyUp}PushExt($48);
'B' : {keyDown}PushExt($50);
else
RestoreArray;
end;
end;
7 : begin {Esc[? keys in vt100 mode PM }
case ch of
'0' : State:=11;
'1' : State:=8;
'7' : State:=9;
else
RestoreArray;
end;
end;
8 : begin {Esc[?1 keys in vt100 mode PM }
case ch of
'l' : {local mode};
'h' : {transmit mode};
';' : { 'Esc[1;0c seems to be sent by M$ telnet app
for no hangup purposes }
state:=10;
else
RestoreArray;
end;
end;
9 : begin {Esc[?7 keys in vt100 mode PM }
case ch of
'l' : {exit_am_mode};
'h' : {enter_am_mode};
else
RestoreArray;
end;
end;
10 : begin {Esc[?1; keys in vt100 mode PM }
case ch of
'0' : state:=11;
else
RestoreArray;
end;
end;
11 : begin {Esc[?1;0 keys in vt100 mode PM }
case ch of
'c' : ;
else
RestoreArray;
end;
end;
13 : begin {Esc[M mouse prefix for xterm }
GenMouseEvent;
end;
255 : { just forget this trailing char };
end;
if (State<>0) and (InCnt=0) then
fpSelect(StdInputHandle+1,@fdsin,nil,nil,10);
end;
if State=1 then
PushKey(ch);
{$endif NotUseTree}
if ch='$' then
begin { '$<XX>' means a delay of XX millisecs }
is_delay :=false;
fpSelect(StdInputHandle+1,@fdsin,nil,nil,10);
if (sysKeyPressed) then
begin
ch:=ttyRecvChar;
is_delay:=(ch='<');
if not is_delay then
begin
PushKey('$');
PushKey(ch);
end
else
begin
{$ifdef logging}
write(f,'$<');
{$endif logging}
fpSelect(StdInputHandle+1,@fdsin,nil,nil,10);
while (sysKeyPressed) and (ch<>'>') do
begin
{ Should we really repect this delay ?? }
ch:=ttyRecvChar;
{$ifdef logging}
write(f,ch);
{$endif logging}
fpSelect(StdInputHandle+1,@fdsin,nil,nil,10);
end;
end;
end
else
PushKey('$');
end;
end
{$ifdef logging}
writeln(f);
{$endif logging}
{$ifndef NotUseTree}
;
ReadKey:=PopKey;
{$else NotUseTree}
else
Begin
case ch of
#127 : PushKey(#8);
else
PushKey(ch);
end;
End;
ReadKey:=PopKey;
{$endif NotUseTree}
End;
function ShiftState:byte;
var
arg,shift : longint;
begin
arg:=6;
shift:=0;
{$Ifndef BSD}
if fpioctl(StdInputHandle,TIOCLINUX,@arg)=0 then
begin
if (arg and 8)<>0 then
shift:=kbAlt;
if (arg and 4)<>0 then
inc(shift,kbCtrl);
{ 2 corresponds to AltGr so set both kbAlt and kbCtrl PM }
if (arg and 2)<>0 then
shift:=shift or (kbAlt or kbCtrl);
if (arg and 1)<>0 then
inc(shift,kbShift);
end;
{$endif}
ShiftState:=shift;
end;
{ Exported functions }
procedure SysInitKeyboard;
begin
SetRawMode(true);
patchkeyboard;
{$ifdef logging}
assign(f,'keyboard.log');
rewrite(f);
{$endif logging}
if not IsConsole then
begin
{ default for Shift prefix is ^ A}
if ShiftPrefix = 0 then
ShiftPrefix:=1;
{default for Alt prefix is ^Z }
if AltPrefix=0 then
AltPrefix:=26;
{ default for Ctrl Prefix is ^W }
if CtrlPrefix=0 then
CtrlPrefix:=23;
end;
{$ifndef NotUseTree}
LoadDefaultSequences;
LoadTerminfoSequences;
{$endif not NotUseTree}
end;
procedure SysDoneKeyboard;
begin
unpatchkeyboard;
SetRawMode(false);
{$ifndef NotUseTree}
FreeTree;
{$endif not NotUseTree}
{$ifdef logging}
close(f);
{$endif logging}
end;
function SysGetKeyEvent: TKeyEvent;
function EvalScan(b:byte):byte;
const
DScan:array[0..31] of byte = (
$39, $02, $28, $04, $05, $06, $08, $28,
$0A, $0B, $09, $0D, $33, $0C, $34, $35,
$0B, $02, $03, $04, $05, $06, $07, $08,
$09, $0A, $27, $27, $33, $0D, $34, $35);
LScan:array[0..31] of byte = (
$29, $1E, $30, $2E, $20, $12, $21, $22,
$23, $17, $24, $25, $26, $32, $31, $18,
$19, $10, $13, $1F, $14, $16, $2F, $11,
$2D, $15, $2C, $1A, $2B, $1B, $29, $0C);
begin
if (b and $E0)=$20 { digits / leters } then
EvalScan:=DScan[b and $1F]
else
case b of
$08:EvalScan:=$0E; { backspace }
$09:EvalScan:=$0F; { TAB }
$0D:EvalScan:=$1C; { CR }
$1B:EvalScan:=$01; { esc }
$40:EvalScan:=$03; { @ }
$5E:EvalScan:=$07; { ^ }
$60:EvalScan:=$29; { ` }
else
EvalScan:=LScan[b and $1F];
end;
end;
function EvalScanZ(b:byte):byte;
begin
EvalScanZ:=b;
if b in [$3B..$44] { F1..F10 -> Alt-F1..Alt-F10} then
EvalScanZ:=b+$2D;
end;
const
{kbHome, kbUp, kbPgUp,Missing, kbLeft,
kbCenter, kbRight, kbAltGrayPlus, kbend,
kbDown, kbPgDn, kbIns, kbDel }
CtrlArrow : array [kbHome..kbDel] of byte =
{($77,$8d,$84,$8e,$73,$8f,$74,$90,$75,$91,$76);}
(kbCtrlHome,kbCtrlUp,kbCtrlPgUp,kbNoKey,kbCtrlLeft,
kbCtrlCenter,kbCtrlRight,kbAltGrayPlus,kbCtrlEnd,
kbCtrlDown,kbCtrlPgDn,kbCtrlIns,kbCtrlDel);
AltArrow : array [kbHome..kbDel] of byte =
(kbAltHome,kbAltUp,kbAltPgUp,kbNoKey,kbAltLeft,
kbCenter,kbAltRight,kbAltGrayPlus,kbAltEnd,
kbAltDown,kbAltPgDn,kbAltIns,kbAltDel);
var
MyScan,
SState : byte;
MyChar : char;
EscUsed,AltPrefixUsed,CtrlPrefixUsed,ShiftPrefixUsed,IsAlt,Again : boolean;
begin {main}
MyChar:=Readkey(IsAlt);
MyScan:=ord(MyChar);
SState:=ShiftState;
CtrlPrefixUsed:=false;
AltPrefixUsed:=false;
ShiftPrefixUsed:=false;
EscUsed:=false;
if IsAlt then
SState:=SState or kbAlt;
repeat
again:=false;
if Mychar=#0 then
begin
MyScan:=ord(ReadKey(IsAlt));
{ Handle Ctrl-<x>, but not AltGr-<x> }
if ((SState and kbCtrl)<>0) and ((SState and kbAlt) = 0) then
begin
case MyScan of
kbHome..kbDel : { cArrow }
MyScan:=CtrlArrow[MyScan];
kbF1..KbF10 : { cF1-cF10 }
MyScan:=MyScan+kbCtrlF1-kbF1;
kbF11..KbF12 : { cF11-cF12 }
MyScan:=MyScan+kbCtrlF11-kbF11;
end;
end
{ Handle Alt-<x>, but not AltGr }
else if ((SState and kbAlt)<>0) and ((SState and kbCtrl) = 0) then
begin
case MyScan of
kbHome..kbDel : { AltArrow }
MyScan:=AltArrow[MyScan];
kbF1..KbF10 : { aF1-aF10 }
MyScan:=MyScan+kbAltF1-kbF1;
kbF11..KbF12 : { aF11-aF12 }
MyScan:=MyScan+kbAltF11-kbF11;
end;
end
else if (SState and kbShift)<>0 then
begin
case MyScan of
kbIns: MyScan:=kbShiftIns;
kbDel: MyScan:=kbShiftDel;
kbF1..KbF10 : { sF1-sF10 }
MyScan:=MyScan+kbShiftF1-kbF1;
kbF11..KbF12 : { sF11-sF12 }
MyScan:=MyScan+kbShiftF11-kbF11;
end;
end;
if (MyChar<>#0) or (MyScan<>0) or (SState<>0) then
SysGetKeyEvent:=$3000000 or ord(MyChar) or (MyScan shl 8) or (SState shl 16)
else
SysGetKeyEvent:=0;
exit;
end
else if MyChar=#27 then
begin
if EscUsed then
SState:=SState and not kbAlt
else
begin
SState:=SState or kbAlt;
Again:=true;
EscUsed:=true;
end;
end
else if (AltPrefix<>0) and (MyChar=chr(AltPrefix)) then
begin { ^Z - replace Alt for Linux OS }
if AltPrefixUsed then
begin
SState:=SState and not kbAlt;
end
else
begin
AltPrefixUsed:=true;
SState:=SState or kbAlt;
Again:=true;
end;
end
else if (CtrlPrefix<>0) and (MyChar=chr(CtrlPrefix)) then
begin
if CtrlPrefixUsed then
SState:=SState and not kbCtrl
else
begin
CtrlPrefixUsed:=true;
SState:=SState or kbCtrl;
Again:=true;
end;
end
else if (ShiftPrefix<>0) and (MyChar=chr(ShiftPrefix)) then
begin
if ShiftPrefixUsed then
SState:=SState and not kbShift
else
begin
ShiftPrefixUsed:=true;
SState:=SState or kbShift;
Again:=true;
end;
end;
if not again then
begin
MyScan:=EvalScan(ord(MyChar));
if ((SState and kbAlt)<>0) and ((SState and kbCtrl) = 0) then
begin
if MyScan in [$02..$0D] then
inc(MyScan,$76);
MyChar:=chr(0);
end
else if (SState and kbShift)<>0 then
if MyChar=#9 then
begin
MyChar:=#0;
MyScan:=kbShiftTab;
end;
end
else
begin
MyChar:=Readkey(IsAlt);
MyScan:=ord(MyChar);
if IsAlt then
SState:=SState or kbAlt;
end;
until not Again;
if (MyChar<>#0) or (MyScan<>0) or (SState<>0) then
SysGetKeyEvent:=$3000000 or ord(MyChar) or (MyScan shl 8) or (SState shl 16)
else
SysGetKeyEvent:=0;
end;
function SysPollKeyEvent: TKeyEvent;
var
KeyEvent : TKeyEvent;
begin
if keypressed then
begin
KeyEvent:=SysGetKeyEvent;
PutKeyEvent(KeyEvent);
SysPollKeyEvent:=KeyEvent
end
else
SysPollKeyEvent:=0;
end;
function SysGetShiftState : Byte;
begin
SysGetShiftState:=ShiftState;
end;
procedure RestoreStartMode;
begin
TCSetAttr(1,TCSANOW,StartTio);
end;
Const
SysKeyboardDriver : TKeyboardDriver = (
InitDriver : @SysInitKeyBoard;
DoneDriver : @SysDoneKeyBoard;
GetKeyevent : @SysGetKeyEvent;
PollKeyEvent : @SysPollKeyEvent;
GetShiftState : @SysGetShiftState;
TranslateKeyEvent : Nil;
TranslateKeyEventUnicode : Nil;
);
begin
SetKeyBoardDriver(SysKeyBoardDriver);
TCGetAttr(1,StartTio);
end.
{
$Log$
Revision 1.15 2003-09-16 16:13:56 marco
* fdset functions renamed to fp<posix name>
Revision 1.14 2003/09/14 20:15:01 marco
* Unix reform stage two. Remove all calls from Unix that exist in Baseunix.
Revision 1.13 2003/03/27 12:52:10 armin
* forgot to initialize RootTree to nil in FreeTree
Revision 1.12 2003/03/26 12:35:23 armin
* Free the Tree in SysDoneKeyboard to avoid a lot of messages if heaptrace is enabled
Revision 1.11 2002/09/07 16:01:27 peter
* old logs removed and tabs fixed
Revision 1.10 2002/03/03 13:23:51 peter
* adjust backspace sequence so it works according to the latest
XFree xterms and linux consoles
}