unit rezklav;
{Rezidentni obsluha klavesnice. Cte polohove kody, ktere pak pomoci tabulek}
{prevedu do ASCII znaku. Vse si udelam sam a nebudu zavisly na DOSovych}
{ovladacich klavesnice}
{Tato unita umi rozlisovat mezi normalnimi sipkami a sipkami na numericke
klavesnici. Pro pohodli programatora ale rovnez definuje kody, ktere mezi
nimi neodlisuji.
napr. Sipka vpravo na numericke klavesnici dava:
KEY_NUM_6 = true a KEY_CURSOR_RIGHT = true
Seda sipka vpravo dava:
KEY_G_CURSOR_RIGHT = true a KEY_CURSOR_RIGHT = true.
Vidite, ze KEY_CURSOR_RIGHT zustava spolecne.

Jestlize zavolate ZapniObsluhuKlavesnice s parametrem KL_S_BIOSEM, tak dal
budou normalne fungovat standardni funkce klavesnice jako
ReadKey, KeyPressed a vubec cely BIOS okolo klavesnice. Na druhou stranu se
musite starat, aby klavesnice nepipala.
Kdyz zavolate ZapniObsluhuKlavesnice(KL_BEZ_BIOSU) tak pipat zarucene nebude,
ale prestanou fungovat standardni pascali funkce okolo klavesnice.}


{Pri pohledu do zdrojaku zarazi velice bizarni chovani klavesy Pause.
Posila velmi exotickou sekvenci a hlavne nesignalizuje uvolneni klavesy.
Zvlastne se taky taky chova Printscreen. Pri zmacknuti totiz vysle 4-bajtovou
sekvenci. Pokud ho ale nepustime, ale drzime dal, tak periodicky vysila
jinou, a to 2-bajtovou sekvenci. Periodicke vysilani maji rovnez oba Alty.}


{$IFDEF FPC}
{$MODE FPC}
  {$IFDEF VER2}{$DEFINE NEWFPC}{$ENDIF}
  {$IFDEF VER3}{$DEFINE NEWFPC}{$ENDIF}
  {$IFDEF NEWFPC}{$CALLING OLDFPCCALL}{$ENDIF}
{$ENDIF}

{$Q-}     {debugovaci informace musi byt v kazdem pripade povypinana}
{$R-}     {jinak se to cele zhrouti}
{$S-}
{$D-}
{$F+}

{$DEFINE OPATRNOST}   {Zda se, ze je mozne ji vypnout...}

{$DEFINE _DEBUG_USER_HANDLER}

interface
{$IFDEF FPC}
uses go32;
{$ELSE}
uses dos;
{$ENDIF}

const KL_INT9_BEZ_BIOSU = 0;
      KL_INT9_S_BIOSEM = 1;
      KL_INT15 = 2;

      RK_MAX_KLAVES = 300;

type kevent = record
     mimoradny_kod:longint;
     scan:word;       {polohovy kod klavesnice}
     dos_ascii:word;  {ASCII kod podle ovladace DOSu}
     ascii:longint;   {ASCII kod bud podle ovl. DOSu nebo podle vlastni...}
                      {...prevodni rutiny}
     uni:longint;     {ASCII znak prelozeny do Unicode podle nahrane tabulky}
     priznaky:word;
     psaci:boolean;   {zda jde o psaci, nebo funkcni klavesu}
     end;

Pklavesy_def = ^Tklavesy_def;
Tklavesy_def = array[1..RK_MAX_KLAVES] of record
   p:longint;    {scan kod (polohovy kod)}
   a:longint;    {pro ASCII}
   u:longint;    {pro unicode}
   c:byte;       {zda reaguje na shift a na Capslock}
   end;

rk_putf8conv = ^rk_utf8conv;
rk_utf8conv = array[128..255] of word;


rk_Punifiltr = ^rk_Tunifiltr;
   rk_Tunifiltr = record
   t_velke_na_male:pointer; {tabulka pro prevod znaku na mala pismena}
   t_male_na_velke:pointer; {a znovu tabulka pro prevod na velka pismena}
   soubor:string[32];
   nazev:string;
   tbl_prevodnik:string[32];
   mapa:pklavesy_def;       {deklarovano v unite RezKlav}
   end;

{_xReadkey_Doplnek = function(b:byte):kevent;}
_xKeyPressed_Doplnek = function(b:byte):boolean;
_xPrevod_mimoradneho_kodu_xKeyPressed = procedure(k:kevent);

_External_Kbd_Debug = procedure(l:longint;b:byte);

{Funkce starajici se o klavesnici pomoci sluzeb DOSu a BIOSu}
function xReadKey:kevent;
function xKeyPressed:boolean;
Procedure HlidejKlavesy;
Procedure ZhltniKlavesu;
Procedure PustKlavesu;
function lKeyPressed:boolean;  {nahrada KeyPressed - nejak mi to funguje lepe}
Function KeyPriznaky:word;     {detekce CTRL, ALT a podobne}
function Je_CapsLock:boolean;
function Je_NumLock:boolean;
function Je_CTRL:boolean;
function Je_ALT:boolean;
Function Je_Shift:boolean;
Function DOS_mem_Je_shift:boolean;
Function PrelozAlt(w:word):byte;
Procedure RK_Nastav_Prevodni_tabulku_DOSASCII_unicode(p:pointer);
Procedure RK_NahrajUTF8Tabulku(s:string;var u:rk_utf8conv);
Function RK_Nahraj_Mapu_Klavesnice_ze_souboru(cesta,tbl_cesta,soubor:string;var klav:rk_Punifiltr):boolean;
Procedure RK_Aktivuj_Mapu_Uzivatelske_Klavesnice(m:rk_Punifiltr);
Function RK_Aktivni_Uzivatelska_Klavesnice:rk_Punifiltr;
Function RK_Aktivni_Uzivatelska_Klavesnice_soubor:string;
Function RK_Aktivni_Uzivatelska_Klavesnice_nazev:string;
Function RK_Aktivni_Uzivatelska_Klavesnice_male_velke:pointer;
Function RK_Aktivni_Uzivatelska_Klavesnice_velke_male:pointer;

function Internal_Je_CapsLock(k:kevent):boolean;
function Internal_Je_NumLock(k:kevent):boolean;
function Internal_Je_CTRL(k:kevent):boolean;
function Internal_Je_ALT(k:kevent):boolean;
Function Internal_Je_Shift(k:kevent):boolean;
Function Zmacknut_scankey_pismenkove_klavesy(taky_fn_klavesy:boolean):boolean;

function Je_CapsLock_dbg:boolean;
function Je_NumLock_dbg:boolean;


{Obsluha handleru}
procedure ZapniObsluhuKlavesnice(irezim:byte);
procedure VypniObsluhuKlavesnice;
Function Rez_Keypressed:boolean;

Procedure Volani_Puvodniho_handleru_INT15h; {zde jen pro debug}


