mirror of
https://github.com/Hofknecht/SystemTrayMenu.git
synced 2024-09-30 17:16:52 +13:00
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:
parent
3153032110
commit
c7f4f44250
7 changed files with 169 additions and 290 deletions
|
@ -42,7 +42,7 @@ namespace SystemTrayMenu
|
|||
|
||||
if (Settings.Default.CheckForUpdates)
|
||||
{
|
||||
Dispatcher.InvokeAsync(
|
||||
_ = Dispatcher.InvokeAsync(
|
||||
() => GitHubUpdate.ActivateNewVersionFormOrCheckForUpdates(showWhenUpToDate: false),
|
||||
DispatcherPriority.ApplicationIdle);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue