mirror of
https://github.com/sharkdp/bat.git
synced 2024-12-15 02:51:30 +01:00
309 lines
8.8 KiB
Ada
309 lines
8.8 KiB
Ada
|
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;
|