Skip to content

Commit

Permalink
add One Shot checkbox and ability to send the actions keys in order a…
Browse files Browse the repository at this point in the history
…ll at once on button press and do nothing on button release
  • Loading branch information
joekolodz committed Jan 5, 2022
1 parent ee8f165 commit cc40cca
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 12 deletions.
1 change: 1 addition & 0 deletions src/Controls/MacroTransport.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,6 @@
Margin="0,2,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"/>
<CheckBox Margin="0,2,0,0" IsChecked="{Binding IsOneShot}">One Shot</CheckBox>
</StackPanel>
</UserControl>
1 change: 1 addition & 0 deletions src/Models/HOTASButton.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public enum ButtonType
public ButtonType Type { get; set; }
public int ShiftModePage { get; set; }
public bool IsShift { get; set; }
public bool IsOneShot { get; set; }

[JsonIgnore]
public string ActionName
Expand Down
43 changes: 41 additions & 2 deletions src/Models/HOTASQueue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ private void DequeueLoop()
}
}
}

Logging.Log.Debug("Dequeue loop stopped");
}

Expand Down Expand Up @@ -238,7 +238,7 @@ private void HandleStandardButton(int offset, int value)
else
{
if (_modeProfileActivationButtons.ContainsKey(_mode) &&
_modeProfileActivationButtons[_mode].InheritFromMode>0)
_modeProfileActivationButtons[_mode].InheritFromMode > 0)
{
map = GetMapFromParentMode(_modeProfileActivationButtons[_mode].InheritFromMode, offset) as HOTASButton;
if (map != null)
Expand All @@ -261,6 +261,12 @@ private void HandleButtonPressed(HOTASButton button, int offset)
{
if (button == null) return;

if (button.IsOneShot)
{
HandleOneShot(button, offset);
return;
}

if (button.IsMacro)
{
HandleMacro(button, offset);
Expand Down Expand Up @@ -329,6 +335,39 @@ private async Task PlayMacroOnce(int offset, ObservableCollection<ButtonAction>
_activeMacros.TryRemove(offset, out _);
}

private void HandleOneShot(HOTASButton button, int offset)
{
Task.Run(() => PlayOneShot(offset, button.ActionCatalogItem.Actions));
}

private async Task PlayOneShot(int offset, ObservableCollection<ButtonAction> actions)
{
foreach (var action in actions)
{
if (action.TimeInMilliseconds > 0)
{
//yes this is precise only to the nearest KeyDownRepeatDelay milliseconds. repeated keys are on a 60 millisecond boundary, so the UI could be locked to 60ms increments only
var timeLeft = action.TimeInMilliseconds;
while (timeLeft > 0)
{
await Task.Delay(Keyboard.KeyDownRepeatDelay);
timeLeft -= Keyboard.KeyDownRepeatDelay;
}
}

Keyboard.SendKeyPress(action.ScanCode, action.IsKeyUp, action.IsExtended);

if (action.IsKeyUp)
{
KeystrokeUpSent?.Invoke(this, new KeystrokeSentEventArgs(offset, offset, action.ScanCode, action.IsKeyUp, action.IsExtended));
}
else
{
KeystrokeDownSent?.Invoke(this, new KeystrokeSentEventArgs(offset, offset, action.ScanCode, action.IsKeyUp, action.IsExtended));
}
}
}

private void HandleButtonReleased(HOTASButton button, int offset)
{
if (button == null) return;
Expand Down
6 changes: 6 additions & 0 deletions src/ViewModels/ButtonMapViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ public HOTASButton.ButtonType Type
set => _hotasButton.Type = value;
}

public bool IsOneShot
{
get => _hotasButton.IsOneShot;
set => _hotasButton.IsOneShot = value;
}

public string ActionName
{
get => ActionItem.ActionName;
Expand Down
102 changes: 92 additions & 10 deletions tests/HOTASQueueTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,18 +254,18 @@ public void macro_start()
var mreMacro = new ManualResetEventSlim();
var mreKeyDown = new ManualResetEventSlim();
var mreKeyUp = new ManualResetEventSlim();

queue.MacroStarted += (sender, e) => { isMacroStartedEventCalled = true; mreMacro.Set(); };
queue.KeystrokeDownSent += (sender, e) => { isKeyDownEventCalled = true; mreKeyDown.Set(); };
queue.KeystrokeUpSent += (sender, e) => { isKeyUpEventCalled = true; mreKeyUp.Set(); };

Assert.False(isMacroStartedEventCalled);
joystick.TestData[0] = new JoystickUpdate() { RawOffset = (int)JoystickOffset.Button1, Sequence = 0, Timestamp = 0, Value = (int)JoystickOffsetValues.ButtonState.ButtonPressed };

mreMacro.Wait(5000);
mreKeyDown.Wait(5000);
mreKeyUp.Wait(5000);

Assert.True(isMacroStartedEventCalled);
Assert.True(isKeyDownEventCalled);
Assert.True(isKeyUpEventCalled);
Expand Down Expand Up @@ -349,21 +349,21 @@ public void button_pressed_use_inherited_parent()
var actualScanCode = 0;

var joystick = new TestJoystick_BasicQueue(dataBufferSize: 1);

var activationList = new Dictionary<int, ModeActivationItem>();
activationList.Add(1, new ModeActivationItem(){Mode = 1});
activationList.Add(2, new ModeActivationItem(){Mode = 2, InheritFromMode = 1});
activationList.Add(1, new ModeActivationItem() { Mode = 1 });
activationList.Add(2, new ModeActivationItem() { Mode = 2, InheritFromMode = 1 });

var mode1map = CreateTestHotasTestMapWithButton();
mode1map.Add(new HOTASButton(){MapId = (int)JoystickOffset.Button2, ActionCatalogItem = new ActionCatalogItem(){Actions = new ObservableCollection<ButtonAction>(){new ButtonAction(){ScanCode = 1}, new ButtonAction(){ScanCode = 1, IsKeyUp = true}}}});
mode1map.Add(new HOTASButton() { MapId = (int)JoystickOffset.Button2, ActionCatalogItem = new ActionCatalogItem() { Actions = new ObservableCollection<ButtonAction>() { new ButtonAction() { ScanCode = 1 }, new ButtonAction() { ScanCode = 1, IsKeyUp = true } } } });
var mode2map = CreateTestHotasTestMapWithButton();
var modeProfiles = new Dictionary<int, ObservableCollection<IHotasBaseMap>>();
modeProfiles.Add(1, mode1map);
modeProfiles.Add(2, mode2map);

var queue = new HOTASQueue(Substitute.For<IKeyboard>());
queue.Listen(joystick, modeProfiles, activationList);

//inherited key should be active
queue.SetMode(2);

Expand All @@ -377,7 +377,7 @@ public void button_pressed_use_inherited_parent()
};

Assert.False(isEventCalled);

joystick.TestData[0] = new JoystickUpdate() { RawOffset = (int)JoystickOffset.Button2, Sequence = 0, Timestamp = 0, Value = (int)JoystickOffsetValues.ButtonState.ButtonPressed };
mre.Wait(5000);

Expand All @@ -404,7 +404,7 @@ public void button_pressed_use_inherited_parent()
joystick.TestData[0] = new JoystickUpdate() { RawOffset = (int)JoystickOffset.Button1, Sequence = 0, Timestamp = 0, Value = (int)JoystickOffsetValues.ButtonState.ButtonReleased };
mre.Reset();
mre.Wait(5000);

joystick.TestData[0] = new JoystickUpdate() { RawOffset = (int)JoystickOffset.Button2, Sequence = 0, Timestamp = 0, Value = (int)JoystickOffsetValues.ButtonState.ButtonReleased };
mre.Reset();
mre.Wait(5000);
Expand Down Expand Up @@ -731,5 +731,87 @@ private static ObservableCollection<IHotasBaseMap> CreateTestHotasTestMapWithPOV
};
return map;
}

[Fact]
public void test_one_shot()
{
var isFirstKeyDownPressed = false;
var isFirstKeyUpPressed = false;

var isSecondKeyOutOfOrder = false;


var joystick = new TestJoystick_BasicQueue(dataBufferSize: 1);
var map = CreateTestHotasTestMapWithButton(timeInMilliseconds: 1);
((HOTASButton)map[0]).IsOneShot = true;

var subKeyboard = Substitute.For<IKeyboard>();
subKeyboard.KeyDownRepeatDelay.Returns(35);

var activationList = new Dictionary<int, ModeActivationItem>();
var modeProfiles = new Dictionary<int, ObservableCollection<IHotasBaseMap>>();
modeProfiles.Add(1, map);

var queue = new HOTASQueue(subKeyboard);
queue.Listen(joystick, modeProfiles, activationList);

var mreKeyDown = new ManualResetEventSlim();
var mreKeyUp = new ManualResetEventSlim();

queue.KeystrokeDownSent += (sender, e) =>
{
if (e.ScanCode == 28 && isFirstKeyDownPressed == false) isFirstKeyDownPressed = true;

if (e.ScanCode == 29 && isFirstKeyDownPressed == false)
{
isSecondKeyOutOfOrder = true;
mreKeyDown.Set();
}
};

queue.KeystrokeUpSent += (sender, e) =>
{
if (e.ScanCode == 28 && e.IsKeyUp && isFirstKeyDownPressed) isFirstKeyUpPressed = true;

if (e.ScanCode == 29 && isFirstKeyUpPressed == false)
{
isSecondKeyOutOfOrder = true;
mreKeyUp.Set();
}
};

joystick.TestData[0] = new JoystickUpdate() { RawOffset = (int)JoystickOffset.Button1, Sequence = 0, Timestamp = 0, Value = (int)JoystickOffsetValues.ButtonState.ButtonPressed };

mreKeyDown.Wait(5000);
mreKeyUp.Wait(5000);

Assert.False(isSecondKeyOutOfOrder);
Assert.True(isFirstKeyDownPressed);
Assert.True(isFirstKeyUpPressed);

subKeyboard.Received().SendKeyPress(Arg.Any<int>(), Arg.Any<bool>(), Arg.Any<bool>());
}

private static ObservableCollection<IHotasBaseMap> CreateTestHotasTestMapWithButtonForOneShot()
{
var map = new ObservableCollection<IHotasBaseMap>()
{
new HOTASButton()
{
MapId = (int) JoystickOffset.Button1, MapName = "trigger", Type = HOTASButton.ButtonType.Button,
ActionName = "fire", ActionCatalogItem = new ActionCatalogItem()
{
ActionName = "fire", Actions = new ObservableCollection<ButtonAction>()
{
new ButtonAction() {ScanCode = 28},
new ButtonAction() {ScanCode = 28, IsKeyUp = true},
new ButtonAction() {ScanCode = 29},
new ButtonAction() {ScanCode = 29, IsKeyUp = true}
}
}
}
};
return map;
}
}
}

0 comments on commit cc40cca

Please sign in to comment.