Replace SharpDX.DirectInput v4.2.0 with HIDDevices v3.0.1

This supports newer .Net version.
Also supports more gamepads like PS5 controller.
This commit is contained in:
Peter Kirmeier 2023-05-01 16:24:33 +02:00
parent 3153032110
commit c7f4f44250
7 changed files with 169 additions and 290 deletions

View file

@ -42,7 +42,7 @@ namespace SystemTrayMenu
if (Settings.Default.CheckForUpdates)
{
Dispatcher.InvokeAsync(
_ = Dispatcher.InvokeAsync(
() => GitHubUpdate.ActivateNewVersionFormOrCheckForUpdates(showWhenUpToDate: false),
DispatcherPriority.ApplicationIdle);
}

View file

@ -33,7 +33,7 @@ namespace SystemTrayMenu.Business
private readonly List<BackgroundWorker> workersSubMenu = new();
private readonly WaitToLoadMenu waitToOpenMenu = new();
private readonly KeyboardInput keyboardInput;
private readonly JoystickHelper joystickHelper;
private readonly JoystickHelper? joystickHelper;
private readonly List<FileSystemWatcher> watchers = new();
private readonly List<EventArgs> watcherHistory = new();
private readonly DispatcherTimer timerShowProcessStartedAsLoadingIcon = new();
@ -118,8 +118,17 @@ namespace SystemTrayMenu.Business
dgvMouseRow.RowMouseLeave += Dgv_RowMouseLeave; // event moved to Menu.CellMouseLeave()
#endif
joystickHelper = new();
joystickHelper.KeyPressed += (key, modifiers) => MainMenu?.Dispatcher.Invoke(keyboardInput.CmdKeyProcessed, new object[] { null!, key, modifiers });
if (Settings.Default.SupportGamepad)
{
joystickHelper = new();
joystickHelper.KeyPressed += (key, modifiers) =>
{
if (IsMainUsable)
{
MainMenu?.Dispatcher.Invoke(keyboardInput.CmdKeyProcessed, new object[] { null!, key, modifiers });
}
};
}
timerStillActiveCheck.Interval = TimeSpan.FromMilliseconds(Settings.Default.TimeUntilClosesAfterEnterPressed + 20);
timerStillActiveCheck.Tick += (sender, e) => StillActiveTick();
@ -206,7 +215,7 @@ namespace SystemTrayMenu.Business
waitToOpenMenu.Dispose();
keyboardInput.Dispose();
joystickHelper.Dispose();
joystickHelper?.Dispose();
timerShowProcessStartedAsLoadingIcon.Stop();
timerStillActiveCheck.Stop();
waitLeave.Stop();
@ -282,7 +291,6 @@ namespace SystemTrayMenu.Business
else
{
openCloseState = OpenCloseState.Opening;
joystickHelper.Enable();
StartWorker();
}
}
@ -798,7 +806,6 @@ namespace SystemTrayMenu.Business
});
Config.AlwaysOpenByPin = false;
joystickHelper.Disable();
}
private void GetScreenBounds(out Rect screenBounds, out bool useCustomLocation, out StartLocation startLocation)

View file

@ -138,9 +138,7 @@ namespace SystemTrayMenu.Helpers
Directory.CreateDirectory(pathToStoreIcons);
}
using HttpResponseMessage response = client.GetAsync(urlGoogleIconDownload).Result;
using HttpContent content = response.Content;
Stream stream = content.ReadAsStreamAsync().Result;
Stream stream = client.GetStreamAsync(urlGoogleIconDownload).Result;
using var fileStream = File.Create(pathIconPng);
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(fileStream);

View file