const
{kody klaves, jak je vraci funkce xReadkey (diky Mircosofte) }
xF1=315;  xShiftF1=340;  xCtrlF1=350;    xAltF1=360;
xF2=316;  xShiftF2=341;  xCtrlF2=351;    xAltF2=361;
xF3=317;  xShiftF3=342;  xCtrlF3=352;    xAltF3=362;
xF4=318;  xShiftF4=343;  xCtrlF4=353;    xAltF4=363;
xF5=319;  xShiftF5=344;  xCtrlF5=354;    xAltF5=364;
xF6=320;  xShiftF6=345;  xCtrlF6=355;    xAltF6=365;
xF7=321;  xShiftF7=346;  xCtrlF7=356;    xAltF7=366;
xF8=322;  xShiftF8=347;  xCtrlF8=357;    xAltF8=367;
xF9=323;  xShiftF9=348;  xCtrlF9=358;    xAltF9=368;
xF10=324; xShiftF10=349; xCtrlF10=359;   xAltF10=369;
xF11=389; xShiftF11=391; xCtrlF11=393;   xAltF11=395;
xF12=390; xShiftF12=392; xCtrlF12=394;   xAltF12=396;
xLSipka=331; {xShiftLSipka=331;} xCtrlLSipka=371;  xAltLsipka=411;
xPSipka=333; {xShiftPSipka=333;} xCtrlPSipka=372;  xAltPSipka=413;
xHSipka=328; {xShiftHSipka=328;} xCtrlHSipka=397;  xAltHSipka=408;
xDSipka=336; {xShiftDSipka=336;} xCtrlDSipka=401;  xAltDSipka=416;
xAltX =301;
xIns=338;xDel=339;xBackSpace=8;
xHome=327;xEndk=335;xPgUp=329;xPgDn=337;xCtrlPgUp=388;
xCtrlG=7;xEnter=13;xESC=27;xTAB=9;xCtrlPgDn=374;xMezera=32;
xCtrlHome=375;xCtrlEnd=373;

xDOS_DEAD_KEY = -8;
xSKIP_BIOS = -9;
xPREFIX_KEY = -10;

xsLSipka=75;  xsHome=71;  xsDel=83;
xsPSipka=77;  xsEndk=79;  xsBackSpace=14;
xsHSipka=72;  xspgup=73;  xsCTRLins=146;
xsDSipka=80;  xspgdn=81;  xsIns=82;


Je_klavesa:boolean = false;
xKlavesa:kevent = (mimoradny_kod:0;
                   scan:0;
                   dos_ascii:0;
                   ascii:0;
                   uni:0;
                   priznaky:0;
                   psaci:false);

rk_mapa_uzivatelske_klavesnice:rk_Punifiltr = nil;
rk_ascii_utf_prevod:pointer = nil;

{xReadkey_Doplnek : _xReadkey_Doplnek = nil;}
xKeyPressed_Doplnek : _xKeyPressed_Doplnek = nil;
xPrevod_mimoradneho_kodu_xKeyPressed : _xPrevod_mimoradneho_kodu_xKeyPressed = nil;

External_Kbd_Debug : _External_Kbd_Debug = nil;

prefix_key:longint = 0;  {je spolecny pro ASCII i Unicode mapu}

{$I rezklav.inc}  {kody klaves}
var

    rezklav_interface_lock_start:byte;
    je_int9_handler:boolean;
    je_int15_handler:boolean;
    kl_kod:word;
    kl_zmena:boolean;
    vsechny_klavesy : Array[0..160] of boolean;
    AltBuf:word;
    kl_skipbios:boolean;
    mimoradny_kod_xKeyPressed:longint;
    {kod specialne detekovanych klaves vyplyvnutych uz z xKeyPressed
     (namisto standardni pozdejsi detekce z xReadKey)}

    debug_word:word;

    rezklav_interface_lock_end:byte;

implementation
uses konfig,vaznik,dos;


const
      levy_shift = 42;
      pravy_shift = 54;

      nulovani_klavesnice:boolean = false;
      key_timer_stamp:dword = 0;
      old_kl_kod:word = 0;

alt_w: array [1..26] of word =
    (272,273,274,275,276,277,278,279,280,281,
     286,287,288,289,290,291,292,293,294,
     300,301,302,303,304,305,306);

alt_s: string = 'qwertyuiopasdfghjklzxcvbnm';

var

{}  rezklav_implementation_lock_start:byte;
    oldint9_handler:tseginfo;
    newint9_handler:tseginfo;
    oldint15_handler:tseginfo;
    newint15_handler:tseginfo;

    oldint15_offset:word;
    oldint15_segment:word;

    global_regs:TRealRegs;

    int15_regs:TRealRegs; external name '___v2prt0_rmcb_regs';
    backupDS:Word; external name '___v2prt0_ds_alias';
    paltbuf:array[0..6] of byte; {je to zahada, ale se stringem mi to nefungovalo}
    obsluha:pointer;
    odpocet:byte;
    rezim_kbd_handleru:byte;
{}  rezklav_implementation_lock_end:byte;


    var_utf8conv:rk_utf8conv;


Procedure Handler_code_start;
begin
{dummy}
end;

Function Handler_ScanKod_do_pole_klaves(skod:byte):boolean;
var volej_bios:boolean;

  procedure ZpracujJednotu(alternativa,spolecna,ja:byte);
  {priklad funkce:
   stisknu levy CTRL -> pridela spolecny CTRL
   ulolnim levy CTRL -> uvolni spolecny CTRL a take pravy CTRL}
  begin
  if ja<>0 then vsechny_klavesy[ja]:=(kl_kod<128);
  if kl_kod<128 then vsechny_klavesy[spolecna]:=true else
     if vsechny_klavesy[alternativa]=false then
        vsechny_klavesy[spolecna]:=false;
  end;

  procedure PrectiAltovyBuffer;
  var i,j:longint;
  begin
  if vsechny_klavesy[SCANKEY_ALT]=false then
     begin
     altbuf:=0;
     j:=1;
     for i:=paltbuf[0] downto 1 do
         begin
         altbuf:=altbuf+paltbuf[i]*j;
         j:=j*10;
         end;
     paltbuf[0]:=0;
     end;
  end;

var klmod128,op:byte;
    ab:byte;
    cislo:boolean;

begin
kl_kod:=skod;
if kl_kod=$e1 then
   if odpocet=0 then odpocet:=7 else else {krkolome osetreni Pause}
if kl_kod=$e0 then
   if odpocet=0 then odpocet:=2 else else

if odpocet<3 then
   begin
   kl_zmena:=true;
   klmod128:=kl_kod mod 128;

   vsechny_klavesy[SCANKEY_PAUSE]:=false;     {vopruz z Pause}
   vsechny_klavesy[SCANKEY_CTRLBREAK]:=false; {stejne se chova CTRL-break}

   op:=odpocet;
   odpocet:=0;
   case op of
      2:case klmod128 of
           69:vsechny_klavesy[SCANKEY_PAUSE]:=true;         {Pause}
           70:vsechny_klavesy[SCANKEY_CTRLBREAK]:=true;     {CTRL-break}
           55:vsechny_klavesy[SCANKEY_PRINT]:=(kl_kod<128); {Printscreen poprve}
        end;

      1:case klmod128 of
           71..83:ZpracujJednotu(klmod128,klmod128+Priznak_jednoty,klmod128+Priznak_sedych_sipek);
           28:ZpracujJednotu(SCANKEY_G_ENTER,SCANKEY_ENTER,SCANKEY_NUM_ENTER);{sedy Enter}
           56:begin
              ZpracujJednotu(SCANKEY_LALT,SCANKEY_ALT,SCANKEY_PALT);          {pravy Alt}
              PrectiAltovyBuffer;
              end;
           29:ZpracujJednotu(SCANKEY_LCTRL,SCANKEY_CTRL,SCANKEY_PCTRL);       {pravy Ctrl}
           55:vsechny_klavesy[SCANKEY_PRINT]:=(kl_kod<128); {Printscreen opakovane}
           42,70:odpocet:=4;  {4-bajtova sekvence E0,neco,E0,neco}
           else vsechny_klavesy[klmod128]:=(kl_kod<128);
        end;

      0:begin
        vsechny_klavesy[klmod128]:=(kl_kod<128);
        case klmod128 of
           71..83:ZpracujJednotu(klmod128+Priznak_sedych_sipek,klmod128+Priznak_jednoty,klmod128);
           28:ZpracujJednotu(SCANKEY_NUM_ENTER,SCANKEY_ENTER,0); {num Enter}
           56:begin
              ZpracujJednotu(SCANKEY_PALT,SCANKEY_ALT,0);        {levy Alt}
              PrectiAltovyBuffer;
              end;
           29:ZpracujJednotu(SCANKEY_PCTRL,SCANKEY_CTRL,0);      {l. Ctrl}
           42:ZpracujJednotu(SCANKEY_PSHIFT,SCANKEY_SHIFT,0);    {l. Shift}
           54:ZpracujJednotu(SCANKEY_LSHIFT,SCANKEY_SHIFT,0);    {p. Shift}
        end;

        if (kl_kod<128) and (vsechny_klavesy[SCANKEY_ALT]) then
           begin {drzime Alt a pritom byla}{zmacknuta klavesa na numericke klavesnici?}
           case kl_kod of
              71:ab:=7;
              72:ab:=8;
              73:ab:=9;
              75:ab:=4;
              76:ab:=5;
              77:ab:=6;
              79:ab:=1;
              80:ab:=2;
              81:ab:=3;
              82:ab:=0;
              56:ab:=11;    {komplikace - periodicke vysilani Altu}
              else ab:=10;
           end;
           if ab<>11 then
              if ab=10 then paltbuf[0]:=0 else
                 if paltbuf[0]<5 then
                    begin
                    inc(paltbuf[0]);
                    paltbuf[paltbuf[0]]:=ab;
                    end
                    else begin
                    paltbuf[0]:=1;
                    paltbuf[1]:=ab;
                    end;
           end;
        end;
   end; {case odpocet}
   if kl_kod>127 then kl_kod:=0 else
      if vsechny_klavesy[SCANKEY_SHIFT] then inc(kl_kod,1000); {musim mit na pameti shifty}
   end; {if odpocet}


