keyboard.inc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. {
  2. System independent keyboard interface for linux
  3. $Id$
  4. }
  5. uses
  6. Linux;
  7. var
  8. OldIO : TermIos;
  9. Procedure SetRawMode(b:boolean);
  10. Var
  11. Tio : Termios;
  12. Begin
  13. TCGetAttr(1,Tio);
  14. if b then
  15. begin
  16. OldIO:=Tio;
  17. Tio.c_iflag:=Tio.c_iflag and (not (IGNBRK or BRKINT or PARMRK or ISTRIP or
  18. INLCR or IGNCR or ICRNL or IXON));
  19. Tio.c_lflag:=Tio.c_lflag and (not (ECHO or ECHONL or ICANON or ISIG or IEXTEN));
  20. end
  21. else
  22. Tio := OldIO;
  23. TCSetAttr(1,TCSANOW,Tio);
  24. End;
  25. type
  26. chgentry=packed record
  27. tab,
  28. idx,
  29. oldtab,
  30. oldidx : byte;
  31. oldval,
  32. newval : word;
  33. end;
  34. kbentry=packed record
  35. kb_table,
  36. kb_index : byte;
  37. kb_value : word;
  38. end;
  39. const
  40. kbdchanges=10;
  41. kbdchange:array[1..kbdchanges] of chgentry=(
  42. (tab:8; idx:$3b; oldtab:0; oldidx:$3b; oldval:0; newval:0),
  43. (tab:8; idx:$3c; oldtab:0; oldidx:$3c; oldval:0; newval:0),
  44. (tab:8; idx:$3d; oldtab:0; oldidx:$3d; oldval:0; newval:0),
  45. (tab:8; idx:$3e; oldtab:0; oldidx:$3e; oldval:0; newval:0),
  46. (tab:8; idx:$3f; oldtab:0; oldidx:$3f; oldval:0; newval:0),
  47. (tab:8; idx:$40; oldtab:0; oldidx:$40; oldval:0; newval:0),
  48. (tab:8; idx:$41; oldtab:0; oldidx:$41; oldval:0; newval:0),
  49. (tab:8; idx:$42; oldtab:0; oldidx:$42; oldval:0; newval:0),
  50. (tab:8; idx:$43; oldtab:0; oldidx:$43; oldval:0; newval:0),
  51. (tab:8; idx:$44; oldtab:0; oldidx:$44; oldval:0; newval:0)
  52. );
  53. KDGKBENT=$4B46;
  54. KDSKBENT=$4B47;
  55. procedure PatchKeyboard;
  56. var
  57. e : ^chgentry;
  58. entry : kbentry;
  59. i : longint;
  60. begin
  61. for i:=1to kbdchanges do
  62. begin
  63. e:=@kbdchange[i];
  64. entry.kb_table:=e^.tab;
  65. entry.kb_index:=e^.idx;
  66. Ioctl(stdinputhandle,KDGKBENT,@entry);
  67. e^.oldval:=entry.kb_value;
  68. entry.kb_table:=e^.oldtab;
  69. entry.kb_index:=e^.oldidx;
  70. ioctl(stdinputhandle,KDGKBENT,@entry);
  71. e^.newval:=entry.kb_value;
  72. end;
  73. for i:=1to kbdchanges do
  74. begin
  75. e:=@kbdchange[i];
  76. entry.kb_table:=e^.tab;
  77. entry.kb_index:=e^.idx;
  78. entry.kb_value:=e^.newval;
  79. Ioctl(stdinputhandle,KDSKBENT,@entry);
  80. end;
  81. end;
  82. procedure UnpatchKeyboard;
  83. var
  84. e : ^chgentry;
  85. entry : kbentry;
  86. i : longint;
  87. begin
  88. for i:=1to kbdchanges do
  89. begin
  90. e:=@kbdchange[i];
  91. entry.kb_table:=e^.tab;
  92. entry.kb_index:=e^.idx;
  93. entry.kb_value:=e^.oldval;
  94. Ioctl(stdinputhandle,KDSKBENT,@entry);
  95. end;
  96. end;
  97. { Buffered Input routines }
  98. const
  99. InSize=256;
  100. var
  101. InBuf : array[0..InSize-1] of char;
  102. InCnt,
  103. InHead,
  104. InTail : longint;
  105. function ttyRecvChar:char;
  106. var
  107. Readed,i : longint;
  108. begin
  109. {Buffer Empty? Yes, Input from StdIn}
  110. if (InHead=InTail) then
  111. begin
  112. {Calc Amount of Chars to Read}
  113. i:=InSize-InHead;
  114. if InTail>InHead then
  115. i:=InTail-InHead;
  116. {Read}
  117. Readed:=fdRead(StdInputHandle,InBuf[InHead],i);
  118. {Increase Counters}
  119. inc(InCnt,Readed);
  120. inc(InHead,Readed);
  121. {Wrap if End has Reached}
  122. if InHead>=InSize then
  123. InHead:=0;
  124. end;
  125. {Check Buffer}
  126. if (InCnt=0) then
  127. ttyRecvChar:=#0
  128. else
  129. begin
  130. ttyRecvChar:=InBuf[InTail];
  131. dec(InCnt);
  132. inc(InTail);
  133. if InTail>=InSize then
  134. InTail:=0;
  135. end;
  136. end;
  137. Const
  138. KeyBufferSize = 20;
  139. var
  140. KeyBuffer : Array[0..KeyBufferSize-1] of Char;
  141. KeyPut,
  142. KeySend : longint;
  143. Procedure PushKey(Ch:char);
  144. Var
  145. Tmp : Longint;
  146. Begin
  147. Tmp:=KeyPut;
  148. Inc(KeyPut);
  149. If KeyPut>=KeyBufferSize Then
  150. KeyPut:=0;
  151. If KeyPut<>KeySend Then
  152. KeyBuffer[Tmp]:=Ch
  153. Else
  154. KeyPut:=Tmp;
  155. End;
  156. Function PopKey:char;
  157. Begin
  158. If KeyPut<>KeySend Then
  159. Begin
  160. PopKey:=KeyBuffer[KeySend];
  161. Inc(KeySend);
  162. If KeySend>=KeyBufferSize Then
  163. KeySend:=0;
  164. End
  165. Else
  166. PopKey:=#0;
  167. End;
  168. Procedure PushExt(b:byte);
  169. begin
  170. PushKey(#0);
  171. PushKey(chr(b));
  172. end;
  173. const
  174. AltKeyStr : string[38]='qwertyuiopasdfghjklzxcvbnm1234567890-=';
  175. AltCodeStr : string[38]=#016#017#018#019#020#021#022#023#024#025#030#031#032#033#034#035#036#037#038+
  176. #044#045#046#047#048#049#050#120#121#122#123#124#125#126#127#128#129#130#131;
  177. Function FAltKey(ch:char):byte;
  178. var
  179. Idx : longint;
  180. Begin
  181. Idx:=Pos(ch,AltKeyStr);
  182. if Idx>0 then
  183. FAltKey:=byte(AltCodeStr[Idx])
  184. else
  185. FAltKey:=0;
  186. End;
  187. { This one doesn't care about keypresses already processed by readkey }
  188. { and waiting in the KeyBuffer, only about waiting keypresses at the }
  189. { TTYLevel (including ones that are waiting in the TTYRecvChar buffer) }
  190. function sysKeyPressed: boolean;
  191. var
  192. fdsin : fdSet;
  193. begin
  194. if (InCnt>0) then
  195. sysKeyPressed:=true
  196. else
  197. begin
  198. FD_Zero(fdsin);
  199. fd_Set(StdInputHandle,fdsin);
  200. sysKeypressed:=(Select(StdInputHandle+1,@fdsin,nil,nil,0)>0);
  201. end;
  202. end;
  203. Function KeyPressed:Boolean;
  204. Begin
  205. Keypressed := (KeySend<>KeyPut) or sysKeyPressed;
  206. End;
  207. Function ReadKey:char;
  208. Var
  209. ch : char;
  210. OldState,
  211. State : longint;
  212. fdsin : fdSet;
  213. Begin
  214. {Check Buffer first}
  215. if KeySend<>KeyPut then
  216. begin
  217. ReadKey:=PopKey;
  218. exit;
  219. end;
  220. {Wait for Key}
  221. if not sysKeyPressed then
  222. begin
  223. FD_Zero (fdsin);
  224. FD_Set (StdInputHandle,fdsin);
  225. Select (StdInputHandle+1,@fdsin,nil,nil,nil);
  226. end;
  227. ch:=ttyRecvChar;
  228. {Esc Found ?}
  229. If (ch=#27) then
  230. begin
  231. FD_Zero(fdsin);
  232. fd_Set(StdInputHandle,fdsin);
  233. State:=1;
  234. if InCnt=0 then
  235. Select(StdInputHandle+1,@fdsin,nil,nil,10);
  236. while (State<>0) and (sysKeyPressed) do
  237. begin
  238. ch:=ttyRecvChar;
  239. OldState:=State;
  240. State:=0;
  241. case OldState of
  242. 1 : begin {Esc}
  243. case ch of
  244. 'a'..'z',
  245. '0'..'9',
  246. '-','=' : PushExt(FAltKey(ch));
  247. #10 : PushKey(#10);
  248. #13 : PushKey(#10);
  249. #127 : PushKey(#8);
  250. '[' : State:=2;
  251. 'O' : State:=6;
  252. else
  253. begin
  254. PushKey(ch);
  255. PushKey(#27);
  256. end;
  257. end;
  258. end;
  259. 2 : begin {Esc[}
  260. case ch of
  261. '[' : State:=3;
  262. 'A' : PushExt(72);
  263. 'B' : PushExt(80);
  264. 'C' : PushExt(77);
  265. 'D' : PushExt(75);
  266. 'G' : PushKey('5');
  267. 'H' : PushExt(71);
  268. 'K' : PushExt(79);
  269. '1' : State:=4;
  270. '2' : State:=5;
  271. '3' : PushExt(83);
  272. '4' : PushExt(79);
  273. '5' : PushExt(73);
  274. '6' : PushExt(81);
  275. else
  276. begin
  277. PushKey(ch);
  278. PushKey('[');
  279. PushKey(#27);
  280. end;
  281. end;
  282. if ch in ['3'..'6'] then
  283. State:=255;
  284. end;
  285. 3 : begin {Esc[[}
  286. case ch of
  287. 'A' : PushExt(59);
  288. 'B' : PushExt(60);
  289. 'C' : PushExt(61);
  290. 'D' : PushExt(62);
  291. 'E' : PushExt(63);
  292. end;
  293. end;
  294. 4 : begin
  295. case ch of
  296. '~' : PushExt(71);
  297. '7' : PushExt(64);
  298. '8' : PushExt(65);
  299. '9' : PushExt(66);
  300. end;
  301. if (Ch<>'~') then
  302. State:=255;
  303. end;
  304. 5 : begin
  305. case ch of
  306. '~' : PushExt(82);
  307. '0' : pushExt(67);
  308. '1' : PushExt(68);
  309. '3' : PushExt(133);
  310. '4' : PushExt(134);
  311. end;
  312. if (Ch<>'~') then
  313. State:=255;
  314. end;
  315. 6 : begin {Esc0 Function keys in vt100 mode PM }
  316. case ch of
  317. 'P' : {F1}PushExt(59);
  318. 'Q' : {F2}PushExt(60);
  319. 'R' : {F3}PushExt(61);
  320. 'S' : {F4}PushExt(62);
  321. 't' : {F5}PushExt(63);
  322. 'u' : {F6}PushExt(64);
  323. 'v' : {F7}PushExt(65);
  324. 'l' : {F8}PushExt(66);
  325. 'w' : {F9}PushExt(67);
  326. 'x' : {F10}PushExt(68);
  327. end;
  328. end;
  329. 255 : ;
  330. end;
  331. if (State<>0) and (InCnt=0) then
  332. Select(StdInputHandle+1,@fdsin,nil,nil,10);
  333. end;
  334. if State=1 then
  335. PushKey(ch);
  336. end
  337. else
  338. Begin
  339. case ch of
  340. #127 : PushKey(#8);
  341. else
  342. PushKey(ch);
  343. end;
  344. End;
  345. ReadKey:=PopKey;
  346. End;
  347. function ShiftState:byte;
  348. var
  349. arg,shift : longint;
  350. begin
  351. arg:=6;
  352. shift:=0;
  353. if IOCtl(StdInputHandle,TIOCLINUX,@arg) then
  354. begin
  355. if (arg and (2 or 8))<>0 then
  356. inc(shift,8);
  357. if (arg and 4)<>0 then
  358. inc(shift,4);
  359. if (arg and 1)<>0 then
  360. inc(shift,3);
  361. end;
  362. ShiftState:=shift;
  363. end;
  364. { Exported functions }
  365. procedure InitKeyboard;
  366. begin
  367. SetRawMode(true);
  368. patchkeyboard;
  369. end;
  370. procedure DoneKeyboard;
  371. begin
  372. unpatchkeyboard;
  373. SetRawMode(false);
  374. end;
  375. function GetKeyEvent: TKeyEvent;
  376. function EvalScan(b:byte):byte;
  377. const
  378. DScan:array[0..31] of byte = (
  379. $39, $02, $28, $04, $05, $06, $08, $28,
  380. $0A, $0B, $09, $0D, $33, $0C, $34, $35,
  381. $0B, $02, $03, $04, $05, $06, $07, $08,
  382. $09, $0A, $27, $27, $33, $0D, $34, $35);
  383. LScan:array[0..31] of byte = (
  384. $29, $1E, $30, $2E, $20, $12, $21, $22,
  385. $23, $17, $24, $25, $26, $32, $31, $18,
  386. $19, $10, $13, $1F, $14, $16, $2F, $11,
  387. $2D, $15, $2C, $1A, $2B, $1B, $29, $0C);
  388. begin
  389. if (b and $E0)=$20 { digits / leters } then
  390. EvalScan:=DScan[b and $1F]
  391. else
  392. case b of
  393. $08:EvalScan:=$0E; { backspace }
  394. $09:EvalScan:=$0F; { TAB }
  395. $0D:EvalScan:=$1C; { CR }
  396. $1B:EvalScan:=$01; { esc }
  397. $40:EvalScan:=$03; { @ }
  398. $5E:EvalScan:=$07; { ^ }
  399. $60:EvalScan:=$29; { ` }
  400. else
  401. EvalScan:=LScan[b and $1F];
  402. end;
  403. end;
  404. function EvalScanZ(b:byte):byte;
  405. begin
  406. EvalScanZ:=b;
  407. if b in [$3B..$44] { F1..F10 -> Alt-F1..Alt-F10} then
  408. EvalScanZ:=b+$2D;
  409. end;
  410. const
  411. CtrlArrow : array [71..81] of byte =
  412. ($77,$8d,$84,$8e,$73,$8f,$74,$90,$75,$91,$76);
  413. var
  414. MyScan,
  415. SState : byte;
  416. MyChar : char;
  417. begin {main}
  418. if PendingKeyEvent<>0 then
  419. begin
  420. GetKeyEvent:=PendingKeyEvent;
  421. PendingKeyEvent:=0;
  422. exit;
  423. end;
  424. MyChar:=Readkey;
  425. MyScan:=ord(MyChar);
  426. SState:=ShiftState;
  427. case MyChar of
  428. #26 : begin { ^Z - replace Alt for Linux OS }
  429. MyChar:=ReadKey;
  430. MyScan:=ord(MyChar);
  431. if MyScan=0 then
  432. MyScan:=EvalScanZ(ord(ReadKey))
  433. else
  434. begin
  435. MyScan:=EvalScan(ord(MyChar));
  436. if MyScan in [$02..$0D] then
  437. inc(MyScan,$76);
  438. MyChar:=chr(0);
  439. end;
  440. end;
  441. #0 : begin
  442. MyScan:=ord(ReadKey);
  443. { Handle Ctrl-<x> }
  444. if (SState and 4)<>0 then
  445. begin
  446. case MyScan of
  447. 71..81 : { cArrow }
  448. MyScan:=CtrlArrow[MyScan];
  449. $3b..$44 : { cF1-cF10 }
  450. MyScan:=MyScan+$23;
  451. end;
  452. end;
  453. { Handle Alt-<x> }
  454. if (SState and 8)<>0 then
  455. begin
  456. case MyScan of
  457. $3b..$44 : { aF1-aF10 }
  458. MyScan:=MyScan+$2d;
  459. end;
  460. end;
  461. end;
  462. else begin
  463. MyScan:=EvalScan(ord(MyChar));
  464. end;
  465. end;
  466. GetKeyEvent:=$3000000 or ord(MyChar) or (MyScan shl 8) or (SState shl 16);
  467. end;
  468. function PollKeyEvent: TKeyEvent;
  469. begin
  470. if PendingKeyEvent<>0 then
  471. exit(PendingKeyEvent);
  472. if keypressed then
  473. begin
  474. { just get the key and place it in the pendingkeyevent }
  475. PendingKeyEvent:=GetKeyEvent;
  476. PollKeyEvent:=PendingKeyEvent;
  477. end
  478. else
  479. PollKeyEvent:=0;
  480. end;
  481. function PollShiftStateEvent: TKeyEvent;
  482. begin
  483. PollShiftStateEvent:=ShiftState shl 16;
  484. end;
  485. { Function key translation }
  486. type
  487. TTranslationEntry = packed record
  488. Min, Max: Byte;
  489. Offset: Word;
  490. end;
  491. const
  492. TranslationTableEntries = 12;
  493. TranslationTable: array [1..TranslationTableEntries] of TTranslationEntry =
  494. ((Min: $3B; Max: $44; Offset: kbdF1), { function keys F1-F10 }
  495. (Min: $54; Max: $5D; Offset: kbdF1), { Shift fn keys F1-F10 }
  496. (Min: $5E; Max: $67; Offset: kbdF1), { Ctrl fn keys F1-F10 }
  497. (Min: $68; Max: $71; Offset: kbdF1), { Alt fn keys F1-F10 }
  498. (Min: $85; Max: $86; Offset: kbdF11), { function keys F11-F12 }
  499. (Min: $87; Max: $88; Offset: kbdF11), { Shift+function keys F11-F12 }
  500. (Min: $89; Max: $8A; Offset: kbdF11), { Ctrl+function keys F11-F12 }
  501. (Min: $8B; Max: $8C; Offset: kbdF11), { Alt+function keys F11-F12 }
  502. (Min: 71; Max: 73; Offset: kbdHome), { Keypad keys kbdHome-kbdPgUp }
  503. (Min: 75; Max: 77; Offset: kbdLeft), { Keypad keys kbdLeft-kbdRight }
  504. (Min: 79; Max: 81; Offset: kbdEnd), { Keypad keys kbdEnd-kbdPgDn }
  505. (Min: $52; Max: $53; Offset: kbdInsert));
  506. function TranslateKeyEvent(KeyEvent: TKeyEvent): TKeyEvent;
  507. var
  508. I: Integer;
  509. ScanCode: Byte;
  510. begin
  511. if KeyEvent and $03000000 = $03000000 then
  512. begin
  513. if KeyEvent and $000000FF <> 0 then
  514. begin
  515. TranslateKeyEvent := KeyEvent and $00FFFFFF;
  516. exit;
  517. end
  518. else
  519. begin
  520. { This is a function key }
  521. ScanCode := (KeyEvent and $0000FF00) shr 8;
  522. for I := 1 to TranslationTableEntries do
  523. begin
  524. if (TranslationTable[I].Min <= ScanCode) and (ScanCode <= TranslationTable[I].Max) then
  525. begin
  526. TranslateKeyEvent := $02000000 + (KeyEvent and $00FF0000) +
  527. (ScanCode - TranslationTable[I].Min) + TranslationTable[I].Offset;
  528. exit;
  529. end;
  530. end;
  531. end;
  532. end;
  533. TranslateKeyEvent := KeyEvent;
  534. end;
  535. function TranslateKeyEventUniCode(KeyEvent: TKeyEvent): TKeyEvent;
  536. begin
  537. TranslateKeyEventUniCode := KeyEvent;
  538. ErrorHandler(errKbdNotImplemented, nil);
  539. end;
  540. {
  541. $Log$
  542. Revision 1.4 2000-10-15 09:18:22 peter
  543. * vt100 keys support merged
  544. Revision 1.3 2000/10/04 11:53:31 pierre
  545. Add TargetEntry and TargetExit (merged)
  546. Revision 1.2 2000/07/13 11:32:25 michael
  547. + removed logs
  548. }