@ -5,30 +5,68 @@
namespace SystemTrayMenu.Helpers
{
using System;
using System.Diagnostics;
using System.Threading;
using System.Reactive.Linq;
using System.Windows.Input;
using SharpDX.DirectInput;
using Key = System.Windows.Input.Key;
using System.Windows.Threading;
using DevDecoder.HIDDevices;
using DevDecoder.HIDDevices.Controllers;
using DevDecoder.HIDDevices.Converters;
public class JoystickHelper : IDisposable
{
private readonly System.Timers.Timer timerReadJoystick = new();
private readonly object lockRead = new();
private Joystick? joystick;
private Key pressingKey;
private int pressingKeyCounter;
private bool joystickHelperEnabled;
private readonly Dispatcher dispatchter = Dispatcher.CurrentDispatcher;
private readonly Devices devices;
private readonly IDisposable? subscriptionGamepads;
private IDisposable? subscriptionGamepadControls;
private Gamepad? gamepad;
public JoystickHelper()
{
timerReadJoystick.Interval = 80;
timerReadJoystick.Elapsed += ReadJoystickLoop;
timerReadJoystick.Enabled = false;
if (Properties.Settings.Default.SupportGamepad)
devices = new();
subscriptionGamepads = devices.Controllers<Gamepad>().Subscribe(g =>
{
timerReadJoystick.Start();
}
// If we already have a connected gamepad ignore any more.
// ReSharper disable once AccessToDisposedClosure
if (gamepad?.IsConnected == true)
{
return;
}
gamepad = g;
g.Connect();
subscriptionGamepadControls = g.Changes.Subscribe(controls =>
{
foreach (var control in controls)
{
if (control.PropertyName.Equals("BButton"))
{
if (g.BButton)
{
// TODO: Check if dispatcher is required or will be dispatched by keypressed any way
dispatchter.Invoke(() => KeyPressed?.Invoke(Key.Enter, ModifierKeys.None));
}
}
else if (control.PropertyName.Equals("Hat"))
{
Key key = Key.None;
switch (g.Hat)
{
case Direction.North: key = Key.Up; break;
case Direction.East: key = Key.Right; break;
case Direction.South: key = Key.Down; break;
case Direction.West: key = Key.Left; break;
}
if (key != Key.None)
{
// TODO: Check if dispatcher is required or will be dispatched by keypressed any way
dispatchter.Invoke(() => KeyPressed?.Invoke(key, ModifierKeys.None));
}
}
}
});
});
}
~JoystickHelper() // the finalizer
@ -38,16 +76,6 @@ namespace SystemTrayMenu.Helpers
public event Action<Key, ModifierKeys>? KeyPressed;
public void Enable()
{
joystickHelperEnabled = true;
}
public void Disable()
{
joystickHelperEnabled = false;
}
public void Dispose()
{
Dispose(true);
@ -58,159 +86,11 @@ namespace SystemTrayMenu.Helpers
{
if (disposing)
{
timerReadJoystick.Elapsed -= ReadJoystickLoop;
timerReadJoystick.Dispose();
joystick?.Dispose();
}
}
private static Key ReadKeyFromState(JoystickUpdate state)
{
Key keys = Key.None;
switch (state.Offset)
{
case JoystickOffset.PointOfViewControllers0:
switch (state.Value)
{
case 0:
keys = Key.Up;
break;
case 9000:
keys = Key.Right;
break;
case 18000:
keys = Key.Down;
break;
case 27000:
keys = Key.Left;
break;
default:
break;
}
break;
case JoystickOffset.Buttons0:
if (state.Value == 128)
{
keys = Key.Enter;
}
break;
default:
break;
}
return keys;
}
private void ReadJoystickLoop(object? sender, System.Timers.ElapsedEventArgs e)
{
if (joystickHelperEnabled)
{
lock (lockRead)
{
timerReadJoystick.Stop();
if (joystick == null)
{
Thread.Sleep(3000);
InitializeJoystick();
}
else
{
ReadJoystick();
}
timerReadJoystick.Start();
}
}
}
private void ReadJoystick()
{
if (joystick != null)
{
try
{
joystick.Poll();
JoystickUpdate[] datas = joystick.GetBufferedData();
foreach (JoystickUpdate state in datas)
{
if (state.Value < 0)
{
pressingKey = Key.None;
pressingKeyCounter = 0;
continue;
}
Key key = ReadKeyFromState(state);
if (key != Key.None)
{
KeyPressed?.Invoke(key, ModifierKeys.None);
if (state.Offset == JoystickOffset.PointOfViewControllers0)
{
pressingKeyCounter = 0;
pressingKey = key;
}
}
}
if (pressingKey != Key.None)
{
pressingKeyCounter += 1;
if (pressingKeyCounter > 1)
{
KeyPressed?.Invoke(pressingKey, ModifierKeys.None);
}
}
}
catch
{
joystick?.Dispose();
joystick = null;
}
}
}
private void InitializeJoystick()
{
// Initialize DirectInput
DirectInput directInput = new();
// Find a Joystick Guid
Guid joystickGuid = Guid.Empty;
foreach (DeviceInstance deviceInstance in directInput.GetDevices(
DeviceType.Gamepad,
DeviceEnumerationFlags.AllDevices))
{
joystickGuid = deviceInstance.InstanceGuid;
}
// If Gamepad not found, look for a Joystick
if (joystickGuid == Guid.Empty)
{
foreach (DeviceInstance deviceInstance in directInput.GetDevices(
DeviceType.Joystick,
DeviceEnumerationFlags.AllDevices))
{
joystickGuid = deviceInstance.InstanceGuid;
}
}
// If Joystick found
if (joystickGuid != Guid.Empty)
{
// Instantiate the joystick
joystick = new Joystick(directInput, joystickGuid);
// Set BufferSize in order to use buffered data.
joystick.Properties.BufferSize = 128;
var handle = Process.GetCurrentProcess().MainWindowHandle;
joystick.SetCooperativeLevel(handle, CooperativeLevel.NonExclusive | CooperativeLevel.Background);
// Acquire the joystick
joystick.Acquire();
subscriptionGamepads?.Dispose();
subscriptionGamepadControls?.Dispose();
gamepad?.Dispose();
gamepad = null;
devices.Dispose();
}
}
}

View file

@ -74,10 +74,8 @@ namespace SystemTrayMenu.Helpers.Updater
try
{
using HttpResponseMessage response = client.GetAsync(urlGithubReleases).Result;
using HttpContent content = response.Content;
string responseString = content.ReadAsStringAsync().Result;
releases = responseString.FromJson<List<Dictionary<string, object>>>();
string response = client.GetStringAsync(urlGithubReleases).Result;
releases = response.FromJson<List<Dictionary<string, object>>>();
}
catch (Exception ex)
{

View file

@ -172,13 +172,12 @@
</COMReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="H.InputSimulator" Version="1.3.0" />
<PackageReference Include="H.NotifyIcon" Version="2.0.108" />
<PackageReference Include="HIDDevices" Version="3.0.1" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="7.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SharpDX.DirectInput" Version="4.2.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View file

@ -1,98 +1,95 @@
// <copyright file="SingleAppInstance.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace SystemTrayMenu.Utilities
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
// <copyright file="SingleAppInstance.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace SystemTrayMenu.Utilities
{
using System;
using System.Diagnostics;
using System.Linq;
using System.Windows.Input;
using SystemTrayMenu.UserInterface.HotkeyTextboxControl;
using WindowsInput;
internal static class SingleAppInstance
{
internal static bool Initialize()
{
bool success = true;
try
{
foreach (Process p in Process.GetProcessesByName(
Process.GetCurrentProcess().ProcessName).
Where(s => s.Id != Environment.ProcessId))
{
if (Properties.Settings.Default.SendHotkeyInsteadKillOtherInstances)
using SystemTrayMenu.UserInterface.HotkeyTextboxControl;
internal static class SingleAppInstance
{
internal static bool Initialize()
{
bool success = true;
try
{
foreach (Process p in Process.GetProcessesByName(
Process.GetCurrentProcess().ProcessName).
Where(s => s.Id != Environment.ProcessId))
{
if (Properties.Settings.Default.SendHotkeyInsteadKillOtherInstances)
{
#if TODO // HOTKEY
Key modifiers = HotkeyControl.HotkeyModifiersFromString(Properties.Settings.Default.HotKey);
Key hotkey = HotkeyControl.HotkeyFromString(Properties.Settings.Default.HotKey);
try
{
List<VirtualKeyCode> virtualKeyCodesModifiers = new();
foreach (string key in modifiers.ToString().ToUpperInvariant().Split(", "))
{
if (key == "NONE")
{
continue;
}
VirtualKeyCode virtualKeyCode = VirtualKeyCode.LWIN;
virtualKeyCode = key switch
{
"ALT" => VirtualKeyCode.MENU,
_ => (VirtualKeyCode)Enum.Parse(
typeof(VirtualKeyCode), key.ToUpperInvariant()),
};
virtualKeyCodesModifiers.Add(virtualKeyCode);
}
VirtualKeyCode virtualKeyCodeHotkey = 0;
if (Enum.IsDefined(typeof(VirtualKeyCode), (int)hotkey))
{
virtualKeyCodeHotkey = (VirtualKeyCode)(int)hotkey;
}
new InputSimulator().Keyboard.ModifiedKeyStroke(virtualKeyCodesModifiers, virtualKeyCodeHotkey);
success = false;
}
catch (Exception ex)
{
Log.Warn($"Send hoktey {Properties.Settings.Default.HotKey} to other instance failed", ex);
#if TODO // HOTKEY
Key modifiers = HotkeyControl.HotkeyModifiersFromString(Properties.Settings.Default.HotKey);
Key hotkey = HotkeyControl.HotkeyFromString(Properties.Settings.Default.HotKey);
try
{
List<VirtualKeyCode> virtualKeyCodesModifiers = new();
foreach (string key in modifiers.ToString().ToUpperInvariant().Split(", "))
{
if (key == "NONE")
{
continue;
}
VirtualKeyCode virtualKeyCode = VirtualKeyCode.LWIN;
virtualKeyCode = key switch
{
"ALT" => VirtualKeyCode.MENU,
_ => (VirtualKeyCode)Enum.Parse(
typeof(VirtualKeyCode), key.ToUpperInvariant()),
};
virtualKeyCodesModifiers.Add(virtualKeyCode);
}
VirtualKeyCode virtualKeyCodeHotkey = 0;
if (Enum.IsDefined(typeof(VirtualKeyCode), (int)hotkey))
{
virtualKeyCodeHotkey = (VirtualKeyCode)(int)hotkey;
}
new InputSimulator().Keyboard.ModifiedKeyStroke(virtualKeyCodesModifiers, virtualKeyCodeHotkey);
success = false;
}
#endif
}
if (!Properties.Settings.Default.SendHotkeyInsteadKillOtherInstances)
{
try
{
if (!p.CloseMainWindow())
{
p.Kill();
}
p.WaitForExit();
p.Close();
}
catch (Exception ex)
{
Log.Error("Run as single instance failed", ex);
success = false;
}
}
}
}
catch (Exception ex)
{
Log.Error("Run as single instance failed", ex);
}
return success;
}
}
}
catch (Exception ex)
{
Log.Warn($"Send hoktey {Properties.Settings.Default.HotKey} to other instance failed", ex);
}
#endif
}
if (!Properties.Settings.Default.SendHotkeyInsteadKillOtherInstances)
{
try
{
if (!p.CloseMainWindow())
{
p.Kill();
}
p.WaitForExit();
p.Close();
}
catch (Exception ex)
{
Log.Error("Run as single instance failed", ex);
success = false;
}
}
}
}
catch (Exception ex)
{
Log.Error("Run as single instance failed", ex);
}
return success;
}
}
}