if odpocet>0 then dec(odpocet);




{if odpocet=0 then kl_zmena:=true;}

kl_skipbios:=false;
{KL_skipbios = true: jsou stiskle klavesy, ktere nejsou vhodne na nacitani
                     pres BIOS a je vhodne pouzit prime cteni pole
                     vsechny_klavesy

 KL_skipbios = false: melo by byt bezpecne dalsi predani klavesy BIOSovemu
                       handleru}


cislo:=false;


if (klmod128=SCANKEY_NUM_PLUS) or (klmod128=SCANKEY_NUM_MINUS)
   then cislo:=true;


(*
for ab:=SCANKEY_1 to SCANKEY_0 do
    if vsechny_klavesy[ab] then cislo:=true;   {je zmackle cislo v hor.rade?}
*)

(*
{????? -- kdyz nasledujici blok zakomentuju, tak v Blocku pri drzeni ALTu
          normalne blika kurzor a lze neco delat}
if vsechny_klavesy[KEY_ALT] then
   if vsechny_klavesy[KEY_SHIFT]
      then kl_skipbios:=kl_skipbios
      else kl_skipbios:=true;
{????? -- kdyz ho necham aktivni, tak se pri drzeni ALTu program jakovy zastavi}
*)


(*{and (cislo=true)} then kl_skipbios:=true;*)


if vsechny_klavesy[SCANKEY_CTRL] then   {zablokuje pro BIOS nektere komb. s CTRL}
   if vsechny_klavesy[SCANKEY_C] then kl_skipbios:=true else
   if cislo=true then kl_skipbios:=true;

{BIOSovy ovladac klavesnice "polyka" kombinace CTRL-C a CTRL-cislo.
 Proto pokud takovou situaci zdetekuji, tak BIOSovy handler preskocim}


if (rezim_kbd_handleru=0) or (kl_skipbios=true)
   then Handler_ScanKod_do_pole_klaves:=false
   else Handler_ScanKod_do_pole_klaves:=true;
end;



Function Handler_Klavesnice_pres_INT9:byte;
var b:boolean;
    k:byte;
begin
k:=InPortB($60);
b:=Handler_ScanKod_do_pole_klaves(k);
if b=false then
   asm
   mov dx,20h
   mov al,20h
   out dx,al
   stc        {nastavi CF, coz pouziju jako znak, ze mam preskocit BIOS}
   end else
   asm
   clc;       {vynuluje CF, tedy zavolam jeste BIOS}
   end;
end;



function CallRealModeIret(var my_regs:TRealRegs) : Boolean;assembler;
asm
   mov ax,302h
   mov ecx, 0
   mov ebx, 0
   mov edi,my_regs
   int 31h
   xor eax, eax
end;


function CallRealModeInterrupt(i:byte;var my_regs:TRealRegs) : Boolean;assembler;
asm
   mov ax,300h
   mov ecx, 0
   mov ebx, 0
   mov bl,i
   mov edi,my_regs
   int 31h
   xor eax, eax
end;



Procedure Volani_Puvodniho_handleru_INT15h;
var save_cs,save_ip,save_ss,save_sp:word;
begin
global_regs:=int15_regs;

global_regs.ip:=word(oldint15_handler.offset);
global_regs.cs:=word(oldint15_handler.segment);

global_regs.ss:=0;
global_regs.sp:=0;
CallRealModeIret(global_regs);

save_cs:=int15_regs.cs;
save_ip:=int15_regs.ip;
save_ss:=int15_regs.ss;
save_sp:=int15_regs.sp;

int15_regs:=global_regs;

int15_regs.cs:=save_cs;
int15_regs.ip:=save_ip;
int15_regs.ss:=save_ss;
int15_regs.sp:=save_sp;
end;


Function Handler_Klavesnice_pres_INT15:byte;
var b:boolean;
    k:byte;
begin
if int15_regs.ah=$4F then
   begin
   k:=int15_regs.al;
   b:=Handler_ScanKod_do_pole_klaves(k);
   end;

Volani_Puvodniho_handleru_INT15h;
end;



procedure int9_handler; {$IFDEF NEWFPC}nostackframe;{$ENDIF}assembler;
{Handler pro primarni preruseni (hardwarove)}
asm
cli
{$IFDEF OPATRNOST}
push ds
push es
push fs
push gs
pushad
{$ENDIF}
   mov ax,cs:[backupDS]
   mov ds,ax
   mov es,ax
   mov ax,dosmemselector
   mov fs,ax
   call obsluha
{$IFDEF OPATRNOST}
popad
pop gs
pop fs
pop es
pop ds
{$ENDIF}

clc

jc @preskoc_orig_handler  {pokud OBSLUHA nastavi CF, tak preskoc orig. handler}
jmp cs:[oldint9_handler]
@preskoc_orig_handler:

sti
iret  {Nutne. Pokud totiz neskocime do orig. handleru, tak to za nas nikdo jiny nezavola}
end;


procedure int9_nbhandler; {$IFDEF NEWFPC}nostackframe;{$ENDIF}assembler;
{Handler pro primarni preruseni (hardwarove)}
asm
cli
{$IFDEF OPATRNOST}
push ds
push es
push fs
push gs
pushad
{$ENDIF}
   mov ax,cs:[backupDS]
   mov ds,ax
   mov es,ax
   mov ax,dosmemselector
   mov fs,ax
   call obsluha
{$IFDEF OPATRNOST}
popad
pop gs
pop fs
pop es
pop ds
{$ENDIF}
sti
iret; {Navrat z preruseni. Variantou by bylo zde IRET vypustit, ale v tom}
end;  {pripade bychom museli celou proceduru volat s modifikatorem INTERRUPT}



procedure int15_handler; {$IFDEF NEWFPC}nostackframe;{$ENDIF}assembler;
{Handler pro sekundarni preruseni (softwarove, resp. softwarove presmerovani z hardwaroveho)}

