namespace XFEEDToolBox.Plugin;
internal static unsafe partial class Navigation
{
private static bool TryBindPhysicalInputs(in Snapshot frame,out string? issue)
{
issue=null;
if(!frame.valid||frame.entity==0||frame.inputRoot==0){issue="current-main-ship";return false;}
if(!EnsureTakeoverKeyboard()){lock(takeoverKeyboardLock)issue=takeoverKeyboardIssue??"keyboard-observer-unavailable";return false;}
return true;
}
private static bool TryReadPhysicalInput(in Snapshot frame,ulong epoch,ulong now,out double magnitude,out string dominant)
{
magnitude=0;dominant="";
if(!frame.valid||frame.entity==0||frame.inputRoot==0)return false;
if(!ReadTakeoverKeyboard(frame,epoch,now,out dominant))return false;
magnitude=dominant.Length>0?1:0;return true;
}
private static void RequirePhysicalInputSources(in Snapshot frame,ulong now)
{
if(!TryReadPhysicalInput(frame,controllerEpoch,now,out var magnitude,out var source))
{
if(!TryBindPhysicalInputs(frame,out var issue)||!TryReadPhysicalInput(frame,controllerEpoch,GetTickCount64(),out magnitude,out source))
{
lock(takeoverKeyboardLock)issue??=takeoverKeyboardIssue;
throw new InvalidOperationException("Original physical input sources unavailable: "+(issue??"keyboard-observer-unavailable"));
}
}
if(magnitude>0){RecordInputTakeover(source,frame);overridden=true;Expire();throw new InvalidOperationException("Player keyboard takeover requested");}
}
private static JsonObject PhysicalInputDescription(bool prepare=false,bool includeNames=false)
{
var frame=Current();string? issue=null;
var prepared=!prepare||TryBindPhysicalInputs(frame,out issue);
var ready=prepared&&TryReadPhysicalInput(frame,controllerEpoch,GetTickCount64(),out _,out _);
double magnitude=0;string source="";
if(ready)TryReadPhysicalInput(frame,controllerEpoch,GetTickCount64(),out magnitude,out source);
lock(takeoverKeyboardLock)issue??=takeoverKeyboardIssue;
return Obj(("ready",ready),("source","physical-keyboard-W-S-over-1000ms-or-X-tap"),("policyVersion",2),
("issue",issue),("sourceState",ready?"verified-low-level-keyboard-observer":"unavailable"),
("entityId",frame.entity.ToString()),("inputRoot",frame.inputRoot.ToString("x")),
("magnitude",ready?(double?)magnitude:null),("dominantSource",source),
("holdMilliseconds",1000),("strictlyGreater",true),("immediateKey","X"),
("gameForegroundOnly",true),("injectedEventsIgnored",true),("mixedOutputAxesUsedForTakeover",false),
("mouseUsedForTakeover",false),("focusChangeUsedForTakeover",false));
}
private static bool PhysicalInputPolicySelfTest()
{
var policy=new XFEEDToolBox.Core.Navigation.ManualTakeoverKeyboardPolicy();
foreach(var key in new uint[]{0x57,0x53})
{policy.Reset();policy.Key(key,true,100,true);if(policy.Evaluate(1100,true)!=""||policy.Evaluate(1101,true)=="")return false;}
policy.Reset();policy.Key(0x58,true,10,true);policy.Key(0x58,false,11,true);if(policy.Evaluate(12,true)!="physical-key-X")return false;
policy.Reset();policy.Key(0x57,true,10,true);if(policy.Evaluate(500,true)!="")return false;policy.Key(0x57,false,501,true);if(policy.Evaluate(2000,true)!="")return false;
policy.Key(0x57,true,3000,true);policy.Evaluate(3500,false);if(policy.Evaluate(5000,true)!="")return false;
policy.Key(0x58,true,6000,true,true);return policy.Evaluate(6001,true)=="";
}
}
namespace XFEEDToolBox.Plugin;
internal static unsafe partial class Navigation
{
private static bool TryBindPhysicalInputs(in Snapshot frame,out string? issue)
{
issue=null;
if(!frame.valid||frame.entity==0||frame.inputRoot==0){issue="current-main-ship";return false;}
if(!EnsureTakeoverKeyboard()){lock(takeoverKeyboardLock)issue=takeoverKeyboardIssue??"keyboard-observer-unavailable";return false;}
return true;
}
private static bool TryReadPhysicalInput(in Snapshot frame,ulong epoch,ulong now,out double magnitude,out string dominant)
{
magnitude=0;dominant="";
if(!frame.valid||frame.entity==0||frame.inputRoot==0)return false;
if(!ReadTakeoverKeyboard(frame,epoch,now,out dominant))return false;
magnitude=dominant.Length>0?1:0;return true;
}
private static void RequirePhysicalInputSources(in Snapshot frame,ulong now)
{
if(!TryReadPhysicalInput(frame,controllerEpoch,now,out var magnitude,out var source))
{
if(!TryBindPhysicalInputs(frame,out var issue)||!TryReadPhysicalInput(frame,controllerEpoch,GetTickCount64(),out magnitude,out source))
{
lock(takeoverKeyboardLock)issue??=takeoverKeyboardIssue;
throw new InvalidOperationException("Original physical input sources unavailable: "+(issue??"keyboard-observer-unavailable"));
}
}
if(magnitude>0){RecordInputTakeover(source,frame);overridden=true;Expire();throw new InvalidOperationException("Player keyboard takeover requested");}
}
private static JsonObject PhysicalInputDescription(bool prepare=false,bool includeNames=false)
{
var frame=Current();string? issue=null;
var prepared=!prepare||TryBindPhysicalInputs(frame,out issue);
var ready=prepared&&TryReadPhysicalInput(frame,controllerEpoch,GetTickCount64(),out _,out _);
double magnitude=0;string source="";
if(ready)TryReadPhysicalInput(frame,controllerEpoch,GetTickCount64(),out magnitude,out source);
lock(takeoverKeyboardLock)issue??=takeoverKeyboardIssue;
return Obj(("ready",ready),("source","physical-keyboard-W-S-over-1000ms-or-X-tap"),("policyVersion",2),
("issue",issue),("sourceState",ready?"verified-low-level-keyboard-observer":"unavailable"),
("entityId",frame.entity.ToString()),("inputRoot",frame.inputRoot.ToString("x")),
("magnitude",ready?(double?)magnitude:null),("dominantSource",source),
("holdMilliseconds",1000),("strictlyGreater",true),("immediateKey","X"),
("gameForegroundOnly",true),("injectedEventsIgnored",true),("mixedOutputAxesUsedForTakeover",false),
("mouseUsedForTakeover",false),("focusChangeUsedForTakeover",false));
}
private static bool PhysicalInputPolicySelfTest()
{
var policy=new XFEEDToolBox.Core.Navigation.ManualTakeoverKeyboardPolicy();
foreach(var key in new uint[]{0x57,0x53})
{policy.Reset();policy.Key(key,true,100,true);if(policy.Evaluate(1100,true)!=""||policy.Evaluate(1101,true)=="")return false;}
policy.Reset();policy.Key(0x58,true,10,true);policy.Key(0x58,false,11,true);if(policy.Evaluate(12,true)!="physical-key-X")return false;
policy.Reset();policy.Key(0x57,true,10,true);if(policy.Evaluate(500,true)!="")return false;policy.Key(0x57,false,501,true);if(policy.Evaluate(2000,true)!="")return false;
policy.Key(0x57,true,3000,true);policy.Evaluate(3500,false);if(policy.Evaluate(5000,true)!="")return false;
policy.Key(0x58,true,6000,true,true);return policy.Evaluate(6001,true)=="";
}
}