Browse Source

Console keyboard shift states in Unix

Margers 10 months ago
parent
commit
a0fe88b60f
1 changed files with 94 additions and 8 deletions
  1. 94 8
      packages/rtl-console/src/unix/keyboard.pp

+ 94 - 8
packages/rtl-console/src/unix/keyboard.pp

@@ -2012,6 +2012,88 @@ begin
   end;
 end;
 
+function RemoveShiftState(AShiftState:TEnhancedShiftState; toRemoveState,aState,toTestState:TEnhancedShiftStateElement):TEnhancedShiftState;
+{ remove state toRemoveState and                                  }
+{ remove state aState if AShiftState does not contain toTestState }
+begin
+  AShiftState:=AShiftState-[toRemoveState];
+  if not (toTestState in AShiftState) then
+    AShiftState:=AShiftState-[aState];
+  RemoveShiftState:=AShiftState;
+end;
+
+var LastShiftState, CurrentShiftState : TEnhancedShiftState;
+
+function GetLastShiftState : byte;
+{ get fake shift state or current shift state for kitty keys }
+var State : byte;
+begin
+  State:=0;
+  if isKittyKeys then
+  begin
+    LastShiftState:=CurrentShiftState;
+    if essLeftShift in LastShiftState then
+      inc(state,kbLeftShift);
+    if essRightShift in LastShiftState then
+      inc(state,kbRightShift);
+    if (essShift in LastShiftState) and (not ((essRightShift in LastShiftState) or (essLeftShift in LastShiftState))) then
+      inc(state,kbShift); {this for super rare case when shift state key press was not recived (maybe that is impossible)}
+  end else
+  if essShift in LastShiftState then
+    inc(state,kbShift);
+  if essCtrl in LastShiftState then
+    inc(state,kbCtrl);
+  if essAlt in LastShiftState then
+    inc(state,kbAlt);
+  GetLastShiftState:=State;
+end;
+
+procedure UpdateCurrentShiftState(nKey:longint; kbDown:byte);
+begin
+  {current shift state changes}
+  if kbDown <3 then
+  begin {state key down}
+    case nKey of
+      kShiftLeft  : CurrentShiftState:=CurrentShiftState +[essShift,essLeftShift];
+      kShiftRight : CurrentShiftState:=CurrentShiftState +[essShift,essRightShift];
+      kCtrlLeft   : CurrentShiftState:=CurrentShiftState +[essCtrl,essLeftCtrl];
+      kCtrlRight  : CurrentShiftState:=CurrentShiftState +[essCtrl,essRightCtrl];
+      kAltRight   : CurrentShiftState:=CurrentShiftState +[essAlt,essRightAlt];
+      kAltLeft    : CurrentShiftState:=CurrentShiftState +[essAlt,essLeftAlt];
+      kAltGr      : CurrentShiftState:=CurrentShiftState +[essAltGr];
+    end;
+  end else
+  begin {state key up}
+    case nKey of
+      kShiftLeft  : CurrentShiftState:=RemoveShiftState(CurrentShiftState,essLeftShift,essShift,essRightShift);
+      kShiftRight : CurrentShiftState:=RemoveShiftState(CurrentShiftState,essRightShift,essShift,essLeftShift);
+      kCtrlLeft   : CurrentShiftState:=RemoveShiftState(CurrentShiftState,essLeftCtrl,essCtrl,essRightCtrl);
+      kCtrlRight  : CurrentShiftState:=RemoveShiftState(CurrentShiftState,essRightCtrl,essCtrl,essLeftCtrl);
+      kAltRight   : CurrentShiftState:=RemoveShiftState(CurrentShiftState,essRightAlt,essAlt,essLeftAlt);
+      kAltLeft    : CurrentShiftState:=RemoveShiftState(CurrentShiftState,essLeftAlt,essAlt,essRightAlt);
+      kAltGr      : CurrentShiftState:=CurrentShiftState -[essAltGr];
+    end;
+  end;
+end;
+
+procedure UpdateShiftStateWithModifier(modifier:longint);
+{ Sanity double check. In case if there is no generated shift state key release (shortcut key intercepted by OS or terminal). }
+{ Make sure on key press there is correct current shift state }
+begin
+  { Shift states}
+  if modifier =  0 then modifier:=1;
+  modifier:=modifier-1;
+  if (modifier and 1)>0 then
+  begin if not (essShift in CurrentShiftState) then CurrentShiftState:=CurrentShiftState+[essShift];
+  end else if (essShift in CurrentShiftState) then CurrentShiftState:=CurrentShiftState-[essLeftShift,essShift,essRightShift];
+  if (modifier and 2)>0 then
+  begin  if not (essAlt in CurrentShiftState) then CurrentShiftState:=CurrentShiftState+[essAlt];
+  end else if (essAlt in CurrentShiftState) then CurrentShiftState:=CurrentShiftState-[essLeftAlt,essAlt,essRightAlt];
+  if (modifier and 4)>0 then
+  begin if not (essCtrl in CurrentShiftState) then CurrentShiftState:=CurrentShiftState+[essCtrl];
+  end else if (essCtrl in CurrentShiftState) then CurrentShiftState:=CurrentShiftState-[essRightCtrl,essCtrl,essLeftCtrl];
+end;
+
 
 function ReadKey:TEnhancedKeyEvent;
 const