{Pri vstupu plati, ze:
 DS:ESI = realmodovy SS:SP
 ES:EDI = zazrcadleni realmodovych registru do naseho protectmodoveho segmentu
 zakazana hardwarova reruseni}

 asm
 cli
 push ds
 push eax
 mov ax,es    {ES vede do naseho segmentu}
 mov ds,ax    {Necht je DS take v nasem segmentu}

 {Ted pozor - SS je ted uplne jine nez mimo handler, coz vede v nekterych
  situacich k zvlastnostem.
  Hlavni je to, ze nekdy spatne adresuje lokalni promenne.
  Typicky v situaci, kdy se vola (primo nebo neprimo) nejaka DPMI sluzba.
  Nejcasteji <SimulateRealmodeInterrupt> , napriklad uvnitr <writeln>.
  V handleru tedy nefunfuje <writeln> a v podstate nic, co v konecnem dusledku
  vola DOS nebo BIOS.
  Jediny zpusob jak to obejit je nepouzivat lokalni parametry - pouze globalni.
  Bohuzel, pokud si ze zalohy nahrajeme "normalni" SS, tak pak se to zhrouti
  uplne.}


 pushad
 mov ax,DOSmemSelector
 mov fs,ax
 call Obsluha

 popad

 pop eax
 pop ds       {Navrat DS do puvodniho stavu}

 push eax
 mov eax,ds:[esi+0]                         {obnova CS:IP}
 mov es:[edi+2Ah],eax

 (*
 mov eax,ds:[esi+4]                         {obnova priznaku}
 mov es:[edi+20h],ax
 *)

 add word ptr es:[edi+2Eh],6                {aktualizace SP}
 pop eax
 sti
 iret
end;


Procedure Handler_code_end;
begin
{dummy}
end;



procedure ZapniObsluhuKlavesnice(irezim:byte);
{rezim:
 0 = handler na INT9, nikde nepredavej na BIOS
 1 = handler na INT9, vetsinu klaves predavej na BIOS
 2 = handler na INT15h, tzn. vse prochazi BIOSem}
var vs,vo:word;
begin
kl_zmena:=false;
rezim_kbd_handleru:=irezim;
altbuf:=0;
FillChar(paltbuf,sizeof(paltbuf),0);
FillChar(vsechny_klavesy,sizeof(vsechny_klavesy),0);

lock_data(rezklav_interface_lock_start,
          longint(@rezklav_interface_lock_end) - longint(@rezklav_interface_lock_start)+1);

lock_data(rezklav_implementation_lock_start,
          longint(@rezklav_implementation_lock_end) - longint(@rezklav_implementation_lock_start)+1);

lock_data(dosmemselector, sizeof(dosmemselector));


lock_code(@Handler_code_start,longint(@Handler_code_end) - longint(@Handler_code_start));

asm cli end;
if rezim_kbd_handleru=2 then
   begin
   {Budeme obsazovat INT 15h/AH=4Fh (volano z realneho modu BIOSem)}
   obsluha:=@Handler_Klavesnice_pres_INT15;
   je_int15_handler:=true;
   get_rm_callback(@int15_handler, int15_regs, newint15_handler);
   {trik, ktery zajisti, abychom handler nemuseli psat v realmodovem assembleru
    a umistovat ho pod 1MB
    Takto muze byt procedure Int15_handler normalne v protected modu}

   Get_rm_interrupt($15,oldint15_handler);
   Set_rm_interrupt($15,newint15_handler);
   {presmerujeme realmodove preruseni}
   end
   else
   begin
   obsluha:=@Handler_Klavesnice_pres_INT9;
   if rezim_kbd_handleru=0 then newint9_handler.offset:=@int9_nbhandler else
   if rezim_kbd_handleru=1 then newint9_handler.offset:=@int9_handler;
   newint9_handler.segment:=get_cs;
   je_int9_handler:=true;
   get_pm_interrupt($9, oldint9_handler);
   set_pm_interrupt($9, newint9_handler);
   {presmerujeme protectmodove preruseni
    (hardwarova preruseni jsou vzdy primarne zpracovavana v protectu)}
   end;
asm sti end;
end;

procedure VypniObsluhuKlavesnice;
begin
if (je_int9_handler=false) and (je_int15_handler=false) then Exit;

asm cli end;
if je_int9_handler=true
   then set_pm_interrupt($9, oldint9_handler)
        {obnova protectmodoveho handleru}
   else set_rm_interrupt($15, oldint15_handler);
        {obnova realmodoveho handleru}
asm sti end;

unlock_data(rezklav_interface_lock_start,
          longint(@rezklav_interface_lock_end) - longint(@rezklav_interface_lock_start)+1);

unlock_data(rezklav_implementation_lock_start,
          longint(@rezklav_implementation_lock_end) - longint(@rezklav_implementation_lock_start)+1);

unlock_data(dosmemselector, sizeof(dosmemselector));
unlock_code(@Handler_code_start,longint(@Handler_code_end) - longint(@Handler_code_start));

je_int9_handler:=false;
je_int15_handler:=false;
end;


Procedure RK_Nastav_Prevodni_tabulku_DOSASCII_unicode(p:pointer);
begin
rk_ascii_utf_prevod:=p;
end;


Procedure RK_NahrajUTF8Tabulku(s:string;var u:rk_utf8conv);
var f:file;
    p:pointer;
    q:pchar;
    l:longint;
begin
Assign(f,s);
Reset(f,1);
l:=FileSize(f);
GetMem(p,l);
BlockRead(f,p^,l);
Close(f);
q:=p;
while q^<>#10 do inc(q);
inc(q,2);
Move(q^,u,sizeof(rk_utf8conv));
FreeMem(p,l);
end;


Function Rez_Keypressed:boolean;
begin
Rez_Keypressed:=kl_zmena and (kl_kod>0);
kl_zmena:=false;
end;


Function Zmacknut_scankey_pismenkove_klavesy(taky_fn_klavesy:boolean):boolean;
var b:longint;
begin
for b:=SCANKEY_Q to SCANKEY_P do
    if vsechny_klavesy[b]=true then Exit(true);
for b:=SCANKEY_A to SCANKEY_AE do
    if vsechny_klavesy[b]=true then Exit(true);
for b:=SCANKEY_Y to SCANKEY_MINUS do
    if vsechny_klavesy[b]=true then Exit(true);

if taky_fn_klavesy=true then
   begin
   for b:=SCANKEY_F1 to SCANKEY_F10 do
       if vsechny_klavesy[b]=true then Exit(true);
   if vsechny_klavesy[SCANKEY_F11]=true then Exit(true);
   if vsechny_klavesy[SCANKEY_F12]=true then Exit(true);
   end;

Zmacknut_scankey_pismenkove_klavesy:=false;
end;


Function lKeyPressed:boolean;assembler;
asm
mov ah,11h
int 16h
mov al,1
jnz @stisknuto
mov al,0
@stisknuto:
end;

Function KeyPriznaky:word;assembler;
asm
mov ax,1200h
int 16h
end;


function xKeyPressed:boolean;
var b,c,d:boolean;
begin
mimoradny_kod_xKeyPressed:=0;   {nulovani priznaku, ktere slouzi k detekci...}
                                {...zda se nestiskla BIOSem nezdetekovana...}
                                {klavesa (kombinace klaves)}

if xKeyPressed_Doplnek=nil then b:=false else b:=xKeyPressed_Doplnek(0);
if B=true then begin Exit(mimoradny_kod_xKeyPressed<>0);end;



