with Chests.Ring_Buffers; with USB.Device.HID.Keyboard; package body Click is  ----------------  -- DEBOUNCE --  ----------------  -- Ideally, in a separate package.  -- should be [], but not fixed yet in GCC 11.  Current_Status : Key_Matrix := [others => [others => False]];  New_Status : Key_Matrix := [others => [others => False]];  Since : Natural := 0;  -- Nb_Bounce : Natural := 5;  function Update (NewS : Key_Matrix) return Boolean is  begin  -- The new state is the same as the current stable state => Do nothing.  if Current_Status = NewS then  Since := 0;  return False;  end if;  if New_Status /= NewS then  -- The new state differs from the previous  -- new state (bouncing) => reset  New_Status := NewS;  Since := 1;  else  -- The new state hasn't changed since last  -- update => towards stabilization.  Since := Since + 1;  end if;  if Since > Nb_Bounce then  declare  Tmp : constant Key_Matrix := Current_Status;  begin  -- New state has been stable enough.  -- Latch it and notifies caller.  Current_Status := New_Status;  New_Status := Tmp;  Since := 0;  end;  return True;  else  -- Not there yet  return False;  end if;  end Update;  procedure Get_Matrix;  -- Could use := []; but GNAT 12 has a bug (fixed in upcoming 13)  Read_Status : Key_Matrix := [others => [others => False]];  function Get_Events return Events is  Num_Evt : Natural := 0;  New_S : Key_Matrix renames Read_Status;  begin  Get_Matrix;  if Update (New_S) then  for I in Current_Status'Range (1) loop  for J in Current_Status'Range (2) loop  if (not New_Status (I, J) and then Current_Status (I, J))  or else (New_Status (I, J) and then not Current_Status (I, J))  then  Num_Evt := Num_Evt + 1;  end if;  end loop;  end loop;  declare  Evts : Events (Natural range 1 .. Num_Evt);  Cursor : Natural range 1 .. Num_Evt + 1 := 1;  begin  for I in Current_Status'Range (1) loop  for J in Current_Status'Range (2) loop  if not New_Status (I, J)  and then Current_Status (I, J)  then  -- Pressing I, J  Evts (Cursor) := [  Evt => Press,  Col => I,  Row => J  ];  Cursor := Cursor + 1;  elsif New_Status (I, J)  and then not Current_Status (I, J)  then  -- Release I, J  Evts (Cursor) := [  Evt => Release,  Col => I,  Row => J  ];  Cursor := Cursor + 1;  end if;  end loop;  end loop;  return Evts;  end;  end if;  return [];  end Get_Events;  procedure Get_Matrix is -- return Key_Matrix is  begin  for Row in Keys.Rows'Range loop  Keys.Rows (Row).Clear;  for Col in Keys.Cols'Range loop  Read_Status (Col, Row) := not Keys.Cols (Col).Set;  end loop;  Keys.Rows (Row).Set;  end loop;  end Get_Matrix;  -- End of DEBOUNCE  --------------  -- Layout --  --------------  package Events_Ring_Buffers is new Chests.Ring_Buffers  (Element_Type => Event,  Capacity => 16);  Queued_Events : Events_Ring_Buffers.Ring_Buffer;  type Statet is (Normal_Key, Layer_Mod, None);  type State is record  Typ : Statet;  Code : Key_Code_T;  Layer_Value : Natural;  -- Col : ColR;  -- Row : RowR;  end record;  type State_Array is array (ColR, RowR) of State;  States : State_Array := [others => [others => (Typ => None, Code => No, Layer_Value => 0)]];  function Kw (Code : Key_Code_T) return Action is  begin  return (T => Key, C => Code, L => 0);  end Kw;  function Lw (V : Natural) return Action is  begin  return (T => Layer, C => No, L => V);  end Lw;  -- FIXME: hardcoded max number of events  subtype Events_Range is Natural range 0 .. 60;  type Array_Of_Reg_Events is array (Events_Range) of Event;  Stamp : Natural := 0;  procedure Register_Events (L : Layout; Es : Events) is  begin  Stamp := Stamp + 1;  Log ("Reg events: " & Stamp'Image);  Log (Es'Length'Image);  for E of Es loop  declare  begin  if Events_Ring_Buffers.Is_Full (Queued_Events) then  raise Program_Error;  end if;  Events_Ring_Buffers.Append (Queued_Events, E);  end;  -- Log ("Reg'ed events:" & Events_Mark'Image);  Log ("Reg'ed events:" & Events_Ring_Buffers.Length (Queued_Events)'Image);  end loop;  end Register_Events;  procedure Release (Col: Colr; Row: Rowr) is  begin  if States (Col, Row).Typ = None then  raise Program_Error;  end if;  States (Col, Row) := (Typ => None, Code => No, Layer_Value => 0);  end Release;  function Get_Current_Layer return Natural is  L : Natural := 0;  begin  for S of States loop  if S.Typ = Layer_Mod then  L := L + S.Layer_Value;  end if;  end loop;  return L;  end Get_Current_Layer;  -- Tick the event.  -- Returns TRUE if it needs to stay in the queued events  -- FALSE if the event has been consumed.  function Tick (L: Layout; E : in out Event) return Boolean is  Current_Layer : Natural := Get_Current_Layer;  A : Action renames L (Current_Layer, E.Row, E.Col);  begin  case E.Evt is  when Press =>  case A.T is  when Key =>  States (E.Col, E.Row) :=  (Typ => Normal_Key,  Code => A.C,  Layer_Value => 0);  when Layer =>  States (E.Col, E.Row) := (Typ => Layer_Mod, Layer_Value => A.L, Code => No);  when others =>  raise Program_Error;  end case;  when Release =>  Release (E.Col, E.Row);  end case;  return False;  end Tick;  Last_Was_Empty_Log : Boolean := False;  procedure Tick (L : Layout) is  begin  for I in 1 .. Events_Ring_Buffers.Length(Queued_Events) loop  declare  E : Event := Events_Ring_Buffers.Last_Element (Queued_Events);  begin  Events_Ring_Buffers.Delete_Last (Queued_Events);  if Tick (L, E) then  Events_Ring_Buffers.Prepend (Queued_Events, E);  end if;  end;  end loop;  if not Last_Was_Empty_Log or else Events_Ring_Buffers.Length(Queued_Events) /= 0 then  Log ("End Tick layout, events: " & Events_Ring_Buffers.Length(Queued_Events)'Image);  Last_Was_Empty_Log := Events_Ring_Buffers.Length(Queued_Events) = 0;  end if;  end Tick;  function Get_Key_Codes return Key_Codes_T is  Codes : Key_Codes_T (0 .. 10);  Wm: Natural := 0;  begin  for S of States loop  if S.Typ = Normal_Key and then  (S.Code < LCtrl or else S.Code > RGui)  then  Codes (Wm) := S.Code;  Wm := Wm + 1;  end if;  end loop;  if Wm = 0 then  return [];  else  return Codes (0 .. Wm - 1);  end if;  end Get_Key_Codes;  function Get_Modifiers return Key_Modifiers is  use USB.Device.HID.Keyboard;  KM : Key_Modifiers (1..8);  I : Natural := 0;  begin  for S of States loop  if S.Typ = Normal_Key then  I := I + 1;  case S.Code is  when LCtrl =>  KM(I) := Ctrl_Left;  when RCtrl =>  KM(I) := Ctrl_Right;  when LShift =>  KM(I) := Shift_Left;  when RShift =>  KM(I) := Shift_Right;  when LAlt =>  KM(I) := Alt_Left;  when RAlt =>  KM(I) := Alt_Right;  when LGui =>  KM(I) := Meta_Left;  when RGui =>  KM(I) := Meta_Right;  when others =>  I := I - 1;  end case;  end if;  end loop;  return KM (1..I);  end Get_Modifiers;  procedure Init is  begin  Events_Ring_Buffers.Clear (Queued_Events);  end Init; end Click;