home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pmos2002.zip / SRC / keyboard.mod < prev    next >
Text File  |  1998-01-28  |  14KB  |  373 lines

  1. IMPLEMENTATION MODULE Keyboard;
  2.  
  3.         (****************************************************************)
  4.         (*                                                              *)
  5.         (*                      Keyboard Input                          *)
  6.         (*                                                              *)
  7.         (*  Programmer:         P. Moylan                               *)
  8.         (*  Last edited:        19 December 1997                        *)
  9.         (*  Status:             Working                                 *)
  10.         (*                                                              *)
  11.         (****************************************************************)
  12.  
  13. (************************************************************************)
  14. (*                                                                      *)
  15. (*  To allow the keyboard user to type ahead, this module contains a    *)
  16. (*  task which puts characters into a circular buffer, where they are   *)
  17. (*  kept until picked up by a call to InKey.  (There are already        *)
  18. (*  type-ahead facilities in the operating system, and also in the      *)
  19. (*  keyboard hardware itself; but doing things this way makes it        *)
  20. (*  easier to provide a "hot key" facility.)                            *)
  21. (*                                                                      *)
  22. (*  As a protection against deadlock, there is a timeout on the         *)
  23. (*  "circular buffer full" condition.  If the buffer remains full for   *)
  24. (*  too long, the oldest character in the buffer is discarded to make   *)
  25. (*  room for the newest character.                                      *)
  26. (*                                                                      *)
  27. (************************************************************************)
  28.  
  29. IMPORT OS2;
  30.  
  31. FROM LowLevel IMPORT
  32.     (* proc *)  IAND;
  33.  
  34. FROM Semaphores IMPORT
  35.     (* type *)  Semaphore,
  36.     (* proc *)  Signal;
  37.  
  38. FROM TaskControl IMPORT
  39.     (* proc *)  CreateTask;
  40.  
  41. FROM CircularBuffers IMPORT
  42.     (* type *)  CircularBuffer,
  43.     (* proc *)  CreateBuffer, PutBufferImpatient, GetBuffer, BufferEmpty;
  44.  
  45. (************************************************************************)
  46. (*                               GLOBALS                                *)
  47. (************************************************************************)
  48.  
  49. CONST
  50.  
  51.     (* Codes to specify the keyboard indicator lights.  *)
  52.  
  53.     ScrollLockLED = 1;
  54.     NumLockLED = 2;
  55.     CapsLockLED = 4;
  56.  
  57. (************************************************************************)
  58. (*                      THE 'HOT KEY' TABLES                            *)
  59. (************************************************************************)
  60.  
  61. TYPE CharSet = SET OF CHAR;
  62.  
  63. VAR HotKeys, HotFunctionKeys: CharSet;
  64.     HotKeySemaphore: ARRAY CHAR OF Semaphore;
  65.     HotFunctionKeySemaphore: ARRAY CHAR OF Semaphore;
  66.  
  67. (************************************************************************)
  68. (*                      THE CHARACTER BUFFER                            *)
  69. (************************************************************************)
  70.  
  71. CONST
  72.     CharBufferSize = 8;
  73.  
  74. VAR
  75.     (* CharBuffer is a circular buffer holding characters.      *)
  76.  
  77.     CharBuffer: CircularBuffer;
  78.  
  79.     (* The state of the three "lock" conditions.  *)
  80.  
  81.     CapsLock, NumLock, ScrollLock: BOOLEAN;
  82.  
  83.     (* A variable that is set unless the process is detached. *)
  84.  
  85.     ProcessIsNotDetached: BOOLEAN;
  86.  
  87. (************************************************************************)
  88. (*         PUTTING KEYBOARD CHARACTERS INTO THE CIRCULAR BUFFER         *)
  89. (************************************************************************)
  90.  
  91. PROCEDURE PutCode (FunctionKey: BOOLEAN;  code: CHAR);
  92.  
  93.     (* Puts a code into CharBuffer - unless it is a hot key, in which   *)
  94.     (* case it is dealt with immediately.                               *)
  95.  
  96.     BEGIN
  97.         IF FunctionKey THEN
  98.             IF code IN HotFunctionKeys THEN
  99.                 Signal (HotFunctionKeySemaphore[code]);
  100.             ELSE
  101.                 PutBufferImpatient (CharBuffer, CHR(0), 2000);
  102.                 PutBufferImpatient (CharBuffer, code, 2000);
  103.             END (*IF*);
  104.         ELSE
  105.             IF code IN HotKeys THEN
  106.                 Signal (HotKeySemaphore[code]);
  107.             ELSE
  108.                 PutBufferImpatient (CharBuffer, code, 2000);
  109.             END (*IF*);
  110.         END (*IF*);
  111.     END PutCode;
  112.  
  113. (************************************************************************)
  114.  
  115. PROCEDURE InputTask;
  116.  
  117.     (* This procedure, which is run as a separate task, picks up the    *)
  118.     (* keyboard input and stores it in CharBuffer.                      *)
  119.  
  120.     VAR KeyData: OS2.KBDKEYINFO;
  121.         result: CHAR;
  122.  
  123.     BEGIN
  124.         LOOP
  125.             OS2.KbdCharIn (KeyData, 0, 0);
  126.             result := KeyData.chChar;
  127.             IF (result = CHR(0)) OR (result = CHR(224)) THEN
  128.                 PutCode (TRUE, KeyData.chScan);
  129.             ELSE
  130.                 PutCode (FALSE, result);
  131.             END (*IF*);
  132.         END (*LOOP*);
  133.     END InputTask;
  134.  
  135. (************************************************************************)
  136. (*                          THE PUTBACK BUFFER                          *)
  137. (************************************************************************)
  138.  
  139. MODULE PutBackBuffer;
  140.  
  141.     (* Implementation of the PutBack procedure poses some awkward       *)
  142.     (* problems, to the point where it would not be worth implementing  *)
  143.     (* if it were not such a useful operation.  The obvious solution,   *)
  144.     (* of stuffing characters back into the character buffer, creates   *)
  145.     (* deadlock if we try to avoid losing characters, and creates some  *)
  146.     (* critical section problems even if we accept the risk of losing   *)
  147.     (* characters.  The critical section problems can easily be solved, *)
  148.     (* but only at the cost of making input less efficient, and this    *)
  149.     (* is hard to justify given that PutBack operations will typically  *)
  150.     (* be infrequent.  (That is, it is undesirable to cripple the       *)
  151.     (* "normal" case just for the sake of supporting a special case     *)
  152.     (* which accounts for just a small proportion of total operations). *)
  153.     (* The solution adopted in this version is to have a separate data  *)
  154.     (* structure to hold the characters which are put back.  These      *)
  155.     (* characters are held in a "lossy stack" - we discard the oldest   *)
  156.     (* datum whenever the stack is going to overflow.                   *)
  157.  
  158.     EXPORT
  159.         (* var  *)  SomeCharsSaved,
  160.         (* proc *)  Push, Pop;
  161.  
  162.     CONST
  163.         stacksize = 8;
  164.  
  165.     VAR
  166.         SomeCharsSaved: BOOLEAN;
  167.         stackptr: [0..stacksize];
  168.         stack: ARRAY [1..stacksize] OF CHAR;
  169.  
  170.     (********************************************************************)
  171.  
  172.     PROCEDURE Push (ch: CHAR);
  173.  
  174.         (* Pushes ch onto the stack.  If the stack is already full, the *)
  175.         (* character at the bottom of the stack is lost.                *)
  176.  
  177.         VAR j: [1..stacksize];
  178.  
  179.         BEGIN
  180.             IF stackptr = stacksize THEN
  181.                 FOR j := 1 TO stacksize-1 DO
  182.                     stack[j] := stack[j+1];
  183.                 END (*FOR*);
  184.                 stack[stacksize] := ch;
  185.             ELSE
  186.                 INC (stackptr);  stack[stackptr] := ch;
  187.                 SomeCharsSaved := TRUE;
  188.             END (*IF*);
  189.         END Push;
  190.  
  191.     (********************************************************************)
  192.  
  193.     PROCEDURE Pop(): CHAR;
  194.  
  195.         (* Returns the character from the top of the stack.     *)
  196.  
  197.         VAR result: CHAR;
  198.  
  199.         BEGIN
  200.             result := stack[stackptr];  DEC(stackptr);
  201.             SomeCharsSaved := stackptr > 0;
  202.             RETURN result;
  203.         END Pop;
  204.  
  205.     (********************************************************************)
  206.  
  207.     BEGIN
  208.         SomeCharsSaved := FALSE;
  209.         stackptr := 0;
  210.     END PutBackBuffer;
  211.  
  212. (************************************************************************)
  213. (*              THE EXTERNALLY CALLABLE INPUT PROCEDURES                *)
  214. (************************************************************************)
  215.  
  216. PROCEDURE KeyPressed(): BOOLEAN;
  217.  
  218.     (* Returns TRUE iff a character is available. *)
  219.  
  220.     BEGIN
  221.         RETURN SomeCharsSaved OR NOT BufferEmpty(CharBuffer);
  222.     END KeyPressed;
  223.  
  224. (************************************************************************)
  225.  
  226. PROCEDURE InKey(): CHAR;
  227.  
  228.     (* Reads one key from the keyboard, or from the putback      *)
  229.     (* buffer if any characters have been put back.              *)
  230.  
  231.     BEGIN
  232.         IF SomeCharsSaved THEN
  233.             RETURN Pop()
  234.         ELSE
  235.             RETURN GetBuffer (CharBuffer);
  236.         END (*IF*);
  237.     END InKey;
  238.  
  239. (************************************************************************)
  240.  
  241. PROCEDURE PutBack (ch: CHAR);
  242.  
  243.     (* This is an "un-read" operation, i.e. the character ch will       *)
  244.     (* re-appear on the next call to InKey.  This facility is provided  *)
  245.     (* for the use of software which can overshoot by one character     *)
  246.     (* when reading its input - a situation which can often occur.      *)
  247.  
  248.     BEGIN
  249.         Push (ch);
  250.     END PutBack;
  251.  
  252. (************************************************************************)
  253.  
  254. PROCEDURE StuffKeyboardBuffer (ch: CHAR);
  255.  
  256.     (* Stores ch as if it had come from the keyboard, so that a         *)
  257.     (* subsequent InKey() will pick it up.                              *)
  258.  
  259.     BEGIN
  260.         PutCode (FALSE, ch);
  261.     END StuffKeyboardBuffer;
  262.  
  263. (************************************************************************)
  264.  
  265. PROCEDURE StuffKeyboardBuffer2 (ch: CHAR);
  266.  
  267.     (* Like StuffKeyboardBuffer, but stores a two-byte sequence: Nul    *)
  268.     (* followed by ch.                                                  *)
  269.  
  270.     BEGIN
  271.         PutCode (TRUE, ch);
  272.     END StuffKeyboardBuffer2;
  273.  
  274. (************************************************************************)
  275.  
  276. PROCEDURE SetLocks (code: CARDINAL);
  277.  
  278.     (* Set/clear the caps lock, num lock, and scroll lock conditions.   *)
  279.     (* The code is defined in KBDRIVER.DEF.                             *)
  280.  
  281.     BEGIN
  282.         CapsLock := ORD(IAND (code, CapsLockLED)) <> 0;
  283.         NumLock := ORD(IAND (code, NumLockLED)) <> 0;
  284.         ScrollLock := ORD(IAND (code, ScrollLockLED)) <> 0;
  285.         (*
  286.         ClearLED (CapsLockLED+NumLockLED+ScrollLockLED);
  287.         ToggleLED (BYTE(code));
  288.         *)
  289.     END SetLocks;
  290.  
  291. (************************************************************************)
  292.  
  293. PROCEDURE LockStatus (): CARDINAL;
  294.  
  295.     (* Returns the current state of the caps lock, num lock, and scroll *)
  296.     (* lock conditions, using the code defined in KBDRIVER.DEF.         *)
  297.  
  298.     (* NOT YET IMPLEMENTED *)
  299.  
  300.     VAR result: CARDINAL;
  301.  
  302.     BEGIN
  303.         result := 0;
  304.         IF CapsLock THEN result := CapsLockLED END(*IF*);
  305.         IF NumLock THEN INC (result, NumLockLED) END(*IF*);
  306.         IF ScrollLock THEN INC (result, ScrollLockLED) END(*IF*);
  307.         RETURN result;
  308.     END LockStatus;
  309.  
  310. (************************************************************************)
  311.  
  312. PROCEDURE HotKey (FunctionKey: BOOLEAN;  code: CHAR;  S: Semaphore);
  313.  
  314.     (* After this procedure is called, typing the key combination for   *)
  315.     (* 'code' will cause a Signal(S).  Set FunctionKey=TRUE to trap one *)
  316.     (* of the two-character special function keys, and FALSE otherwise. *)
  317.     (* The character is consumed; if it should be passed on, then the   *)
  318.     (* user's hot key handler can do a PutBack().  Note: there is no    *)
  319.     (* provision for having multiple hot key handlers for the same key; *)
  320.     (* any existing hot key mapping will be overridden.                 *)
  321.  
  322.     BEGIN
  323.         IF FunctionKey THEN
  324.             INCL (HotFunctionKeys, code);
  325.             HotFunctionKeySemaphore[code] := S;
  326.         ELSE
  327.             INCL (HotKeys, code);
  328.             HotKeySemaphore[code] := S;
  329.         END (*IF*);
  330.     END HotKey;
  331.  
  332. (************************************************************************)
  333. (*                     CHECK FOR DETACHED MODE                          *)
  334. (************************************************************************)
  335.  
  336. PROCEDURE NotDetached(): BOOLEAN;
  337.  
  338.     (* Returns TRUE unless called by a process running detached.        *)
  339.     (* (A detached process may not do keyboard, screen, or mouse I/O.)  *)
  340.  
  341.     BEGIN
  342.         RETURN ProcessIsNotDetached;
  343.     END NotDetached;
  344.  
  345. (************************************************************************)
  346.  
  347. PROCEDURE DetachCheck;
  348.  
  349.     (* Sets the variable ProcessIsNotDetached. *)
  350.  
  351.     VAR pPib: OS2.PPIB;  pTib: OS2.PTIB;
  352.  
  353.     BEGIN
  354.         OS2.DosGetInfoBlocks (pTib, pPib);
  355.         ProcessIsNotDetached := pPib^.pib_ultype <> 4;
  356.     END DetachCheck;
  357.  
  358. (************************************************************************)
  359. (*                          INITIALISATION                              *)
  360. (************************************************************************)
  361.  
  362. BEGIN
  363.     HotKeys := CharSet{};
  364.     HotFunctionKeys := CharSet{};
  365.     CreateBuffer (CharBuffer, CharBufferSize);
  366.     DetachCheck;
  367.     IF ProcessIsNotDetached THEN
  368.         (*SetLocks (0);*)
  369.         CreateTask (InputTask, 8, "Keyboard main");
  370.     END (*IF*);
  371. END Keyboard.
  372.  
  373.