if mimoradny_kod_xKeyPressed=0 then
   begin
   if kl_skipbios then  {specialni kombinace klaves na kterou...}
      begin             {...nenechame BIOS sahnout. Tyka se to CTRL-C...}
                        {...a take CTRL-cislo}
      mimoradny_kod_xKeyPressed:=xSKIP_BIOS;
      Exit(true);
      end;


   if rk_mapa_uzivatelske_klavesnice<>nil then
      begin
      {Uzivatelske klavesnice musim detekovat zvlast z toho duvodu, ze DOSovy a
       BIOSovy handler zamlci stisteni DOSove "dead key"}
       c:=Rez_keypressed;             {co si mysli rezidentni detektor?}
       d:=LKeypressed;                {a co BIOSovy detektor?}
       if (c=true) and (d=false) and    {jestli neni shoda, tak mozna mame dead key}
          (kl_kod<>SCANKEY_PSHIFT+1000) and (kl_kod<>SCANKEY_LSHIFT+1000) then
          begin
          mimoradny_kod_xKeyPressed:=xDOS_DEAD_KEY;
          Exit(true);
          end;
      xKeyPressed:=d;
      end
      else xKeyPressed:=LKeyPressed;

   end else xKeyPressed:=true;    {Mimoradny_kod_xKeypressed je TRUE?}
                                  {Tak musi byt TRUE i vysledek funkce}

(*
if xKeyPressed_Doplnek=nil
   then xKeyPressed:=LKeyPressed
        {standardni BIOS/DOS detekce zmackle klavesy}
   else xKeyPressed:=xKeyPressed_Doplnek(0);
        {vlastni detekce zmacklych klaves - napr lze primo zkoumat pole...
         ...<vsechny_klavesy>. V takovem pripade lze nastavit promennou...
         ...mimoradny_kod_xKeyPressed}
*)
end;


Function xReadKey_cteni_jen_z_biosu_byte:byte;assembler;
{v AH je scan kod (ignorujeme), v AL je ASCII (vracime)}
asm
mov ax,1000h
int 16h
end;


Procedure xReadKey_cteni_z_dosu_a_biosu(var dosascii,priznaky:word;var scankod:byte);assembler;
asm
@znovu:
   mov ax,1100h
   int 16h
   jz @znovu             {cekej, nez se v bufferu neco objevi}

   mov edi,scankod
   mov ds:[edi],ah       {zapamatuj si scan kod/rozsireny kod}

   {mov bh,255}



mov bh,0
   mov ax,0600h
   mov dl,0ffh
   int 21h
jz @nochar      {RBIL rika, ze pokud neni pripraven znak, tak je ZF=1}
   cmp al,0
   jnz @nochar

   int 21h
   inc bh
@nochar:
   mov ah,bh


(*
mov AX,$0700 {cislo sluzby pro cteni klaves}
   int $21      {precteni klavesy - normalni Readkey}
   or AL,AL      {neco udelame s AL, ve kterem se vratil ten znak (tak, abychom ho nezmenili), tim se nastavi ZF}
   jnz @vratznak {takze kdyz to AL neni 0, skocime dal >> }
   int $21        {jestli jsme jeste tady, tak v AL byla 0, takze precteme dalsi znak (v AX porad jeste je $07)}
   mov AH,1       {do AH ulozime 1, aby bylo poznat, ze to byla specialni klavesa}
   jmp @konec     {a mame hotovo.}
@vratznak:
   xor AH,AH     { >> vynulujeme AH, precteny znak zustane v AL a je vymalovano.}
@konec:
*)

   mov edi,dosascii
   mov ds:[edi],ax

   mov ax,1200h
   int 16h

   mov edi,priznaky
   mov ds:[edi],ax
end;


Function RK_PrelozZnak(b:longint;u:rk_putf8conv):longint;
begin
if (b<128) or (b>255) then RK_PrelozZnak:=b else RK_PrelozZnak:=u^[b];
end;


Procedure RK_Aktivuj_Mapu_Uzivatelske_Klavesnice(m:rk_Punifiltr);
begin
rk_mapa_uzivatelske_klavesnice:=m;
end;


Function RK_Aktivni_Uzivatelska_Klavesnice:rk_Punifiltr;
begin
RK_Aktivni_Uzivatelska_Klavesnice:=rk_mapa_uzivatelske_klavesnice;
end;


Function RK_Aktivni_Uzivatelska_Klavesnice_soubor:string;
begin
if RK_Aktivni_Uzivatelska_Klavesnice=nil
   then RK_Aktivni_Uzivatelska_Klavesnice_soubor:=''
   else RK_Aktivni_Uzivatelska_Klavesnice_soubor:=RK_Aktivni_Uzivatelska_Klavesnice^.soubor;
end;


Function RK_Aktivni_Uzivatelska_Klavesnice_nazev:string;
begin
if RK_Aktivni_Uzivatelska_Klavesnice=nil
   then RK_Aktivni_Uzivatelska_Klavesnice_nazev:=''
   else RK_Aktivni_Uzivatelska_Klavesnice_nazev:=RK_Aktivni_Uzivatelska_Klavesnice^.nazev;
end;


Function RK_Aktivni_Uzivatelska_Klavesnice_male_velke:pointer;
begin
if RK_Aktivni_Uzivatelska_Klavesnice=nil
   then RK_Aktivni_Uzivatelska_Klavesnice_male_velke:=nil
   else RK_Aktivni_Uzivatelska_Klavesnice_male_velke:=RK_Aktivni_Uzivatelska_Klavesnice^.t_male_na_velke;
end;


Function RK_Aktivni_Uzivatelska_Klavesnice_velke_male:pointer;
begin
if RK_Aktivni_Uzivatelska_Klavesnice=nil
   then RK_Aktivni_Uzivatelska_Klavesnice_velke_male:=nil
   else RK_Aktivni_Uzivatelska_Klavesnice_velke_male:=RK_Aktivni_Uzivatelska_Klavesnice^.t_velke_na_male;
end;


Function DOS_mem_funkcni_klavesy:byte;
begin
DOS_mem_funkcni_klavesy:=Mem[Seg0040:$17];
end;


Function DOS_mem_Je_shift:boolean;
var b:byte;
begin
b:=DOS_mem_funkcni_klavesy;
DOS_mem_Je_shift:=(b and 3)<>0;
end;


function Internal_Je_CapsLock(k:kevent):boolean;
begin
Internal_Je_CapsLock:=(k.priznaky and 64)<>0;
end;
function Je_CapsLock:boolean;
begin
Je_CapsLock:=Internal_Je_CapsLock(xKlavesa);
end;

function Internal_Je_NumLock(k:kevent):boolean;
begin
Internal_Je_Numlock:=(k.priznaky and 32)<>0;
end;
function Je_NumLock:boolean;
begin
Je_NumLock:=Internal_Je_NumLock(xKlavesa);
end;

function Internal_Je_CTRL(k:kevent):boolean;
begin
Internal_Je_CTRL:=(k.priznaky and 4)<>0;
end;
function Je_CTRL:boolean;
begin
Je_CTRL:=Internal_Je_CTRL(xKlavesa);
end;

function Internal_Je_ALT(k:kevent):boolean;
begin
Internal_Je_ALT:=(k.priznaky and 8)<>0;
end;
function Je_ALT:boolean;
begin
Je_ALT:=Internal_Je_ALT(xKlavesa);
end;

Function Internal_Je_Shift(k:kevent):boolean;
begin
Internal_Je_Shift:=(k.priznaky and 3)<>0;
end;
Function Je_Shift:boolean;
begin
Je_shift:=Internal_Je_Shift(xKlavesa);
end;



Function FromTimer:dword;assembler;
asm
mov eax,fs:[$400+$6c]
end;


Procedure Dummy_External_Kbd_Debug(l:longint;b:byte);
begin
{dummy}
end;