@@ -2074,13 +2156,9 @@ var
          modifier:=((enh[3]-1)and 7)+1;
          kbDown:=enh[4];
 
-         unicodeCodePoint:=0;
-         if kbDown <3 then
-         begin
-           unicodeCodePoint:=enh[6];
-           if unicodeCodePoint < 0 then
-              unicodeCodePoint:=enh[0];
-         end;
+         unicodeCodePoint:=enh[6];
+         if unicodeCodePoint < 0 then
+            unicodeCodePoint:=enh[0];
          enh[5]:=modifier;
 
          escStr:='';
@@ -2139,6 +2217,7 @@ var
                NPT:=nil;
          end;
 
+         UpdateShiftStateWithModifier(modifier);
          if kbDown =3 then arrayind:=0; {release keys are ignored}
 
          if not assigned(NPT) and (ch='u') then
@@ -2148,6 +2227,8 @@ var
              {function keys have been pressed}
              arrayind:=0;
              nKey:=unicodeCodePoint-cKeysUnicodePrivateBase;
+             if (nKey >=kShiftLeft) and (nKey<=kAltGr) then
+               UpdateCurrentShiftState(nKey,kbDown);
              if (nKey < 128) and (kbDown <3) then
              begin
                if nKey = 60 then nKey:= kMiddle; {KP_5 -> KP_BEGIN}
@@ -2443,6 +2524,7 @@ end;
 procedure SysInitKeyboard;
 begin
   isKittyKeys:=false;
+  CurrentShiftState:=[];
   PendingEnhancedKeyEvent:=NilEnhancedKeyEvent;
   Utf8KeyboardInputEnabled:={$IFDEF FPC_DOTTEDUNITS}System.Console.{$ENDIF}UnixKVMBase.UTF8Enabled;
   SetRawMode(true);
@@ -2581,6 +2663,7 @@ begin {main}
   if PendingEnhancedKeyEvent<>NilEnhancedKeyEvent then
     begin
       SysGetEnhancedKeyEvent:=PendingEnhancedKeyEvent;
+      LastShiftState:=SysGetEnhancedKeyEvent.ShiftState; {to fake shift state later}
       PendingEnhancedKeyEvent:=NilEnhancedKeyEvent;
       exit;
     end;
@@ -2638,6 +2721,7 @@ begin {main}
             SysGetEnhancedKeyEvent.ShiftState:=SState;
             SysGetEnhancedKeyEvent.VirtualScanCode:=(MyScan shl 8) or Ord(MyChar);
           end;
+        LastShiftState:=SysGetEnhancedKeyEvent.ShiftState; {to fake shift state later}
         exit;
       end
     else if MyChar=#27 then
@@ -2733,6 +2817,7 @@ begin {main}
       SysGetEnhancedKeyEvent.ShiftState:=SState;
       SysGetEnhancedKeyEvent.VirtualScanCode:=(MyScan shl 8) or Ord(MyChar);
     end;
+  LastShiftState:=SysGetEnhancedKeyEvent.ShiftState; {to fake shift state later}
 end;
 
 
@@ -2750,6 +2835,7 @@ begin
     end
   else
     SysPollEnhancedKeyEvent:=NilEnhancedKeyEvent;
+  LastShiftState:=SysPollEnhancedKeyEvent.ShiftState; {to fake shift state later}
 end;
 
 
@@ -2760,7 +2846,7 @@ begin
     SysGetShiftState:=ShiftState
   else
 {$endif}
-    SysGetShiftState:=0;
+    SysGetShiftState:=GetLastShiftState;
 end;