-- Keyboard.Mesa  Edited by Sandman on May 22, 1980  3:24 PM
-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  AltoDisplay USING [Coordinate],
  InlineDefs USING [BITAND, BITSHIFT, BITXOR],
  KeyDefs USING [KeyArray, KeyBits, KeyItem, updown],
  KeyPrivate USING [Xmax, Ymax],
  StreamDefs USING [KeyboardHandle, KeyBufChars, StreamHandle];

Keyboard: MONITOR LOCKS monitor
  IMPORTS InlineDefs EXPORTS KeyDefs, KeyPrivate, StreamDefs SHARES StreamDefs =
  BEGIN OPEN KeyDefs, StreamDefs;

  monitor: PUBLIC MONITORLOCK;
  wakeup: PUBLIC CONDITION;
  charactersAvailable: PUBLIC CONDITION;

  -- variables set by KeyStreams

  ks: PUBLIC KeyboardHandle;

  userAbort, halt, trackCursor: PUBLIC BOOLEAN;

  KeyTable: PUBLIC POINTER TO ARRAY [0..80) OF KeyItem;

  -- The Keyboard part:
  -- fixed addresses for keyboard and mouse

  keys: PUBLIC POINTER TO KeyArray;

  mouse: PUBLIC POINTER TO AltoDisplay.Coordinate;
  cursor: PUBLIC POINTER TO AltoDisplay.Coordinate;

  oldState, newState: PUBLIC POINTER TO KeyArray;

  ProcessKeyboard: PUBLIC ENTRY PROCEDURE =
    BEGIN OPEN InlineDefs;
    bitcount, start: [0..15];
    char: [0..377B];
    entry: KeyItem;
    i: [0..SIZE[KeyArray]);
    newin: CARDINAL;
    StateWord: WORD;
    stroke: POINTER TO KeyBits = LOOPHOLE[newState];
    DO
      WAIT wakeup[ ! ABORTED => CONTINUE];
      IF halt THEN EXIT; -- first update the cursor
      IF trackCursor THEN
	BEGIN
	mouse.x ← cursor.x ← MAX[MIN[KeyPrivate.Xmax, mouse.x], 0];
	mouse.y ← cursor.y ← MAX[MIN[KeyPrivate.Ymax, mouse.y], 0];
	END;
      newState↑ ← keys↑;
      -- The following code checks for down transitions in the keyboard state
      -- and enters characters in the current keystream buffer
      FOR i IN [0..SIZE[KeyArray]) DO
	IF (StateWord ← BITXOR[oldState[i], newState[i]]) # 0 THEN
	  BEGIN -- found one or more transitions
	  start ← 0;
	  DO
	    FOR bitcount IN [start..15] DO
	      IF LOOPHOLE[StateWord, INTEGER] < 0 THEN EXIT;
	      StateWord ← BITSHIFT[StateWord, 1];
	      ENDLOOP;
	    entry ← KeyTable[i*16 + bitcount];
	    IF (char ← entry.NormalCode) # 0 AND BITAND[
	      oldState[i], BITSHIFT[100000B, -bitcount]] # 0 THEN
	      BEGIN
	      SELECT updown[down] FROM
		stroke.Ctrl =>
		  IF char = 177B THEN BEGIN userAbort ← TRUE; GOTO skip END
		  ELSE char ← BITAND[char, 37B];
		stroke.LeftShift, stroke.RightShift => char ← entry.ShiftCode;
		stroke.Lock => IF entry.Letter THEN char ← entry.ShiftCode;
		ENDCASE;
	      IF (newin ← ks.in + 1) = KeyBufChars THEN newin ← 0;
	      IF newin # ks.out THEN
		BEGIN
		ks.buffer[ks.in] ← LOOPHOLE[char];
		ks.in ← newin;
		BROADCAST charactersAvailable;
		END;
	      EXITS skip => NULL;
	      END;
	    IF (StateWord ← BITSHIFT[StateWord, 1]) = 0 THEN EXIT;
	    start ← bitcount + 1;
	    ENDLOOP;
	  END;
	ENDLOOP;
      oldState↑ ← newState↑;
      ENDLOOP;
    END;

  ReadChar: PUBLIC ENTRY PROCEDURE [stream: StreamHandle]
    RETURNS [char: UNSPECIFIED] =
    BEGIN
    char ← 0;
    WITH s: stream SELECT FROM
      Keyboard =>
	DO
	  -- until character typed
	  IF s.out # s.in THEN
	    BEGIN
	    char ← s.buffer[s.out];
	    s.out ← IF s.out = KeyBufChars - 1 THEN 0 ELSE s.out + 1;
	    RETURN
	    END;
	  WAIT charactersAvailable[ ! UNWIND => NULL];
	  ENDLOOP;
      ENDCASE;
    RETURN;
    END;

  InputBufferEmpty: PUBLIC ENTRY PROCEDURE [stream: StreamHandle]
    RETURNS [empty: BOOLEAN] =
    BEGIN
    empty ← TRUE;
    WITH s: stream SELECT FROM
      Keyboard => IF s.in # s.out THEN empty ← FALSE;
      ENDCASE;
    RETURN
    END;


  END.