Procedure Zhodnot_zda_jde_o_psaci_klavesu(var e:kevent);
var i:word;
begin
{if e.scan=78 then
   write(#7);}
e.psaci:=false;
if Internal_Je_alt(e)=false then
   begin
   if e.mimoradny_kod=xDOS_DEAD_KEY then
   {POZOR!!!nedela duplikaty klaves?}
      if (e.ASCII<>0) and (e.uni<>0) and (e.DOS_ASCII=0)
         then begin
         {Problem je tento: pokud se zmackne "dead key" DOSoveho handleru,
          tak je to zdetekovano jako e.mimoradny_kod=xDOS_DEAD_KEY.
          Jenze <e.mimoradny_kod=xDOS_DEAD_KEY> taky nekdy vyvstane jen tak,
          pri stisteni bezne klavesy - jde o nesoulad mezi rezidentnim
          handlerem a BIOSovym.
          To se v praxi projevuje obcasnym zdvojovanim stistenych klaves.
          Toto je zadouci odfiltrovat}

         {Lze tedy shrnout, ze se snazim odlisit zakonite vznikle "dead keys"
          od tech nahodne vzniklych}
         if (e.scan=13) then e.psaci:=true;
         end;
         {nastane, kdyz nas handler prebije DOSovskou "dead key"}

   if e.mimoradny_kod=0 then
      begin
      if e.ASCII=0 then Exit;
      if e.ASCII=xPREFIX_KEY then Exit;

      if e.scan in [41,74{num-},78{num+},86] then e.psaci:=true;

      if (e.Scan>70) and (e.Scan<84) and (Internal_Je_NumLock(e)=false)
         then Exit;
      if (e.DOS_ASCII>31) and (e.DOS_ASCII<256) then e.psaci:=true else
      if (e.Scan>1) and (e.Scan<14) then e.psaci:=true else
      if (e.Scan>15) and (e.Scan<28) then e.psaci:=true else
      if (e.Scan>29) and (e.Scan<54) then e.psaci:=true else
      end;
   end;
end;


Procedure ProvedPrekladKlavesy(var e:kevent);
var mp:pklavesy_def;
    a,a2,kl,{km,}kk,scancode_z_mapy:longint;
    ft:dword;
    d,p:word;
    s:byte;
    je_treba_novy_pruchod:boolean;

begin
if e.mimoradny_kod<>0 then {Mimoradny kod? Jde o "dead key" ci o neco jineho?}
   if e.mimoradny_kod<>xDOS_DEAD_KEY then Exit;

   {Jestli slo o "dead key", tak to jeste budeme zkoumat, ale jestli o neco...}
   {...jineho, tak uz to resit nebudeme}

if rk_mapa_uzivatelske_klavesnice<>nil then  {Bezi podpora uziv. klavesnic?}
   if rk_mapa_uzivatelske_klavesnice^.mapa<>nil then {A je uziv. klavesnice...}
      begin                                          {...prave nastavena?}
      {Ano...}

      mp:=rk_mapa_uzivatelske_klavesnice^.mapa;
      je_treba_novy_pruchod:=false;
      kl:=kl_kod;
      {KL_KOD je globalni promenna z rezklav - scan kod stistene klavesy}
      {pokud je k tomu drzen SHIFT, tak je k nemu (jeste v obsluze preruseni)
       pricteno 1000}

      {km:=kl;}
      kk:=kl;

      if e.mimoradny_kod=xDOS_DEAD_KEY then
         if kl>=1000 then e.scan:=kl-1000 else e.scan:=kl;

      {$IFDEF DEBUG_USER_HANDLER}
      External_Kbd_Debug(kl,1);
      {$ENDIF}


      {if prefix<>0 then while xkeypressed do xReadKey;}
      {?}{if (prefix_key<>0) then
            while lKeyPressed do xReadKey_cteni_z_dosu_a_biosu(d,p,s);}

      {Ted bude divny blok kodu, ktery by mel omezovat stavy, kdy se klavesa
       detekuje dvakrat. Divne je, ze se to stava v MS-DOSu, ale ne ve FreeDOSu}

      {
      ft:=FromTimer;
      if ft=Key_Timer_Stamp then begin e.ascii:=0;e.uni:=0;Exit;end;
      if (ft=Key_Timer_Stamp+1) and (kl=old_kl_kod) then begin e.ascii:=0;e.uni:=0;Exit;end;
      Key_Timer_Stamp:=ft;
      old_kl_kod:=kl;
      }


      {----------------------------------------------------------------------}

      {Ted dulezity predpoklad - prefixove klavesy se chovaji jako prefixove}
      {v obou modech - ASCII i Unicode}

      kl:=kl+prefix_key;  {pricteme prefix (byl-li nejaky nastradany)}

      {if e.mimoradny_kod=xDOS_DEAD_KEY then write('D');}
      {write(kl,' ');}

      repeat


      for a:=1 to RK_MAX_KLAVES do
      {budeme prohledavat pole scankodu klaves, tak jak bylo nacteno
       na zacatku programu funkci ZpracujMapyKlavesnic}

          begin
          scancode_z_mapy:=mp^[a].p;  {beru scan kody z pole...}
          if scancode_z_mapy=kl then  {shoduje se se stistenou klavesou?}
             begin
             {write('(',a,') ');}
             e.ascii:=mp^[a].a;
             e.uni:=mp^[a].u;

             {if U then km:=aktualni_klavesnice^.mapa^[a].u
                  else km:=aktualni_klavesnice^.mapa^[a].a;}

             if e.ascii>=2000 then
                begin
                {Kod >=2000 neni kod klavesy, ale znak prefixu. V takovem pri-
                 pade si zapamatujeme prefix a koncime}
                prefix_key:=e.ascii;
                e.ascii:=xPREFIX_KEY;
                e.uni:=xPREFIX_KEY;
                Exit;                    {A tim muzeme ukoncit zpracovani}
                end;

             if (internal_je_capslock(e)=false) or (mp^[a].c=0)
             {pokud neni zaply CapsLock anebo CapsLock danou klavesu
              neovlivnuje, tak mame hotovo}
                then begin
                prefix_key:=0;

                {$IFDEF __DEBUG_USER_HANDLER}
                External_Kbd_Debug(e.ascii,2);
                {$ENDIF}

                Exit;
                end;

             {Pripad, ze je zmackly CapsLock, a ten ma na klasesu vratny vliv
              T.j. CapsLock zpusobi prevod na velka pismena, ale Shift to
              prebije zpet na mala. Tyka se pismen (q/Q, w/W, e/E atd)}
             if mp^[a].c=1 then
                begin
                if kk<1000 then a2:=a+1 else a2:=a-1;
                e.ascii:=mp^[a2].a;
                e.uni:=mp^[a2].u;
                prefix_key:=0;
                Exit;
                end;


             {Posledni moznost. Je zmackly CapsLock a ten ma na klavesu
              komplexni vliv. Pokud neni zmackly Shift, tak vraci velky
              ekvivalent pismena, ale pokud Shift zmackly je, tak to vraci
              nikoliv male pismeno, ale neco uplne jineho
              Tyka se napr. ceskych klaves jako (Ø/2, ç/3, Ÿ/4)}

             {1.podmoznost - je zmackly shift, ktery prebije CapsLock}
             if kk>1000 then
                begin   {Jeste v handleru Shift pricetl ke scan kodu 1000}
                prefix_key:=0;
                {Neni potreba nic menit, e.ASCII a e.UNI mame nastavene uz
                 od predtim}
                Exit;
                end;

             {2.podmoznost - Je zmackly CapsLock, ale nikoliv Shift}
             kl:=kl+mp^[a].c*1000;
             {v takovem pripade prictu specialni CapsLockovy prefix}

            (*{---Pro testovani kodu klaves---}
             bar(vga,400,0,500,20,64000);
             print_fn(410,18,mystr(kl),l_f);*)
             for a2:=1 to RK_MAX_KLAVES do {Znovu projedu cele pole scan kodu}
                 if mp^[a2].p=kl then
                    begin {zachyt?}
                    e.ascii:=mp^[a2].a;
                    e.uni:=mp^[a2].u;
                    prefix_key:=0;
                    Exit;
                    end;

             prefix_key:=0; {zachranna moznost - v definici klavesnice chybi}
             Exit;          {varianta klavesy s CapsLockovym prefixem...}
                            {...tak pouzijeme neprefixovanou variantu}
             end; {[if scancode_z_mapy=kl]}
             end; {[for a:=1 to RK_MAX_KLAVES]}



      {Ted jsme se dostali do bodu, kdy jsme proskenovali cele pole, ale
       nenasli jsme odpovidajici scan kod.}

       if prefix_key<>0 then
          begin
          {Muze to byt tim, ze byl od predtim namackly prefix, ale ted se
           zmackla klavesa, ktera neni s prefixem spojena}
          kl:=kl-prefix_key;
          prefix_key:=0;
          je_treba_novy_pruchod:=true;
          {V tom pripade prefix zahod a znovu proved scan pole klaves}
          end;

       until je_treba_novy_pruchod=false;


       {Porad nic?
        V takovem pripade jeste zkontroluju kody z numericke klavesnice}



      {---Pro testovani kodu klaves---}
      {bar(vga,400,0,500,20,64000);
      print_fn(410,18,mystr(km),l_f);}

      if Je_NumLock
         then Exit  {v tomto pripade akceptujeme prapuvodni kod}
         else begin
         {!_TODO_! -- nechavam puvodni kod k predelani}
         {//
         Function ZkontrolujNumerickou(o,kk:word):word;
         begin
         if Je_NumLock then Exit(o)
         else case o of
         KEY_NUM_7:o:=xHome;
         KEY_NUM_8:o:=xHSipka;
         KEY_NUM_9:o:=xPgUp;
         KEY_NUM_4:o:=xLSipka;
         KEY_NUM_5:o:=0;
         KEY_NUM_6:o:=xPSipka;
         KEY_NUM_1:o:=xEndk;
         KEY_NUM_2:o:=xDSipka;
         KEY_NUM_3:o:=xPgDn;
         KEY_NUM_0:o:=0;
         KEY_NUM_COMMA:o:=xIns;
         end;
         ZkontrolujNumerickou:=o;
         end;
         //}
         end;
  end {[if rk_mapa_uzivatelske_klavesnice^.mapa<>nil]}
end;


function xReadKey:kevent;
{Do xReadkey uz muze jit predem detekovany kod jeste z xKeyPressed. Timto
 "doprednym zpusobem" se detekuji napriklad BIOSem nezjistitelne kombinace
 klaves.
 Pokud xKeyPressed takto "dopredu zdetekovala" klavesu, tak bude nastavena
 v promenne <mimoradny_kod_xKeypressed>}
var e:kevent;
    dosascii:word;
    priznaky:word;
    scankod:byte;

begin
e.mimoradny_kod:=mimoradny_kod_xKeyPressed;
if e.mimoradny_kod<>0 then
   begin
   if xPrevod_mimoradneho_kodu_xKeyPressed=nil then
      begin {var 1: defaultni schema prevodu mimoradneho kodu na standardni kod}
      e.scan:=255;       {nastrkam se nejake definovane hodnoty}
      e.priznaky:=DOS_mem_funkcni_klavesy;
      {??? diskutabilni, zda je tento postup dostatecny}
      if e.mimoradny_kod=xDOS_DEAD_KEY then
         begin
         e.dos_ascii:=0;  {Defaultne se budeme chovat stejne jako DOS, t.j.}
         e.ascii:=0;      {mrtvou klavesu jakoby nevidime}
         e.uni:=0;
         end
         else begin                {pokud neslo o "dead key", ale o jinou}
         e.ascii:=e.mimoradny_kod; {mimoradne detekovanou klavesu, tak...}
         e.uni:=e.mimoradny_kod;   {...to prenes i do ASCII a UNI}
         e.dos_ascii:=255;
         end;
      end
      else  {var 2: lze definovat vlastni alternativni prevodni rutinu}
      xPrevod_mimoradneho_kodu_xKeyPressed(e);
   end
   else begin   {normalni detekovana klavesa, t.j. nikoliv pres <mimoradny_kod_xKeyPressed>}
   {Napred nechame pracovat DOS (bez ohledu na to, zda budeme pouzivat...
    ...vlastni rozvrzeni klavesnice)}
   xReadKey_cteni_z_dosu_a_biosu(dosascii,priznaky,scankod);
   if scankod=14 then dosascii:=8;  {zvlastni vec - pokud bylo soucasne zmacknuto...}
                                    {...CTRL, tak vracelo 127}

   e.dos_ascii:=dosascii;
   e.priznaky:=priznaky;
   e.scan:=scankod;       {Pozor - mozna bude za chvilku menit!}
                          {Zjistil jsem, ze nektere}
                          {BIOS/DOS/Win drivery delaji takovou prasarnu, ze}
                          {podle nastaveni narodni klavenisce meni nejen}
                          {ASCII kod ale i scankod.}
                          {Tyka se to ceske klavesnice a prohazovani Y/Z}

   if je_int9_handler or je_int15_handler then
      begin
      if kl_kod>=1000
         then e.scan:=kl_kod-1000
         else e.scan:=kl_kod;
      end;

   {Dalsi zpracovani...}
   {Ted prozatim budeme predpokladat, ze nemame uzivatelskou mapu...
    ...klavesnice a budeme se chovat podle DOSu. Eventualne to pozdeji...
    ...prebijeme v procedure <ProvedPrekladKlavesy>}
   e.ascii:=e.dos_ascii;

   if Internal_je_shift(e) {and (Internal_je_numlock(e)=false)} then
      begin
      if e.scan=SCANKEY_NUM_3 then e.ascii:=xPgDn else
      if e.scan=SCANKEY_NUM_9 then e.ascii:=xPgUp else
      if e.scan=SCANKEY_NUM_7 then e.ascii:=xHome else
      if e.scan=SCANKEY_NUM_8 then e.ascii:=xHSipka else
      if e.scan=SCANKEY_NUM_2 then e.ascii:=xDSipka else
      if e.scan=SCANKEY_NUM_4 then e.ascii:=xLSipka else
      if e.scan=SCANKEY_NUM_6 then e.ascii:=xPSipka;
      end;

   if rk_ascii_utf_prevod<>nil
      then e.uni:=RK_PrelozZnak(e.ascii,rk_ascii_utf_prevod)
      else e.uni:=0;
   end;

{Zhodnot_zda_jde_o_psaci_klavesu(e);}
ProvedPrekladKlavesy(e); {A ted eventualne nechame uradovat vlastni (non-DOS)}
                         {rozvrzeni}

Zhodnot_zda_jde_o_psaci_klavesu(e);
{puvodne bylo volano jeste pred <ProvedPrekladKlavesy>, ale to asi nebylo dobre}

xReadKey:=e;
End;{xreadkey}


Procedure HlidejKlavesy;
begin
Je_Klavesa:=xKeypressed;
if Je_klavesa=true then
   begin
   xKlavesa:=xReadKey;
   nulovani_klavesnice:=true;
   end
   else
   if nulovani_klavesnice then
      begin
      FillChar(xKlavesa,SizeOf(xKlavesa),0);
      nulovani_klavesnice:=false;
      end;
end;


Procedure PustKlavesu;
var dbgs:string;
    a,b,j:longint;
    w1,w2:word;
    b1:byte;
begin
{while Je_Klavesa do HlidejKlavesy;}

while LKeyPressed do xReadKey_cteni_jen_z_biosu_byte;

{Puvodne zde bylo xKeyPressed a xReadkey, ale xReadkey nevidelo vsechny
 xKeypressed a cekalo pak ve smycce na pristi klavesu}


while (vsechny_klavesy[SCANKEY_CTRL]=true) do begin end;
while (vsechny_klavesy[SCANKEY_ALT]=true) do begin end;

{a ted pro jistotu jeste klavesy, ktere xReadKey_cteni_z_dosu_a_biosu nemusi
 videt}
end;


Procedure ZhltniKlavesu;
begin
xKlavesa.scan:=0;
xKlavesa.ascii:=0;
end;


Function PrelozAlt(w:word):byte;
var i: byte;
begin
for i:=1 to 26 do
   if alt_w[i]=w then Exit(byte(alt_s[i]));
PrelozAlt:=0;
end;


Function MyVal(s:string):longint;
var Pom2,pom1 : Integer;
begin
Val (S, Pom1, Pom2);
MyVal := Pom1;
end;


Function Convert_up(s:string):string;
var a:byte;
begin
for a:=1 to Length(s) do s[a]:=UpCase(s[a]);
Convert_up:=s;
end;


Function RK_PrelozZnak(b:longint):longint;
begin
if (b<128) or (b>255)
   then RK_PrelozZnak:=b
   else RK_PrelozZnak:=var_utf8conv[b];
end;



Procedure ZpracujZnakMapyKlavesnic(i,k:longint;s:string;v:PKlavesy_def;c:byte);
var a,b:longint;
begin
v^[i].p:=k;   {prefixovany ci neprefixovany scan kod s nebo bez shiftu}
v^[i].c:=c;   {je tato klavesa ovlivnitelna CapsLockem?}
a:=Pos('(',s);   {je explicitne definovan unicode znak?}
if a=0 then
   begin         {ne}
   v^[i].a:=MyVal(s);
   {priradim ASCII hodnotu}
   v^[i].u:=RK_PrelozZnak(v^[i].a);
   {a z TBL souboru vytahnu UTF ekvivalent}
   end
   else begin    {ano}
   v^[i].a:=MyVal(Copy(s,1,a-1));     {to pred zavorkou je ASCII hodnota}
   b:=Pos(')',s);
   v^[i].u:=MyVal(Copy(s,a+1,b-a-1)); {a to v zavorce je UTF}
   end;
end;


Function RK_Nahraj_Mapu_Klavesnice_ze_souboru(cesta,tbl_cesta,soubor:string;var klav:rk_Punifiltr):boolean;
{FALSE = neco bylo spatne, soubor se nepovedlo nacist
 TRUE = v poradku nacteno}

var v:PKlavesy_def;
    uni,s:string;
    kf:TKonfig;
    pp:PUzel;
    vv:PKFpolozka;
    a,i,b:longint;
    caps:byte;

begin
klav:=nil;     {pro jistotu se napred nachystame na variantu, ze...}
               {...definicni soubor neni ve spravnem formatu}

caps:=0;
for a:=Low(var_utf8conv) to High(var_utf8conv) do
    var_utf8conv[a]:=a;

kf.init;
kf.NactiKonfiguraci({EXEdir+'key_tbl\'}cesta+soubor);


if kf.NactiPolozku('version','.')<>'2' then Exit(false);

pp:=kf.Je_v_komentari('[keyboard layout]'); {V PP bude uzel na radku, kde je...}
                                            {...retezec [keyboard layout]}
if pp=nil then Exit(false);  {Pokud to v celem souboru neni, pak nejde o platny soubor}

{no, uff, snad muzeme predpokladat, ze je soubor ve spravnem formatu}
New(klav);          {alokace pameti pro definice}
New(v);             {alokace pro mapu klavesnice v uzsim smyslu}
FillChar(v^,sizeof(Tklavesy_def),0);
klav^.mapa:=v;

klav^.soubor:=Convert_UP(soubor);

klav^.nazev:=kf.NactiPolozku('title','???');
{timto titulkem se bude mapa klavesnice prezentovat}
if klav^.nazev[1]='"' then
   begin delete(klav^.nazev,1,1);delete(klav^.nazev,Length(klav^.nazev),1);end;

klav^.t_velke_na_male:=nil;
klav^.t_male_na_velke:=nil;

{
q^.t_velke_na_male:=@DOS_velke_na_male;
q^.t_male_na_velke:=@DOS_male_na_velke;

s:=kf.NactiMultiPolozku('collation','?',' ');
if s<>'?' then NahrajPrevodniTabulku(q,s);
}

uni:=kf.NactiPolozku('unicodetable','?');  {defaultni mapovani unicode znaku}
if uni<>'?'
   then begin
        klav^.tbl_prevodnik:=uni;
        RK_NahrajUTF8tabulku(tbl_cesta+uni,var_utf8conv);
        end
   else klav^.tbl_prevodnik:='';

i:=1;
pp:=pp^.dalsi;      {PP jsme si nacetlio neco vyse - je to radka, kde je...}
                    {...napis [keyboard layout]}
while pp<>nil do
   begin
   vv:=pp^.vazba;
   if vv^.z=nil then  {1.varianta: radek bez znaku <ROVNA SE>}
      begin
      s:=Convert_UP(vv^.k^);
      if Copy(s,1,9)='-CAPSLOCK' then caps:=0 else
      if Copy(s,1,9)='+CAPSLOCK' then caps:=1 else
      if Copy(s,1,9)='*CAPSLOCK' then
         caps:=MyVal(Copy(s,10,5)) div 1000;
      end
   else begin         {2.varianta: radek se znakem <ROVNA SE>}
   a:=Pos(',',vv^.c^);   {Jsou v jednom radku definovane obe varianty?}                         {(t.j. bez shiftu a se shiftem?)}
   if a=0 then
      begin
      ZpracujZnakMapyKlavesnic(i,MyVal(vv^.z^),vv^.c^,klav^.mapa,caps);
      ZpracujZnakMapyKlavesnic(i+1,MyVal(vv^.z^)+1000,vv^.c^,klav^.mapa,caps);
      end
      else begin
      ZpracujZnakMapyKlavesnic(i,MyVal(vv^.z^),Copy(vv^.c^,1,a-1),klav^.mapa,caps);
      ZpracujZnakMapyKlavesnic(i+1,MyVal(vv^.z^)+1000,Copy(vv^.c^,a+1,255),klav^.mapa,caps);
      end;
   inc(i,2);
   end;

   pp:=pp^.dalsi;
   end;
kf.Done;
RK_Nahraj_Mapu_Klavesnice_ze_souboru:=true;
end;


function Je_CapsLock_dbg:boolean;
var w:word;
begin
{$IFDEF FPC}
asm
mov ax,fs:[$400+$17]
and ax,64
mov w,ax
end;
{$ELSE}
w:=MemW[Seg0040:$17] and 64;
{$ENDIF}
Je_CapsLock_dbg:=w<>0;
end;

function Je_NumLock_dbg:boolean;
var w:word;
begin
{$IFDEF FPC}
asm
mov ax,fs:[$400+$17]
and ax,8192
mov w,ax
end;
{$ELSE}
w:=MemW[Seg0040:$17] and 8192;
{$ENDIF}
Je_NumLock_dbg:=w<>0;
end;


Procedure Inicializace_promennych;
begin
je_int9_handler:=false;
je_int15_handler:=false;
odpocet:=0;
debug_word:=0;
end;


begin
{inicializace jednotky}
Inicializace_promennych;
External_Kbd_Debug:=@Dummy_External_Kbd_Debug;
end.
