diff --git a/src/Controls/MacroTransport.xaml b/src/Controls/MacroTransport.xaml
index 31e66ef..a644406 100644
--- a/src/Controls/MacroTransport.xaml
+++ b/src/Controls/MacroTransport.xaml
@@ -65,6 +65,11 @@
Margin="0,2,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"/>
- One Shot
+
+
+ One Shot
+
+
+
diff --git a/src/Models/HOTASButton.cs b/src/Models/HOTASButton.cs
index 2af2ee5..4ceb449 100644
--- a/src/Models/HOTASButton.cs
+++ b/src/Models/HOTASButton.cs
@@ -21,6 +21,7 @@ public enum ButtonType
public int ShiftModePage { get; set; }
public bool IsShift { get; set; }
public bool IsOneShot { get; set; }
+ public int RepeatCount{ get; set; }
public Guid ActionId
{
diff --git a/src/Models/HOTASCollection.cs b/src/Models/HOTASCollection.cs
index 05c847c..ae0dfbf 100644
--- a/src/Models/HOTASCollection.cs
+++ b/src/Models/HOTASCollection.cs
@@ -25,6 +25,8 @@ public class HOTASCollection : IHOTASCollection
public virtual event EventHandler KeystrokeUpSent;
public virtual event EventHandler MacroStarted;
public virtual event EventHandler MacroCancelled;
+ public virtual event EventHandler RepeatStarted;
+ public virtual event EventHandler RepeatCancelled;
public virtual event EventHandler ButtonPressed;
public virtual event EventHandler AxisChanged;
public virtual event EventHandler ModeChanged;
@@ -120,15 +122,7 @@ private void StopDevice(IHOTASDevice device)
{
if (device == null) return;
- device.ButtonPressed -= Device_ButtonPressed;
- device.AxisChanged -= Device_AxisChanged;
- device.KeystrokeDownSent -= Device_KeystrokeDownSent;
- device.KeystrokeUpSent -= Device_KeystrokeUpSent;
- device.MacroStarted -= Device_MacroStarted;
- device.MacroCancelled -= Device_MacroCancelled;
- device.ModeSelected -= device_modeSelected;
- device.ShiftReleased -= Device_ShiftReleased;
- device.LostConnectionToDevice -= Device_LostConnectionToDevice;
+ RemoveHandlers(device);
device.Stop();
}
@@ -163,12 +157,16 @@ public void ListenToAllDevices()
public void ListenToDevice(IHOTASDevice device)
{
+ RemoveHandlers(device);
+
device.ButtonPressed += Device_ButtonPressed;
device.AxisChanged += Device_AxisChanged;
device.KeystrokeDownSent += Device_KeystrokeDownSent;
device.KeystrokeUpSent += Device_KeystrokeUpSent;
device.MacroStarted += Device_MacroStarted;
device.MacroCancelled += Device_MacroCancelled;
+ device.RepeatStarted += Device_RepeatStarted;
+ device.RepeatCancelled += Device_RepeatCancelled;
device.ModeSelected += device_modeSelected;
device.ShiftReleased += Device_ShiftReleased;
device.LostConnectionToDevice += Device_LostConnectionToDevice;
@@ -177,6 +175,21 @@ public void ListenToDevice(IHOTASDevice device)
device.ListenAsync();
}
+ private void RemoveHandlers(IHOTASDevice device)
+ {
+ device.ButtonPressed -= Device_ButtonPressed;
+ device.AxisChanged -= Device_AxisChanged;
+ device.KeystrokeDownSent -= Device_KeystrokeDownSent;
+ device.KeystrokeUpSent -= Device_KeystrokeUpSent;
+ device.MacroStarted -= Device_MacroStarted;
+ device.MacroCancelled -= Device_MacroCancelled;
+ device.RepeatStarted -= Device_RepeatStarted;
+ device.RepeatCancelled -= Device_RepeatCancelled;
+ device.ModeSelected -= device_modeSelected;
+ device.ShiftReleased -= Device_ShiftReleased;
+ device.LostConnectionToDevice -= Device_LostConnectionToDevice;
+ }
+
private void Device_LostConnectionToDevice(object sender, LostConnectionToDeviceEventArgs e)
{
LostConnectionToDevice?.Invoke(sender, e);
@@ -252,6 +265,15 @@ private void Device_MacroCancelled(object sender, MacroCancelledEventArgs e)
{
MacroCancelled?.Invoke(sender, e);
}
+ private void Device_RepeatStarted(object sender, RepeatStartedEventArgs e)
+ {
+ Logging.Log.Debug("HOTASCollection - repeat started event");
+ RepeatStarted?.Invoke(sender, e);
+ }
+ private void Device_RepeatCancelled(object sender, RepeatCancelledEventArgs e)
+ {
+ RepeatCancelled?.Invoke(sender, e);
+ }
public void ForceButtonPress(IHOTASDevice device, JoystickOffset offset, bool isDown)
{
diff --git a/src/Models/HOTASDevice.cs b/src/Models/HOTASDevice.cs
index 17bf75d..4d6377d 100644
--- a/src/Models/HOTASDevice.cs
+++ b/src/Models/HOTASDevice.cs
@@ -17,6 +17,8 @@ public class HOTASDevice : IHOTASDevice
public event EventHandler KeystrokeUpSent;
public event EventHandler MacroStarted;
public event EventHandler MacroCancelled;
+ public event EventHandler RepeatStarted;
+ public event EventHandler RepeatCancelled;
public event EventHandler ButtonPressed;
public event EventHandler ModeSelected;
public event EventHandler ShiftReleased;
@@ -228,6 +230,8 @@ private void AddQueueHandlers()
_hotasQueue.KeystrokeUpSent += OnKeystrokeUpSent;
_hotasQueue.MacroStarted += OnMacroStarted;
_hotasQueue.MacroCancelled += OnMacroCancelled;
+ _hotasQueue.RepeatStarted += OnRepeatStarted;
+ _hotasQueue.RepeatCancelled += OnRepeatCancelled;
_hotasQueue.ButtonPressed += OnButtonPress;
_hotasQueue.AxisChanged += OnAxisChanged;
_hotasQueue.ModeSelected += onModeSelected;
@@ -241,6 +245,8 @@ private void RemoveQueueHandlers()
_hotasQueue.KeystrokeUpSent -= OnKeystrokeUpSent;
_hotasQueue.MacroStarted -= OnMacroStarted;
_hotasQueue.MacroCancelled -= OnMacroCancelled;
+ _hotasQueue.RepeatStarted -= OnRepeatStarted;
+ _hotasQueue.RepeatCancelled -= OnRepeatCancelled;
_hotasQueue.ButtonPressed -= OnButtonPress;
_hotasQueue.AxisChanged -= OnAxisChanged;
_hotasQueue.ModeSelected -= onModeSelected;
@@ -473,6 +479,16 @@ private void OnMacroCancelled(object sender, MacroCancelledEventArgs e)
MacroCancelled?.Invoke(sender, e);
}
+ private void OnRepeatStarted(object sender, RepeatStartedEventArgs e)
+ {
+ Logging.Log.Debug("HOTASDevice - repeat started event");
+ RepeatStarted?.Invoke(sender, e);
+ }
+ private void OnRepeatCancelled(object sender, RepeatCancelledEventArgs e)
+ {
+ RepeatCancelled?.Invoke(sender, e);
+ }
+
private void OnButtonPress(object sender, ButtonPressedEventArgs e)
{
ButtonPressed?.Invoke(this, new ButtonPressedEventArgs() { ButtonId = e.ButtonId, Device = this });
diff --git a/src/Models/HOTASQueue.cs b/src/Models/HOTASQueue.cs
index 1240c09..e2c3111 100644
--- a/src/Models/HOTASQueue.cs
+++ b/src/Models/HOTASQueue.cs
@@ -18,6 +18,8 @@ public class HOTASQueue : IHOTASQueue
public event EventHandler KeystrokeDownSent;
public event EventHandler MacroStarted;
public event EventHandler MacroCancelled;
+ public event EventHandler RepeatStarted;
+ public event EventHandler RepeatCancelled;
public event EventHandler ButtonPressed;
public event EventHandler ButtonReleased;
public event EventHandler AxisChanged;
@@ -82,7 +84,7 @@ public static int TranslatePointOfViewOffset(JoystickOffset offset, int value)
return translatedOffset;
}
- private void ListenLoop()
+ private async Task ListenLoop()
{
while (!_isStopRequested)
{
@@ -110,7 +112,7 @@ private void ListenLoop()
if (offset >= JoystickOffset.Button1 && offset <= JoystickOffset.Button128)
{
Logging.Log.Debug($"Offset:{offset}({state.RawOffset}), Seq:{state.Sequence}, Value:{state.Value}");
- HandleStandardButton((int)offset, state.Value);
+ await HandleStandardButton((int)offset, state.Value);
continue;
}
@@ -120,7 +122,7 @@ private void ListenLoop()
offset == JoystickOffset.POV4)
{
Logging.Log.Debug($"Offset:{offset}({state.RawOffset}), POV:{TranslatePointOfViewOffset(offset, state.Value)}, Seq:{state.Sequence}, Value:{state.Value}");
- HandlePovButton((JoystickOffset)state.Offset, state.Value);
+ await HandlePovButton((JoystickOffset)state.Offset, state.Value);
continue;
}
@@ -141,7 +143,7 @@ private void ListenLoop()
}
if (_jitterDetectionDictionary[state.RawOffset].IsJitter(state.Value)) continue;
- HandleAxis(state);
+ await HandleAxis(state);
OnAxisChanged(state);
continue;
}
@@ -196,7 +198,7 @@ private bool IsButtonDown(int value)
}
private static readonly ConcurrentDictionary _lastPovButton = new ConcurrentDictionary();
- private void HandlePovButton(JoystickOffset offset, int value)
+ private async Task HandlePovButton(JoystickOffset offset, int value)
{
if (_lastPovButton.ContainsKey(offset) || value == (int)JoystickOffsetValues.PointOfViewPositionValues.Released)
{
@@ -218,14 +220,15 @@ private void HandlePovButton(JoystickOffset offset, int value)
if (!(GetMap(translatedOffset) is HOTASButton map)) return;
- HandleButtonPressed(map, translatedOffset);
+ await HandleButtonPressed(map, translatedOffset);
OnButtonPress(translatedOffset);
}
}
+ private static readonly ConcurrentDictionary _activeRepeat = new ConcurrentDictionary();
private static readonly ConcurrentDictionary _activeMacros = new ConcurrentDictionary();
private static readonly ConcurrentDictionary _activeButtons = new ConcurrentDictionary();
- private void HandleStandardButton(int offset, int value)
+ private async Task HandleStandardButton(int offset, int value)
{
var map = GetMap(offset) as HOTASButton;
if (IsButtonDown(value))
@@ -233,7 +236,7 @@ private void HandleStandardButton(int offset, int value)
if (map != null && (map.ActionCatalogItem.Actions.Count > 0 || map.ShiftModePage > 0))
{
//if action list has a timer in it, then it is a macro and executes on another thread independently. does not interrupt other buttons
- HandleButtonPressed(map, offset);
+ await HandleButtonPressed(map, offset);
}
else
{
@@ -243,7 +246,7 @@ private void HandleStandardButton(int offset, int value)
map = GetMapFromParentMode(_modeActivationButtons[_mode].InheritFromMode, offset) as HOTASButton;
if (map != null)
{
- HandleButtonPressed(map, offset);
+ await HandleButtonPressed(map, offset);
}
}
}
@@ -257,10 +260,17 @@ private void HandleStandardButton(int offset, int value)
}
}
- private void HandleButtonPressed(HOTASButton button, int offset)
+ private async Task HandleButtonPressed(HOTASButton button, int offset)
{
if (button == null) return;
+ //macros and oneshots can be repeated so check repeats first
+ if (button.RepeatCount != 0)
+ {
+ HandleRepeat(button, offset);
+ return;
+ }
+
if (button.IsOneShot)
{
HandleOneShot(button, offset);
@@ -282,6 +292,80 @@ private void HandleButtonPressed(HOTASButton button, int offset)
_actionJobs.Add(new ActionJobItem() { Offset = offset, MapId = button.MapId, Actions = button.ActionCatalogItem.Actions });
}
+ private void HandleRepeat(HOTASButton button, int offset)
+ {
+ if (_activeRepeat.TryGetValue(offset, out _))
+ {
+ //cancel a repeat already in progress
+ _activeRepeat.TryRemove(offset, out _);
+ RepeatCancelled?.Invoke(this, new RepeatCancelledEventArgs(offset, (int)Win32Structures.ScanCodeShort.REPEAT_CANCELLED));
+ return;
+ }
+
+ _activeRepeat.TryAdd(offset, true);
+
+ Task.Run(() => PlayRepeat(button, offset));
+ }
+
+ private async Task PlayRepeat(HOTASButton button, int offset)
+ {
+ Logging.Log.Debug("HOTASQueue - repeat started event");
+ RepeatStarted?.Invoke(this, new RepeatStartedEventArgs(offset, (int)Win32Structures.ScanCodeShort.REPEAT_STARTED));
+
+ var repeatedButton = new HOTASButton()
+ {
+ RepeatCount = 0,
+ ActionCatalogItem = button.ActionCatalogItem,
+ ActionId = button.ActionId,
+ ActionName = button.ActionName,
+ IsOneShot = button.IsOneShot,
+ IsShift = button.IsShift,
+ MapId = button.MapId,
+ MapName = button.MapName,
+ ShiftModePage = button.ShiftModePage,
+ Type = button.Type
+ };
+
+
+ var repeatsLeft = button.RepeatCount;
+
+ if (repeatsLeft == -1)
+ {
+ while (true)
+ {
+ if (await BaseRepeat(button, offset, repeatedButton)) break;
+ }
+ }
+ else
+ {
+ while (repeatsLeft-- > 0)
+ {
+ if (await BaseRepeat(button, offset, repeatedButton)) break;
+ }
+ }
+
+ _activeRepeat.TryRemove(offset, out _);
+ }
+
+ private async Task BaseRepeat(HOTASButton button, int offset, HOTASButton repeatedButton)
+ {
+ await HandleButtonPressed(repeatedButton, offset);
+
+ if (_activeRepeat.ContainsKey(offset) == false) return true;
+
+ if (button.IsMacro)
+ {
+ while (_activeMacros.ContainsKey(offset))
+ {
+ await Task.Delay(500);
+ if (_activeRepeat.ContainsKey(offset) == false) break;
+ }
+ }
+
+ return false;
+ }
+
+
private void HandleMacro(HOTASButton button, int offset)
{
if (_activeMacros.TryGetValue(offset, out _))
@@ -382,7 +466,7 @@ private void HandleButtonReleased(HOTASButton button, int offset)
_actionJobs.Add(new ActionJobItem() { Offset = offset, MapId = mapId, Actions = null });
}
- private void HandleAxis(JoystickUpdate state)
+ private async Task HandleAxis(JoystickUpdate state)
{
var offset = (int)state.Offset;
@@ -393,7 +477,7 @@ private void HandleAxis(JoystickUpdate state)
if (!axis.IsSegmentChanged) return;
var map = axis.GetButtonMapFromRawValue(state.Value);
- HandleButtonPressed(map, offset);
+ await HandleButtonPressed(map, offset);
HandleButtonReleased(map, offset);
}
diff --git a/src/Models/IHOTASCollection.cs b/src/Models/IHOTASCollection.cs
index 2d86cc6..2a2170a 100644
--- a/src/Models/IHOTASCollection.cs
+++ b/src/Models/IHOTASCollection.cs
@@ -10,6 +10,8 @@ public interface IHOTASCollection
event EventHandler KeystrokeUpSent;
event EventHandler MacroStarted;
event EventHandler MacroCancelled;
+ event EventHandler RepeatStarted;
+ event EventHandler RepeatCancelled;
event EventHandler ButtonPressed;
event EventHandler AxisChanged;
event EventHandler ModeChanged;
diff --git a/src/Models/IHOTASDevice.cs b/src/Models/IHOTASDevice.cs
index 56c5dd5..9886965 100644
--- a/src/Models/IHOTASDevice.cs
+++ b/src/Models/IHOTASDevice.cs
@@ -11,6 +11,8 @@ public interface IHOTASDevice
event EventHandler KeystrokeUpSent;
event EventHandler MacroStarted;
event EventHandler MacroCancelled;
+ event EventHandler RepeatStarted;
+ event EventHandler RepeatCancelled;
event EventHandler ButtonPressed;
event EventHandler ModeSelected;
event EventHandler ShiftReleased;
diff --git a/src/Models/IHOTASQueue.cs b/src/Models/IHOTASQueue.cs
index 80c3ee2..156a29a 100644
--- a/src/Models/IHOTASQueue.cs
+++ b/src/Models/IHOTASQueue.cs
@@ -10,6 +10,8 @@ public interface IHOTASQueue
event EventHandler KeystrokeDownSent;
event EventHandler MacroCancelled;
event EventHandler MacroStarted;
+ event EventHandler RepeatStarted;
+ event EventHandler RepeatCancelled;
event EventHandler ButtonPressed;
event EventHandler ButtonReleased;
event EventHandler AxisChanged;
diff --git a/src/Models/RecordStartedEventArgs.cs b/src/Models/RecordStartedEventArgs.cs
new file mode 100644
index 0000000..2d4e55e
--- /dev/null
+++ b/src/Models/RecordStartedEventArgs.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace SierraHOTAS.Models
+{
+ public class RepeatStartedEventArgs : EventArgs
+ {
+ public int Offset { get; set; }
+ public int Code { get; set; }
+
+ public RepeatStartedEventArgs(int offset, int code)
+ {
+ Offset = offset;
+ Code = code;
+ }
+ }
+}
diff --git a/src/Models/RepeatCancelledEventArgs.cs b/src/Models/RepeatCancelledEventArgs.cs
new file mode 100644
index 0000000..d2ce8b0
--- /dev/null
+++ b/src/Models/RepeatCancelledEventArgs.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace SierraHOTAS.Models
+{
+ public class RepeatCancelledEventArgs : EventArgs
+ {
+ public int Offset { get; set; }
+ public int Code { get; set; }
+
+ public RepeatCancelledEventArgs(int offset, int code)
+ {
+ Offset = offset;
+ Code = code;
+ }
+ }
+}
diff --git a/src/SierraHOTAS.csproj b/src/SierraHOTAS.csproj
index 9b5c0a5..9f9d9a8 100644
--- a/src/SierraHOTAS.csproj
+++ b/src/SierraHOTAS.csproj
@@ -166,6 +166,8 @@
+
+
diff --git a/src/ViewModels/ButtonMapViewModel.cs b/src/ViewModels/ButtonMapViewModel.cs
index 0735f55..c8fdf59 100644
--- a/src/ViewModels/ButtonMapViewModel.cs
+++ b/src/ViewModels/ButtonMapViewModel.cs
@@ -59,6 +59,17 @@ public bool IsOneShot
}
}
+ public int RepeatCount
+ {
+ get => _hotasButton.RepeatCount;
+ set
+ {
+ if (_hotasButton.RepeatCount == value) return;
+ _hotasButton.RepeatCount = value;
+ OnPropertyChanged(nameof(RepeatCount));
+ }
+ }
+
public string ActionName
{
get => ActionItem.ActionName;
diff --git a/src/ViewModels/HOTASCollectionViewModel.cs b/src/ViewModels/HOTASCollectionViewModel.cs
index 3433180..64f82f9 100644
--- a/src/ViewModels/HOTASCollectionViewModel.cs
+++ b/src/ViewModels/HOTASCollectionViewModel.cs
@@ -276,6 +276,8 @@ public void Initialize()
_deviceList.KeystrokeDownSent += DeviceList_KeystrokeDownSent;
_deviceList.MacroStarted += DeviceList_MacroStarted;
_deviceList.MacroCancelled += DeviceList_MacroCancelled;
+ _deviceList.RepeatStarted += DeviceList_RepeatStarted;
+ _deviceList.RepeatCancelled += DeviceList_RepeatCancelled;
_deviceList.KeystrokeUpSent += DeviceList_KeystrokeUpSent;
_deviceList.ModeChanged += OnModeChanged;
_deviceList.LostConnectionToDevice += DeviceList_LostConnectionToDevice;
@@ -353,15 +355,26 @@ private void AddActivity(IHotasBaseMap map, KeystrokeSentEventArgs e)
private void DeviceList_MacroStarted(object sender, MacroStartedEventArgs e)
{
- AddMacroActivity(sender, e.Offset, e.Code, false, "Macro Started");
+ AddCustomActivity(sender, e.Offset, e.Code, false, "Macro Started");
}
private void DeviceList_MacroCancelled(object sender, MacroCancelledEventArgs e)
{
- AddMacroActivity(sender, e.Offset, e.Code, true, "Macro Cancelled");
+ AddCustomActivity(sender, e.Offset, e.Code, true, "Macro Cancelled");
}
- private void AddMacroActivity(object sender, int offset, int scanCode, bool isKeyUp, string message)
+ private void DeviceList_RepeatStarted(object sender, RepeatStartedEventArgs e)
+ {
+ Logging.Log.Debug("HOTASCollectionVM - repeat started event");
+ AddCustomActivity(sender, e.Offset, e.Code, false, "Repeat Started");
+ }
+
+ private void DeviceList_RepeatCancelled(object sender, RepeatCancelledEventArgs e)
+ {
+ AddCustomActivity(sender, e.Offset, e.Code, true, "Repeat Cancelled");
+ }
+
+ private void AddCustomActivity(object sender, int offset, int scanCode, bool isKeyUp, string message)
{
var map = (sender as HOTASQueue)?.GetMap(offset);
if (map == null) return;
diff --git a/src/Win32/Keyboard.cs b/src/Win32/Keyboard.cs
index 4729243..82d59f7 100644
--- a/src/Win32/Keyboard.cs
+++ b/src/Win32/Keyboard.cs
@@ -97,7 +97,9 @@ private static void PopulateKeyDisplayNamesExtendedDictionary()
{Win32Structures.ScanCodeShort.LEFT, "LEFT"},
{Win32Structures.ScanCodeShort.RETURN, "NUM ENTR"},
{Win32Structures.ScanCodeShort.MACRO_STARTED, "MACRO"},
- {Win32Structures.ScanCodeShort.MACRO_CANCELLED, "MACRO"}
+ {Win32Structures.ScanCodeShort.MACRO_CANCELLED, "MACRO"},
+ {Win32Structures.ScanCodeShort.REPEAT_STARTED, "REPEAT"},
+ {Win32Structures.ScanCodeShort.REPEAT_CANCELLED, "REPEAT"}
};
}
diff --git a/src/Win32/Win32Structures.cs b/src/Win32/Win32Structures.cs
index 0e5fcce..9b54d37 100644
--- a/src/Win32/Win32Structures.cs
+++ b/src/Win32/Win32Structures.cs
@@ -883,7 +883,9 @@ public enum ScanCodeShort : short
//PA1 = 0,
//OEM_CLEAR = 0,
MACRO_STARTED = -1, //custom addition for sierraHOTAS
- MACRO_CANCELLED = -2 //custom addition for sierraHOTAS
+ MACRO_CANCELLED = -2, //custom addition for sierraHOTAS
+ REPEAT_STARTED = -3, //custom addition for sierraHOTAS
+ REPEAT_CANCELLED = -4 //custom addition for sierraHOTAS
}
[StructLayout(LayoutKind.Sequential)]