-- KeyStreams.Mesa  Edited by Sandman on September 2, 1980  7:55 AM
-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  AltoDisplay USING [CursorXY, MouseXY],
  ControlDefs USING [GlobalFrameHandle],
  InlineDefs USING [BITAND, BITNOT, BITOR, BITSHIFT],
  FrameDefs USING [
    GlobalFrame, MakeCodeResident, SelfDestruct, UnlockCode, UnNew],
  KeyPrivate USING [
    charactersAvailable, cursor, halt, InputBufferEmpty, KeyboardData, keys,
    KeyTable, ks, monitor, mouse, newState, oldState, ProcessKeyboard, ReadChar,
    wakeup],
  KeyDefs USING [Keyboard, KeyItem, KeyName, Keys],
  ProcessDefs USING [CV, DIW, GetPriority, InterruptLevel, Priority, SetPriority],
  StreamDefs USING [
    KeyboardHandle, KeyBufChars, StreamError, StreamHandle, StreamObject,
    trackCursor, userAbort],
  Storage USING [Node, Free];

KeyStreams: MONITOR LOCKS KeyPrivate.monitor
  IMPORTS
    FrameDefs, InlineDefs, KeyDefs, KeyPrivate, ProcessDefs, StreamDefs, Storage
  EXPORTS KeyDefs, StreamDefs
  SHARES StreamDefs =
  BEGIN OPEN StreamDefs, KeyPrivate;

  -- The Stream part:

  data: POINTER TO KeyboardData;

  GetDefaultKey: PUBLIC PROCEDURE RETURNS [KeyboardHandle] =
    BEGIN RETURN[@data.stream]; END;

  GetCurrentKey: PUBLIC PROCEDURE RETURNS [KeyboardHandle] =
    BEGIN RETURN[ks]; END;

  CreateKeyStream: PUBLIC PROCEDURE RETURNS [KeyboardHandle] =
    BEGIN OPEN Storage;
    s: KeyboardHandle ← Storage.Node[SIZE[Keyboard StreamObject]];
    s↑ ← data.stream;
    s.in ← s.out ← 0;
    data.stream.link ← s;
    RETURN[s];
    END;

  OpenKeyStream: PUBLIC ENTRY PROCEDURE [stream: StreamHandle] =
    BEGIN
    WITH s: stream SELECT FROM
      Keyboard => ks ← @s;
      ENDCASE => SIGNAL StreamError[stream, StreamType ! UNWIND => NULL];
    RETURN;
    END;

  ClearInputBuffer: ENTRY PROCEDURE [stream: StreamHandle] =
    BEGIN
    WITH s: stream SELECT FROM
      Keyboard => s.in ← s.out ← 0;
      ENDCASE => SIGNAL StreamError[stream, StreamType ! UNWIND => NULL];
    RETURN;
    END;

  PutBackChar: ENTRY PROCEDURE [stream: StreamHandle, char: UNSPECIFIED] =
    BEGIN
    newout: CARDINAL;
    WITH s: stream SELECT FROM
      Keyboard =>
	BEGIN
	newout ← s.out;
	newout ← IF newout = 0 THEN KeyBufChars - 1 ELSE newout - 1;
	IF newout # s.in THEN
	  BEGIN
	  s.out ← newout;
	  s.buffer[s.out] ← char;
	  BROADCAST charactersAvailable;
	  END;
	END;
      ENDCASE => SIGNAL StreamError[stream, StreamType ! UNWIND => NULL];
    RETURN;
    END;

  WriteChar: PROCEDURE [stream: StreamHandle, char: UNSPECIFIED] =
    BEGIN SIGNAL StreamError[stream, StreamAccess]; RETURN END;

  CloseKeyStream: PUBLIC ENTRY PROCEDURE [stream: StreamHandle] =
    BEGIN
    WITH s: stream SELECT FROM
      Keyboard => ks ← @data.stream;
      ENDCASE => SIGNAL StreamError[stream, StreamType ! UNWIND => NULL];
    RETURN;
    END;

  DestroyKey: --ENTRY--PROCEDURE [stream: StreamHandle] =
    BEGIN OPEN Storage;
    prev: StreamHandle;
    WITH s: stream SELECT FROM
      Keyboard =>
	BEGIN
	IF @s = ks THEN ks ← @data.stream;
	IF @s # @data.stream THEN
	  BEGIN
	  IF data.stream.link = stream THEN data.stream.link ← stream.link
	  ELSE
	    FOR prev ← @data.stream, prev.link DO
	      IF prev.link = NIL THEN ERROR;
	      IF prev.link = stream THEN BEGIN prev.link ← stream.link; EXIT END;
	      ENDLOOP;
	  Free[@s ! UNWIND => NULL];
	  END;
	END;
      ENDCASE => SIGNAL StreamError[stream, StreamType ! UNWIND => NULL];
    RETURN;
    END;

  InitKeyTable: PROCEDURE =
    BEGIN OPEN KeyDefs;
    data.table ←
      [ -- MEMORY[177033B]	Index [0..15]
	KeyItem[FALSE, 0, 0], -- UNUSED
	KeyItem[FALSE, 0, 0], -- UNUSED
	KeyItem[FALSE, 0, 0], -- UNUSED
	KeyItem[FALSE, 0, 0], -- UNUSED
	KeyItem[FALSE, 0, 0], -- UNUSED
	KeyItem[FALSE, 0, 0], -- UNUSED
	KeyItem[FALSE, 0, 0], -- UNUSED
	KeyItem[FALSE, 0, 0], -- UNUSED
	KeyItem[FALSE, 0, 0], -- KeyItemset1
	KeyItem[FALSE, 0, 0], -- KeyItemset2
	KeyItem[FALSE, 0, 0], -- KeyItemset3
	KeyItem[FALSE, 0, 0], -- KeyItemset4
	KeyItem[FALSE, 0, 0], -- KeyItemset5
	KeyItem[FALSE, 0, 0], -- Red
	KeyItem[FALSE, 0, 0], -- Blue
	KeyItem[FALSE, 0, 0], -- Yellow
	-- MEMORY[177034B]	Index [16..31]
	KeyItem[FALSE, 45B, 65B], -- %,5
	KeyItem[FALSE, 44B, 64B], -- $,4
	KeyItem[FALSE, 176B, 66B], -- ~,6
	KeyItem[TRUE, 105B, 145B], -- E
	KeyItem[FALSE, 46B, 67B], -- &,7
	KeyItem[TRUE, 104B, 144B], -- D
	KeyItem[TRUE, 125B, 165B], -- U
	KeyItem[TRUE, 126B, 166B], -- V
	KeyItem[FALSE, 51B, 60B], -- ),0
	KeyItem[TRUE, 113B, 153B], -- K
	KeyItem[FALSE, 30B, 55B], -- `,-
	KeyItem[TRUE, 120B, 160B], -- P
	KeyItem[FALSE, 77B, 57B], -- ?,/
	KeyItem[FALSE, 174B, 134B], -- |,\
	KeyItem[FALSE, 12B, 12B], -- LF
	KeyItem[FALSE, 10B, 10B], -- BS
	-- MEMORY[177035B]	Index [32..47]
	KeyItem[FALSE, 43B, 63B], -- #,3
	KeyItem[FALSE, 100B, 62B], -- @,2
	KeyItem[TRUE, 127B, 167B], -- W
	KeyItem[TRUE, 121B, 161B], -- Q
	KeyItem[TRUE, 123B, 163B], -- S
	KeyItem[TRUE, 101B, 141B], -- A
	KeyItem[FALSE, 50B, 71B], -- (,9
	KeyItem[TRUE, 111B, 151B], -- I
	KeyItem[TRUE, 130B, 170B], -- X
	KeyItem[TRUE, 117B, 157B], -- O
	KeyItem[TRUE, 114B, 154B], -- L
	KeyItem[FALSE, 74B, 54B], -- <,,
	KeyItem[FALSE, 42B, 47B], -- ",'
	KeyItem[FALSE, 175B, 135B], -- },]
	KeyItem[FALSE, 0B, 0B], -- SPARE2
	KeyItem[FALSE, 27B, 27B], -- SPARE1
	-- MEMORY[177036B]	Index [48..63]
	KeyItem[FALSE, 41B, 61B], -- !,1
	KeyItem[FALSE, 33B, 33B], -- ESCAPE
	KeyItem[FALSE, 11B, 11B], -- TAB
	KeyItem[TRUE, 106B, 146B], -- F
	KeyItem[FALSE, 0B, 0B], -- CONTROL
	KeyItem[TRUE, 103B, 143B], -- C
	KeyItem[TRUE, 112B, 152B], -- J
	KeyItem[TRUE, 102B, 142B], -- B
	KeyItem[TRUE, 132B, 172B], -- Z
	KeyItem[FALSE, 0B, 0B], -- SHIFT
	KeyItem[FALSE, 76B, 56B], -- >,.
	KeyItem[FALSE, 72B, 73B], -- :,;
	KeyItem[FALSE, 15B, 15B], -- CR
	KeyItem[FALSE, 136B, 137B], -- ↑,←
	KeyItem[FALSE, 177B, 177B], -- DEL
	KeyItem[FALSE, 0B, 0B], -- NOT USED (FL3)
	-- MEMORY[177037B]	Index [64..79]
	KeyItem[TRUE, 122B, 162B], -- R
	KeyItem[TRUE, 124B, 164B], -- T
	KeyItem[TRUE, 107B, 147B], -- G
	KeyItem[TRUE, 131B, 171B], -- Y
	KeyItem[TRUE, 110B, 150B], -- H
	KeyItem[FALSE, 52B, 70B], -- *,8
	KeyItem[TRUE, 116B, 156B], -- N
	KeyItem[TRUE, 115B, 155B], -- M
	KeyItem[FALSE, 0B, 0B], -- LOCK
	KeyItem[FALSE, 40B, 40B], -- SPACE
	KeyItem[FALSE, 173B, 133B], -- {,[
	KeyItem[FALSE, 53B, 75B], -- +,=
	KeyItem[FALSE, 0B, 0B], -- SHIFT
	KeyItem[FALSE, 0B, 0B], -- SPARE3
	KeyItem[FALSE, 0B, 0B], -- NOT USED (FL4)
	KeyItem[FALSE, 0B, 0B]]; -- NOT USED (FR5)

    END;

  ChangeKey: PUBLIC PROCEDURE [key: KeyDefs.KeyName, action: KeyDefs.KeyItem]
    RETURNS [oldAction: KeyDefs.KeyItem] =
    BEGIN
    oldAction ← data.table[LOOPHOLE[key, CARDINAL]];
    data.table[LOOPHOLE[key, CARDINAL]] ← action;
    RETURN
    END;

  -- The Process (and initialization) part


  StartKeyHandler: PUBLIC PROCEDURE =
    BEGIN OPEN ProcessDefs;
    frame: ControlDefs.GlobalFrameHandle = FrameDefs.GlobalFrame[KeyDefs.Keyboard];
    KeyboardPriority: Priority = 6;
    KeyboardLevel: InterruptLevel = 7;
    KeyboardBit: WORD = InlineDefs.BITSHIFT[1, KeyboardLevel];
    save: Priority = GetPriority[];
    data ← Storage.Node[SIZE[KeyboardData]];
    InitKeyTable[];
    data.stream ← StreamObject[
      ClearInputBuffer, ReadChar, PutBackChar, WriteChar, InputBufferEmpty,
      DestroyKey, NIL, Keyboard[0, 0, ]];
    FrameDefs.MakeCodeResident[frame];
    IF ~frame.started THEN START KeyDefs.Keyboard;
    trackCursor ← TRUE;
    userAbort ← FALSE;
    halt ← FALSE;
    keys ← LOOPHOLE[KeyDefs.Keys];
    oldState ← @data.old;
    data.old ← keys↑;
    newState ← @data.new;
    KeyTable ← @data.table;
    ks ← @data.stream;
    mouse ← AltoDisplay.MouseXY;
    cursor ← AltoDisplay.CursorXY;
    SetPriority[KeyboardPriority];
    data.process ← FORK ProcessKeyboard;
    SetPriority[save];
    CV[KeyboardLevel] ← @wakeup;
    DIW↑ ← InlineDefs.BITOR[DIW↑, KeyboardBit];
    END;

  StopKeyHandler: PUBLIC PROCEDURE =
    BEGIN OPEN ProcessDefs;
    KeyboardLevel: InterruptLevel = 7;
    KeyboardBit: WORD = InlineDefs.BITSHIFT[1, KeyboardLevel];
    frame: ControlDefs.GlobalFrameHandle = FrameDefs.GlobalFrame[KeyDefs.Keyboard];
    halt ← TRUE;
    JOIN data.process;
    CV[KeyboardLevel] ← NIL;
    DIW↑ ← InlineDefs.BITAND[DIW↑, InlineDefs.BITNOT[KeyboardBit]];
    FrameDefs.UnlockCode[frame];
    Storage.Free[data];
    END;

  DestroyKeyHandler: PUBLIC PROCEDURE =
    BEGIN
    IF ~halt THEN StopKeyHandler[];
    FrameDefs.UnNew[FrameDefs.GlobalFrame[KeyDefs.Keyboard]];
    FrameDefs.SelfDestruct[];
    END;

  FrameDefs.UnlockCode[FrameDefs.GlobalFrame[KeyDefs.Keyboard]];

  END.