diff --git a/HandheldCompanion.iss b/HandheldCompanion.iss index a641b5eba..f93aadead 100644 --- a/HandheldCompanion.iss +++ b/HandheldCompanion.iss @@ -389,7 +389,7 @@ end; #define MyAppSetupName 'Handheld Companion' #define MyBuildId 'HandheldCompanion' -#define MyAppVersion '0.18.0.3' +#define MyAppVersion '0.18.0.4' #define MyAppPublisher 'BenjaminLSR' #define MyAppCopyright 'Copyright @ BenjaminLSR' #define MyAppURL 'https://github.com/Valkirie/HandheldCompanion' diff --git a/HandheldCompanion/Controllers/DS4Controller.cs b/HandheldCompanion/Controllers/DS4Controller.cs index c331591f4..f43ba69a7 100644 --- a/HandheldCompanion/Controllers/DS4Controller.cs +++ b/HandheldCompanion/Controllers/DS4Controller.cs @@ -1,6 +1,8 @@ using HandheldCompanion.Inputs; using HandheldCompanion.Managers; using HandheldCompanion.Utils; +using Inkore.UI.WPF.Modern; +using System.Windows; using System.Windows.Media; using static JSL; @@ -97,6 +99,15 @@ public override void Unplug() base.Unplug(); } + public override void SetLightColor(byte R, byte G, byte B) + { + // UI thread + Application.Current.Dispatcher.Invoke(() => + { + JslSetLightColour(UserIndex, CommonUtils.rgb_to_int(R, G, B)); + }); + } + public override void Cleanup() { TimerManager.Tick -= UpdateInputs; diff --git a/HandheldCompanion/Controllers/DualSenseController.cs b/HandheldCompanion/Controllers/DualSenseController.cs index 2bbc7f202..48755e2e8 100644 --- a/HandheldCompanion/Controllers/DualSenseController.cs +++ b/HandheldCompanion/Controllers/DualSenseController.cs @@ -1,6 +1,7 @@ using HandheldCompanion.Inputs; using HandheldCompanion.Managers; using HandheldCompanion.Utils; +using System.Windows; using static JSL; namespace HandheldCompanion.Controllers; @@ -90,6 +91,15 @@ public override void Unplug() base.Unplug(); } + public override void SetLightColor(byte R, byte G, byte B) + { + // UI thread + Application.Current.Dispatcher.Invoke(() => + { + JslSetLightColour(UserIndex, CommonUtils.rgb_to_int(R, G, B)); + }); + } + public override void Cleanup() { TimerManager.Tick -= UpdateInputs; diff --git a/HandheldCompanion/Controllers/IController.xaml b/HandheldCompanion/Controllers/IController.xaml index b73fc82c9..57033b1a5 100644 --- a/HandheldCompanion/Controllers/IController.xaml +++ b/HandheldCompanion/Controllers/IController.xaml @@ -63,11 +63,19 @@ - + Spacing="6" + Visibility="Collapsed"> + + + diff --git a/HandheldCompanion/Controllers/IController.xaml.cs b/HandheldCompanion/Controllers/IController.xaml.cs index 1315d230c..254e7cd71 100644 --- a/HandheldCompanion/Controllers/IController.xaml.cs +++ b/HandheldCompanion/Controllers/IController.xaml.cs @@ -206,8 +206,7 @@ public void InjectButton(ButtonFlags button, bool IsKeyDown, bool IsKeyUp) public virtual void SetVibrationStrength(uint value, bool rumble = false) { VibrationStrength = value / 100.0d; - if (rumble) - Rumble(); + if (rumble) Rumble(); } public virtual void SetVibration(byte LargeMotor, byte SmallMotor) @@ -223,11 +222,14 @@ public virtual bool IsConnected() return false; } - public virtual async void Rumble(int delay = 125) + public virtual void Rumble(int delay = 125) { - SetVibration(byte.MaxValue, byte.MaxValue); - await Task.Delay(delay); - SetVibration(0, 0); + Task.Run(async () => + { + SetVibration(byte.MaxValue, byte.MaxValue); + await Task.Delay(delay); + SetVibration(0, 0); + }); } public virtual bool IsPlugged() @@ -262,59 +264,64 @@ public virtual void Cleanup() public bool IsHidden() { - var hide_device = HidHide.IsRegistered(Details.deviceInstanceId); + // var hide_device = HidHide.IsRegistered(Details.deviceInstanceId); var hide_base = HidHide.IsRegistered(Details.baseContainerDeviceInstanceId); return /* hide_device || */ hide_base; } - public void Hide(bool powerCycle = true) + public virtual void Hide(bool powerCycle = true) { HideHID(); - // set flag - powerCycle = powerCycle && this is not SteamController; - if (powerCycle) { // UI thread (async) Application.Current.Dispatcher.BeginInvoke(() => { IsEnabled = false; - ProgressBarUpdate.Visibility = Visibility.Visible; + ProgressBarPanel.Visibility = Visibility.Visible; }); ControllerManager.PowerCyclers[Details.baseContainerDeviceInstanceId] = true; - Details.CyclePort(); + + CyclePort(); return; } RefreshControls(); } - public void Unhide(bool powerCycle = true) + public virtual void Unhide(bool powerCycle = true) { UnhideHID(); - // set flag - powerCycle = powerCycle && this is not SteamController; - if (powerCycle) { // UI thread (async) Application.Current.Dispatcher.BeginInvoke(() => { IsEnabled = false; - ProgressBarUpdate.Visibility = Visibility.Visible; + ProgressBarPanel.Visibility = Visibility.Visible; }); ControllerManager.PowerCyclers[Details.baseContainerDeviceInstanceId] = true; - Details.CyclePort(); + + CyclePort(); return; } RefreshControls(); } + public virtual void CyclePort() + { + Details.CyclePort(); + } + + public virtual void SetLightColor(byte R, byte G, byte B) + { + } + public void HideHID() { HidHide.HidePath(Details.baseContainerDeviceInstanceId); @@ -343,6 +350,11 @@ public void UnhideHID() */ } + public virtual bool RestoreDrivers() + { + return true; + } + protected virtual void Calibrate() { } diff --git a/HandheldCompanion/Controllers/JSController.cs b/HandheldCompanion/Controllers/JSController.cs index a538cdcda..cab491d15 100644 --- a/HandheldCompanion/Controllers/JSController.cs +++ b/HandheldCompanion/Controllers/JSController.cs @@ -1,5 +1,7 @@ using HandheldCompanion.Inputs; using HandheldCompanion.Utils; +using Nefarius.Utilities.DeviceManagement.PnP; +using System.Threading.Tasks; using System.Windows; using static JSL; using Timer = System.Timers.Timer; @@ -177,6 +179,30 @@ protected override void Calibrate() }); } + public override void CyclePort() + { + string enumerator = Details.GetEnumerator(); + switch (enumerator) + { + default: + case "BTHENUM": + Task.Run(async () => + { + // Details.InstallNullDrivers(); + // await Task.Delay(1000); + // Details.InstallCustomDriver("hidbth.inf"); + + Details.Uninstall(false); + await Task.Delay(1000); + Devcon.Refresh(); + }); + break; + case "USB": + base.CyclePort(); + break; + } + } + protected override void ui_button_calibrate_Click(object sender, RoutedEventArgs e) { // start calibration diff --git a/HandheldCompanion/Controllers/SteamController.cs b/HandheldCompanion/Controllers/SteamController.cs index 59283f53a..d429b2882 100644 --- a/HandheldCompanion/Controllers/SteamController.cs +++ b/HandheldCompanion/Controllers/SteamController.cs @@ -28,6 +28,20 @@ public virtual void SetVirtualMuted(bool mute) isVirtualMuted = mute; } + public override void Hide(bool powerCycle = true) + { + HideHID(); + + RefreshControls(); + } + + public override void Unhide(bool powerCycle = true) + { + UnhideHID(); + + RefreshControls(); + } + public override string GetGlyph(ButtonFlags button) { switch (button) diff --git a/HandheldCompanion/Controllers/XInputController.cs b/HandheldCompanion/Controllers/XInputController.cs index a35718e67..9dcec7290 100644 --- a/HandheldCompanion/Controllers/XInputController.cs +++ b/HandheldCompanion/Controllers/XInputController.cs @@ -1,9 +1,12 @@ using HandheldCompanion.Inputs; using HandheldCompanion.Managers; +using Nefarius.Utilities.DeviceManagement.PnP; using SharpDX.XInput; using System; using System.Linq; using System.Runtime.InteropServices; +using System.Threading.Tasks; +using System.Windows; using System.Windows.Media; namespace HandheldCompanion.Controllers; @@ -13,9 +16,6 @@ public class XInputController : IController private Controller Controller; private Gamepad Gamepad; - private GamepadButtonFlags prevButtons; - private XInputStateSecret prevState; - private XInputStateSecret State; public XInputController() @@ -75,11 +75,6 @@ public override void UpdateInputs(long ticks) // update secret state XInputGetStateSecret14(UserIndex, out State); - /* - if (prevButtons.Equals(Gamepad.Buttons) && State.wButtons.Equals(prevState.wButtons) && prevInjectedButtons.Equals(InjectedButtons)) - return; - */ - Inputs.ButtonState = InjectedButtons.Clone() as ButtonState; Inputs.ButtonState[ButtonFlags.B1] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.A); @@ -129,10 +124,6 @@ public override void UpdateInputs(long ticks) Inputs.AxisState[AxisFlags.L2] = Gamepad.LeftTrigger; Inputs.AxisState[AxisFlags.R2] = Gamepad.RightTrigger; - - // update states - prevButtons = Gamepad.Buttons; - prevState = State; } catch { } @@ -195,6 +186,67 @@ public static UserIndex TryGetUserIndex(PnPDetails details) return SharpDX.XInput.UserIndex.Any; } + public override void Hide(bool powerCycle = true) + { + if (powerCycle) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + ProgressBarWarning.Text = Properties.Resources.ControllerPage_XInputControllerWarning; + }); + } + + base.Hide(powerCycle); + } + + public override void Unhide(bool powerCycle = true) + { + if (powerCycle) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + ProgressBarWarning.Text = Properties.Resources.ControllerPage_XInputControllerWarning; + }); + } + + base.Unhide(powerCycle); + } + + public override void CyclePort() + { + string enumerator = Details.GetEnumerator(); + switch (enumerator) + { + default: + case "BTHENUM": + Task.Run(async () => + { + /* + // Bluetooth HID Device + Details.InstallNullDrivers(false); + Details.InstallCustomDriver("hidbth.inf", false); + + // Bluetooth XINPUT compatible input device + Details.InstallNullDrivers(true); + Details.InstallCustomDriver("xinputhid.inf", true); + */ + + /* + Details.Uninstall(false); // Bluetooth HID Device + Details.Uninstall(true); // Bluetooth XINPUT compatible input device + await Task.Delay(1000); + Devcon.Refresh(); + */ + }); + break; + case "USB": + base.CyclePort(); + break; + } + } + public override string GetGlyph(ButtonFlags button) { switch (button) diff --git a/HandheldCompanion/Devices/ASUS/AsusACPI.cs b/HandheldCompanion/Devices/ASUS/AsusACPI.cs new file mode 100644 index 000000000..caf644f03 --- /dev/null +++ b/HandheldCompanion/Devices/ASUS/AsusACPI.cs @@ -0,0 +1,511 @@ +using System.Management; +using System.Runtime.InteropServices; +using System; +using System.Threading; +using HandheldCompanion.Managers; + +public enum AsusFan +{ + CPU = 0, + GPU = 1, + Mid = 2, + XGM = 3 +} + +public enum AsusMode +{ + Balanced = 0, + Turbo = 1, + Silent = 2 +} + +public enum AsusGPU +{ + Eco = 0, + Standard = 1, + Ultimate = 2 +} + +namespace HandheldCompanion.Devices.ASUS +{ + public class AsusACPI + { + const string FILE_NAME = @"\\.\\ATKACPI"; + const uint CONTROL_CODE = 0x0022240C; + + const uint DSTS = 0x53545344; + const uint DEVS = 0x53564544; + const uint INIT = 0x54494E49; + + public const uint UniversalControl = 0x00100021; + + public const int KB_Light_Up = 0xc4; + public const int KB_Light_Down = 0xc5; + public const int Brightness_Down = 0x10; + public const int Brightness_Up = 0x20; + public const int KB_Sleep = 0x6c; + public const int KB_DUO_PgUpDn = 0x4B; + public const int KB_DUO_SecondDisplay = 0x6A; + + + public const int Touchpad_Toggle = 0x6B; + + public const int ChargerMode = 0x0012006C; + + public const int ChargerUSB = 2; + public const int ChargerBarrel = 1; + + public const uint CPU_Fan = 0x00110013; + public const uint GPU_Fan = 0x00110014; + public const uint Mid_Fan = 0x00110031; + + public const uint PerformanceMode = 0x00120075; // Performance modes + public const uint VivoBookMode = 0x00110019; // Vivobook performance modes + + public const uint GPUEco = 0x00090020; + public const uint GPUXGConnected = 0x00090018; + public const uint GPUXG = 0x00090019; + public const uint GPUMux = 0x00090016; + + public const uint BatteryLimit = 0x00120057; + public const uint ScreenOverdrive = 0x00050019; + public const uint ScreenMiniled = 0x0005001E; + + public const uint DevsCPUFan = 0x00110022; + public const uint DevsGPUFan = 0x00110023; + + public const uint DevsCPUFanCurve = 0x00110024; + public const uint DevsGPUFanCurve = 0x00110025; + public const uint DevsMidFanCurve = 0x00110032; + + public const int Temp_CPU = 0x00120094; + public const int Temp_GPU = 0x00120097; + + public const int PPT_TotalA0 = 0x001200A0; // SPL (Total limit for all-AMD models) / PL1 + public const int PPT_EDCA1 = 0x001200A1; // CPU EDC + public const int PPT_TDCA2 = 0x001200A2; // CPU TDC + public const int PPT_APUA3 = 0x001200A3; // sPPT (long boost limit) / PL2 + + public const int PPT_CPUB0 = 0x001200B0; // CPU PPT on 2022 (PPT_LIMIT_APU) + public const int PPT_CPUB1 = 0x001200B1; // Total PPT on 2022 (PPT_LIMIT_SLOW) + + public const int PPT_GPUC0 = 0x001200C0; // NVIDIA GPU Boost + public const int PPT_APUC1 = 0x001200C1; // fPPT (fast boost limit) + public const int PPT_GPUC2 = 0x001200C2; // NVIDIA GPU Temp Target (75.. 87 C) + + public const int TUF_KB_BRIGHTNESS = 0x00050021; + public const int TUF_KB = 0x00100056; + public const int TUF_KB_STATE = 0x00100057; + + public const int MICMUTE_LED = 0x00040017; + + public const int TabletState = 0x00060077; + public const int FnLock = 0x00100023; + + public const int ScreenPadToggle = 0x00050031; + public const int ScreenPadBrightness = 0x00050032; + + public const int Tablet_Notebook = 0; + public const int Tablet_Tablet = 1; + public const int Tablet_Tent = 2; + public const int Tablet_Rotated = 3; + + public const int PerformanceBalanced = 0; + public const int PerformanceTurbo = 1; + public const int PerformanceSilent = 2; + public const int PerformanceManual = 4; + + public const int GPUModeEco = 0; + public const int GPUModeStandard = 1; + public const int GPUModeUltimate = 2; + + public const int MinTotal = 5; + + public static int MaxTotal = 150; + public static int DefaultTotal = 125; + + public const int MinCPU = 5; + public const int MaxCPU = 100; + public const int DefaultCPU = 80; + + public const int MinGPUBoost = 5; + public const int MaxGPUBoost = 25; + + public const int MinGPUTemp = 75; + public const int MaxGPUTemp = 87; + + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern IntPtr CreateFile( + string lpFileName, + uint dwDesiredAccess, + uint dwShareMode, + IntPtr lpSecurityAttributes, + uint dwCreationDisposition, + uint dwFlagsAndAttributes, + IntPtr hTemplateFile + ); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool DeviceIoControl( + IntPtr hDevice, + uint dwIoControlCode, + byte[] lpInBuffer, + uint nInBufferSize, + byte[] lpOutBuffer, + uint nOutBufferSize, + ref uint lpBytesReturned, + IntPtr lpOverlapped + ); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool CloseHandle(IntPtr hObject); + + private const uint GENERIC_READ = 0x80000000; + private const uint GENERIC_WRITE = 0x40000000; + private const uint OPEN_EXISTING = 3; + private const uint FILE_ATTRIBUTE_NORMAL = 0x80; + private const uint FILE_SHARE_READ = 1; + private const uint FILE_SHARE_WRITE = 2; + + private const int ASUS_WMI_KEYBOARD_POWER_BOOT = 0x03 << 16; + private const int ASUS_WMI_KEYBOARD_POWER_AWAKE = 0x0C << 16; + private const int ASUS_WMI_KEYBOARD_POWER_SLEEP = 0x30 << 16; + private const int ASUS_WMI_KEYBOARD_POWER_SHUTDOWN = 0xC0 << 16; + + private IntPtr handle; + + // Event handling attempt + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool WaitForSingleObject(IntPtr hHandle, int dwMilliseconds); + + private IntPtr eventHandle; + + // still works only with asus optimization service on , if someone knows how to get ACPI events from asus without that - let me know + public void RunListener() + { + + eventHandle = CreateEvent(IntPtr.Zero, false, false, "ATK4001"); + + byte[] outBuffer = new byte[16]; + byte[] data = new byte[8]; + bool result; + + data[0] = BitConverter.GetBytes(eventHandle.ToInt32())[0]; + data[1] = BitConverter.GetBytes(eventHandle.ToInt32())[1]; + + Control(0x222400, data, outBuffer); + + while (true) + { + WaitForSingleObject(eventHandle, Timeout.Infinite); + Control(0x222408, new byte[0], outBuffer); + int code = BitConverter.ToInt32(outBuffer, 0); + } + } + + + public AsusACPI() + { + handle = CreateFile( + FILE_NAME, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + IntPtr.Zero, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + IntPtr.Zero + ); + + if (!IsOpen()) + { + LogManager.LogError("Can't connect to Asus ACPI"); + return; + } + + MaxTotal = 50; + DefaultTotal = 30; + } + + public bool IsOpen() + { + return handle != new IntPtr(-1); + } + + public void Control(uint dwIoControlCode, byte[] lpInBuffer, byte[] lpOutBuffer) + { + + uint lpBytesReturned = 0; + DeviceIoControl( + handle, + dwIoControlCode, + lpInBuffer, + (uint)lpInBuffer.Length, + lpOutBuffer, + (uint)lpOutBuffer.Length, + ref lpBytesReturned, + IntPtr.Zero + ); + } + + public void Close() + { + CloseHandle(handle); + } + + protected byte[] CallMethod(uint MethodID, byte[] args) + { + byte[] acpiBuf = new byte[8 + args.Length]; + byte[] outBuffer = new byte[16]; + + BitConverter.GetBytes((uint)MethodID).CopyTo(acpiBuf, 0); + BitConverter.GetBytes((uint)args.Length).CopyTo(acpiBuf, 4); + Array.Copy(args, 0, acpiBuf, 8, args.Length); + + // if (MethodID == DEVS) Debug.WriteLine(BitConverter.ToString(acpiBuf, 0, acpiBuf.Length)); + + Control(CONTROL_CODE, acpiBuf, outBuffer); + + return outBuffer; + + } + + public byte[] DeviceInit() + { + byte[] args = new byte[8]; + return CallMethod(INIT, args); + } + + public int DeviceSet(uint DeviceID, int Status) + { + byte[] args = new byte[8]; + BitConverter.GetBytes((uint)DeviceID).CopyTo(args, 0); + BitConverter.GetBytes((uint)Status).CopyTo(args, 4); + + byte[] status = CallMethod(DEVS, args); + int result = BitConverter.ToInt32(status, 0); + + return result; + } + + public int DeviceSet(uint DeviceID, byte[] Params, string logName) + { + byte[] args = new byte[4 + Params.Length]; + BitConverter.GetBytes((uint)DeviceID).CopyTo(args, 0); + Params.CopyTo(args, 4); + + byte[] status = CallMethod(DEVS, args); + int result = BitConverter.ToInt32(status, 0); + + return BitConverter.ToInt32(status, 0); + } + + public int DeviceGet(uint DeviceID) + { + byte[] args = new byte[8]; + BitConverter.GetBytes((uint)DeviceID).CopyTo(args, 0); + byte[] status = CallMethod(DSTS, args); + + return BitConverter.ToInt32(status, 0) - 65536; + + } + + public byte[] DeviceGetBuffer(uint DeviceID, uint Status = 0) + { + byte[] args = new byte[8]; + BitConverter.GetBytes((uint)DeviceID).CopyTo(args, 0); + BitConverter.GetBytes((uint)Status).CopyTo(args, 4); + + return CallMethod(DSTS, args); + } + + public int SetGPUEco(int eco) + { + int ecoFlag = DeviceGet(GPUEco); + if (ecoFlag < 0) return -1; + + if (ecoFlag == 1 && eco == 0) + return DeviceSet(GPUEco, eco); + + if (ecoFlag == 0 && eco == 1) + return DeviceSet(GPUEco, eco); + + return -1; + } + + public int SetFanRange(AsusFan device, byte[] curve) + { + byte min = (byte)(curve[8] * 255 / 100); + byte max = (byte)(curve[15] * 255 / 100); + byte[] range = { min, max }; + + int result; + switch (device) + { + case AsusFan.GPU: + result = DeviceSet(DevsGPUFan, range, "FanRangeGPU"); + break; + default: + result = DeviceSet(DevsCPUFan, range, "FanRangeCPU"); + break; + } + return result; + } + + public int SetFanSpeed(AsusFan device, byte speed) + { + byte[] curve = new byte[] { 0x1E, 0x28, 0x32, 0x3C, 0x46, 0x50, 0x5A, 0x5A, speed, speed, speed, speed, speed, speed, speed, speed }; + int result; + switch (device) + { + case AsusFan.GPU: + result = DeviceSet(DevsGPUFanCurve, curve, "FanGPU"); + break; + case AsusFan.Mid: + result = DeviceSet(DevsMidFanCurve, curve, "FanMid"); + break; + default: + result = DeviceSet(DevsCPUFanCurve, curve, "FanCPU"); + break; + } + return result; + } + + public int SetFanCurve(AsusFan device, byte[] curve) + { + + if (curve.Length != 16) return -1; + // if (curve.All(singleByte => singleByte == 0)) return -1; + + int result; + int fanScale = 100; //AppConfig.Get("fan_scale", 100); + + // if (fanScale != 100 && device == AsusFan.CPU) Logger.WriteLine("Custom fan scale: " + fanScale); + + // it seems to be a bug, when some old model's bios can go nuts if fan is set to 100% + + for (int i = 8; i < curve.Length; i++) curve[i] = (byte)(Math.Max((byte)0, Math.Min((byte)99, curve[i])) * fanScale / 100); + + switch (device) + { + case AsusFan.GPU: + result = DeviceSet(DevsGPUFanCurve, curve, "FanGPU"); + break; + case AsusFan.Mid: + result = DeviceSet(DevsMidFanCurve, curve, "FanMid"); + break; + default: + result = DeviceSet(DevsCPUFanCurve, curve, "FanCPU"); + break; + } + + return result; + } + + public byte[] GetFanCurve(AsusFan device, int mode = 0) + { + uint fan_mode; + + // because it's asus, and modes are swapped here + switch (mode) + { + case 1: fan_mode = 2; break; + case 2: fan_mode = 1; break; + default: fan_mode = 0; break; + } + + switch (device) + { + case AsusFan.GPU: + return DeviceGetBuffer(DevsGPUFanCurve, fan_mode); + case AsusFan.Mid: + return DeviceGetBuffer(DevsMidFanCurve, fan_mode); + default: + return DeviceGetBuffer(DevsCPUFanCurve, fan_mode); + } + + } + + public static bool IsInvalidCurve(byte[] curve) + { + return curve.Length != 16 || IsEmptyCurve(curve); + } + + public static bool IsEmptyCurve(byte[] curve) + { + return false; + } + + public static byte[] FixFanCurve(byte[] curve) + { + return curve; + + } + + public bool IsXGConnected() + { + //return true; + return DeviceGet(GPUXGConnected) == 1; + } + + public bool IsAllAmdPPT() + { + return DeviceGet(PPT_CPUB0) >= 0 && DeviceGet(PPT_GPUC0) < 0; + } + + public void TUFKeyboardBrightness(int brightness) + { + int param = 0x80 | (brightness & 0x7F); + DeviceSet(TUF_KB_BRIGHTNESS, param); + } + + public void TUFKeyboardRGB(int mode, System.Drawing.Color color, int speed) + { + + byte[] setting = new byte[12]; + setting[0] = (byte)0xB4; + setting[1] = (byte)mode; + setting[2] = color.R; + setting[3] = color.G; + setting[4] = color.B; + setting[5] = (byte)speed; + + DeviceSet(TUF_KB, setting, "TUF RGB"); + //Debug.WriteLine(BitConverter.ToString(setting)); + } + + public void TUFKeyboardPower(bool awake = true, bool boot = false, bool sleep = false, bool shutdown = false) + { + int state = 0xbd; + + if (boot) state = state | ASUS_WMI_KEYBOARD_POWER_BOOT; + if (awake) state = state | ASUS_WMI_KEYBOARD_POWER_AWAKE; + if (sleep) state = state | ASUS_WMI_KEYBOARD_POWER_SLEEP; + if (shutdown) state = state | ASUS_WMI_KEYBOARD_POWER_SHUTDOWN; + + state = state | 0x01 << 8; + + DeviceSet(TUF_KB_STATE, state); + } + + public void SubscribeToEvents(Action EventHandler) + { + try + { + ManagementEventWatcher watcher = new ManagementEventWatcher(); + watcher.EventArrived += new EventArrivedEventHandler(EventHandler); + watcher.Scope = new ManagementScope("root\\wmi"); + watcher.Query = new WqlEventQuery("SELECT * FROM AsusAtkWmiEvent"); + watcher.Start(); + } + catch + { + + } + } + } +} diff --git a/HandheldCompanion/Devices/ASUS/ROGAlly.cs b/HandheldCompanion/Devices/ASUS/ROGAlly.cs index 625d6372b..1aace0523 100644 --- a/HandheldCompanion/Devices/ASUS/ROGAlly.cs +++ b/HandheldCompanion/Devices/ASUS/ROGAlly.cs @@ -1,15 +1,18 @@ -using HandheldCompanion.Inputs; +using HandheldCompanion.Devices.ASUS; +using HandheldCompanion.Inputs; using HandheldCompanion.Managers; using HandheldCompanion.Utils; -using HidSharp; -using HidSharp.Reports.Input; +using HidLibrary; using Nefarius.Utilities.DeviceManagement.PnP; using System; using System.Collections.Generic; +using System.Drawing; using System.Linq; +using System.Management; using System.Numerics; -using System.Threading.Tasks; +using System.Text; using WindowsInput.Events; +using Task = System.Threading.Tasks.Task; namespace HandheldCompanion.Devices; @@ -27,8 +30,24 @@ public class ROGAlly : IDevice { 236, ButtonFlags.None } }; - private IEnumerable _hidDevices; - private List _hidStreams = new(); + private HidDevice hidDevice; + private AsusACPI asusACPI; + + private enum AuraMode + { + Static = 0, + Breathe = 1, + Cycle = 2, + Rainbow = 3, + Strobe = 4, + } + + private enum AuraSpeed + { + Slow = 0xeb, + Medium = 0xf5, + Fast = 0xe1, + } public ROGAlly() { @@ -62,6 +81,9 @@ public ROGAlly() { 'Z', 'Y' } }; + // device specific capacities + Capabilities = DeviceCapabilities.FanControl; + OEMChords.Add(new DeviceChord("CC", new List(), new List(), false, ButtonFlags.OEM1 @@ -84,71 +106,47 @@ public override bool Open() if (!success) return false; - // set exclusive connection with highest priority - var deviceConfiguration = new OpenConfiguration(); - deviceConfiguration.SetOption(OpenOption.Exclusive, true); - deviceConfiguration.SetOption(OpenOption.Transient, true); - deviceConfiguration.SetOption(OpenOption.Priority, OpenPriority.VeryHigh); + if (hidDevice is not null) + hidDevice.OpenDevice(); - foreach (var _hidDevice in _hidDevices) - { - try - { - // connect to hid device - var _stream = _hidDevice.Open(); - - // add stream to array - _hidStreams.Add(_stream); - - // get descriptor - var deviceDescriptor = _hidDevice.GetReportDescriptor(); - - foreach (var inputReport in deviceDescriptor.InputReports) - { - DeviceItemInputParser hiddeviceInputParser = inputReport.DeviceItem.CreateDeviceItemInputParser(); - HidDeviceInputReceiver hidDeviceInputReceiver = deviceDescriptor.CreateHidDeviceInputReceiver(); - - // listen for event(s) - hidDeviceInputReceiver.Received += (sender, e) => - InputReportReceiver_Received(_hidDevice, hiddeviceInputParser, hidDeviceInputReceiver); + // try open asus ACPI + asusACPI = new AsusACPI(); + if (asusACPI is null) + return false; - // start receiver - hidDeviceInputReceiver.Start(_stream); + asusACPI.SubscribeToEvents(WatcherEventArrived); - LogManager.LogInformation("HID connected: {0}", _stream.Device.DevicePath); - } - } - catch - { - LogManager.LogError("HID error: {0}", _hidDevice.DevicePath); - } - } return true; } public override void Close() { - // close stream(s) - foreach (HidStream stream in _hidStreams) - stream.Close(); + // close Asus ACPI + if (asusACPI is not null) + asusACPI.Close(); // clear array - _hidStreams.Clear(); + if (hidDevice is not null) + hidDevice.CloseDevice(); base.Close(); } - public override bool IsReady() - { - // get hid devices - _hidDevices = DeviceList.Local.GetHidDevices() - .Where(d => d.ProductID == _pid && d.VendorID == _vid); - var _hidDevice = _hidDevices.FirstOrDefault(); + public void WatcherEventArrived(object sender, EventArrivedEventArgs e) + { + if (e.NewEvent is null) return; + int EventID = int.Parse(e.NewEvent["EventID"].ToString()); + LogManager.LogDebug("WMI event {0}", EventID); + HandleEvent((byte)EventID); + } - if (_hidDevice is null) + public override bool IsReady() + { + hidDevice = GetHidDevices(_vid, _pid).FirstOrDefault(); + if (hidDevice is null) return false; - var pnpDevice = PnPDevice.GetDeviceByInterfaceId(_hidDevice.DevicePath); + var pnpDevice = PnPDevice.GetDeviceByInterfaceId(hidDevice.DevicePath); var device_parent = pnpDevice.GetProperty(DevicePropertyKey.Device_Parent); var pnpParent = PnPDevice.GetDeviceByInstanceId(device_parent); @@ -158,6 +156,35 @@ public override bool IsReady() return DeviceHelper.IsDeviceAvailable(parent_guid, parent_instanceId); } + public override void SetFanControl(bool enable) + { + if (!asusACPI.IsOpen()) + return; + + switch (enable) + { + case false: + asusACPI.DeviceSet(AsusACPI.PerformanceMode, (int)AsusMode.Turbo); + return; + } + } + + public override void SetFanDuty(double percent) + { + if (!asusACPI.IsOpen()) + return; + + asusACPI.SetFanSpeed(AsusFan.CPU, Convert.ToByte(percent)); + asusACPI.SetFanSpeed(AsusFan.GPU, Convert.ToByte(percent)); + } + + public override float ReadFanDuty() + { + int cpuFan = asusACPI.DeviceGet(AsusACPI.CPU_Fan); + int gpuFan = asusACPI.DeviceGet(AsusACPI.GPU_Fan); + return (cpuFan + gpuFan) / 2 * 100; + } + public override void SetKeyPressDelay(HIDmode controllerMode) { switch (controllerMode) @@ -171,80 +198,63 @@ public override void SetKeyPressDelay(HIDmode controllerMode) } } - private void InputReportReceiver_Received(HidDevice hidDevice, DeviceItemInputParser hiddeviceInputParser, - HidDeviceInputReceiver hidDeviceInputReceiver) + private void HandleEvent(byte key) { - var inputReportBuffer = new byte[hidDevice.GetMaxInputReportLength()]; + if (!keyMapping.ContainsKey(key)) + return; + + // get button + var button = keyMapping[key]; - while (hidDeviceInputReceiver.TryRead(inputReportBuffer, 0, out var report)) + // HID Report Item = hex = decimal + // Left or right paddle = A5 = 165 + // Left OEM key = A6 = 166 + // Right OEM key = 38 = 56 + // Right OEM key hold = A7 and A8 = 167 and 168 + + switch (key) { - switch (report.ReportID) - { - case 90: - break; - default: - return; - } - - if (!hiddeviceInputParser.TryParseReport(inputReportBuffer, 0, report)) continue; - var key = inputReportBuffer[1]; - - if (!keyMapping.ContainsKey(key)) + case 236: return; - // get button - var button = keyMapping[key]; + case 0: + { + KeyRelease(ButtonFlags.OEM3); + } + return; - // HID Report Item = hex = decimal - // Left or right paddle = A5 = 165 - // Left OEM key = A6 = 166 - // Right OEM key = 38 = 56 - // Right OEM key hold = A7 and A8 = 167 and 168 + case 56: + case 166: + { + // OEM1 and OEM2 key needs a key press delay based on emulated controller + Task.Run(async () => + { + KeyPress(button); + await Task.Delay(KeyPressDelay); + KeyRelease(button); + }); + } + break; - switch (key) - { - case 236: - return; + case 165: + case 167: + KeyPress(button); + break; - case 0: - { - KeyRelease(ButtonFlags.OEM3); - } - return; + case 168: + KeyRelease(button); + break; - case 56: - case 166: - { - // OEM1 and OEM2 key needs a key press delay based on emulated controller - Task.Factory.StartNew(async () => - { - KeyPress(button); - await Task.Delay(KeyPressDelay); - KeyRelease(button); - }); - } - break; - - case 165: - case 167: - KeyPress(button); - break; - - case 168: - KeyRelease(button); - break; - - default: + default: + { + Task.Run(async () => { - Task.Factory.StartNew(async () => - { - KeyPress(button); - await Task.Delay(20); - KeyRelease(button); - }); - } - break; - } + KeyPress(button); + await Task.Delay(20); + KeyRelease(button); + }); + } + break; } } } \ No newline at end of file diff --git a/HandheldCompanion/Devices/GPD/GPDWin4-2023-7640U.cs b/HandheldCompanion/Devices/GPD/GPDWin4-2023-7640U.cs new file mode 100644 index 000000000..9f0d0b341 --- /dev/null +++ b/HandheldCompanion/Devices/GPD/GPDWin4-2023-7640U.cs @@ -0,0 +1,74 @@ +using HandheldCompanion.Inputs; +using System.Collections.Generic; +using System.Numerics; +using WindowsInput.Events; + +namespace HandheldCompanion.Devices; + +public class GPDWin4_2023_7640U : IDevice +{ + public GPDWin4_2023_7640U() + { + // device specific settings + ProductIllustration = "device_gpd4"; + + // https://www.amd.com/en/products/apu/amd-ryzen-5-7640u + nTDP = new double[] { 15, 15, 28 }; + cTDP = new double[] { 5, 28 }; + GfxClock = new double[] { 200, 2600 }; + + AngularVelocityAxis = new Vector3(1.0f, 1.0f, -1.0f); + AngularVelocityAxisSwap = new SortedDictionary + { + { 'X', 'Y' }, + { 'Y', 'Z' }, + { 'Z', 'X' } + }; + + AccelerationAxis = new Vector3(1.0f, 1.0f, 1.0f); + AccelerationAxisSwap = new SortedDictionary + { + { 'X', 'X' }, + { 'Y', 'Z' }, + { 'Z', 'Y' } + }; + + // device specific capacities + Capabilities = DeviceCapabilities.FanControl; + + ECDetails = new ECDetails + { + AddressControl = 0x275, + AddressDuty = 0x1809, + AddressRegistry = 0x4E, + AddressData = 0x4F, + ValueMin = 0, + ValueMax = 184 + }; + + // Note, OEM1 not configured as this device has it's own Menu button for guide button + + // Note, chords need to be manually configured in GPD app first by end user + + // GPD Back buttons do not have a "hold", configured buttons are key down and up immediately + // Holding back buttons will result in same key down and up input every 2-3 seconds + // Configured chords in GPD app need unique characters otherwise this leads to a + // "mixed" result when pressing both buttons at the same time + OEMChords.Add(new DeviceChord("Bottom button left", + new List { KeyCode.F11, KeyCode.L }, + new List { KeyCode.F11, KeyCode.L }, + false, ButtonFlags.OEM2 + )); + + OEMChords.Add(new DeviceChord("Bottom button right", + new List { KeyCode.F12, KeyCode.R }, + new List { KeyCode.F12, KeyCode.R }, + false, ButtonFlags.OEM3 + )); + } + + public override void Close() + { + base.Close(); + } +} diff --git a/HandheldCompanion/Devices/GPD/GPDWin4-2023-7840U.cs b/HandheldCompanion/Devices/GPD/GPDWin4-2023-7840U.cs new file mode 100644 index 000000000..ed97f046d --- /dev/null +++ b/HandheldCompanion/Devices/GPD/GPDWin4-2023-7840U.cs @@ -0,0 +1,74 @@ +using HandheldCompanion.Inputs; +using System.Collections.Generic; +using System.Numerics; +using WindowsInput.Events; + +namespace HandheldCompanion.Devices; + +public class GPDWin4_2023_7840U : IDevice +{ + public GPDWin4_2023_7840U() + { + // device specific settings + ProductIllustration = "device_gpd4"; + + // https://www.amd.com/en/products/apu/amd-ryzen-7-7840u + nTDP = new double[] { 15, 15, 28 }; + cTDP = new double[] { 5, 28 }; + GfxClock = new double[] { 200, 2700 }; + + AngularVelocityAxis = new Vector3(1.0f, 1.0f, -1.0f); + AngularVelocityAxisSwap = new SortedDictionary + { + { 'X', 'Y' }, + { 'Y', 'Z' }, + { 'Z', 'X' } + }; + + AccelerationAxis = new Vector3(1.0f, 1.0f, 1.0f); + AccelerationAxisSwap = new SortedDictionary + { + { 'X', 'X' }, + { 'Y', 'Z' }, + { 'Z', 'Y' } + }; + + // device specific capacities + Capabilities = DeviceCapabilities.FanControl; + + ECDetails = new ECDetails + { + AddressControl = 0x275, + AddressDuty = 0x1809, + AddressRegistry = 0x4E, + AddressData = 0x4F, + ValueMin = 0, + ValueMax = 184 + }; + + // Note, OEM1 not configured as this device has it's own Menu button for guide button + + // Note, chords need to be manually configured in GPD app first by end user + + // GPD Back buttons do not have a "hold", configured buttons are key down and up immediately + // Holding back buttons will result in same key down and up input every 2-3 seconds + // Configured chords in GPD app need unique characters otherwise this leads to a + // "mixed" result when pressing both buttons at the same time + OEMChords.Add(new DeviceChord("Bottom button left", + new List { KeyCode.F11, KeyCode.L }, + new List { KeyCode.F11, KeyCode.L }, + false, ButtonFlags.OEM2 + )); + + OEMChords.Add(new DeviceChord("Bottom button right", + new List { KeyCode.F12, KeyCode.R }, + new List { KeyCode.F12, KeyCode.R }, + false, ButtonFlags.OEM3 + )); + } + + public override void Close() + { + base.Close(); + } +} diff --git a/HandheldCompanion/Devices/IDevice.cs b/HandheldCompanion/Devices/IDevice.cs index 5218d1a33..98b2614a4 100644 --- a/HandheldCompanion/Devices/IDevice.cs +++ b/HandheldCompanion/Devices/IDevice.cs @@ -2,6 +2,7 @@ using HandheldCompanion.Managers; using HandheldCompanion.Sensors; using HandheldCompanion.Utils; +using HidLibrary; using System; using System.Collections.Generic; using System.Linq; @@ -18,7 +19,7 @@ public enum DeviceCapabilities : ushort None = 0, InternalSensor = 1, ExternalSensor = 2, - FanControl = 4, + FanControl = 4 } public struct ECDetails @@ -224,7 +225,18 @@ public static IDevice GetDefault() device = new GPDWin3(); break; case "G1618-04": - device = new GPDWin4(); + switch (Processor) + { + case "AMD Ryzen 7 6800U with Radeon Graphics": + device = new GPDWin4(); + break; + case "AMD Ryzen 5 7640U w/ Radeon 760M Graphics": + device = new GPDWin4_2023_7640U(); + break; + case "AMD Ryzen 7 7840U w/ Radeon 780M Graphics": + device = new GPDWin4_2023_7840U(); + break; + } break; case "G1619-03": device = new GPDWinMax2Intel(); @@ -465,6 +477,15 @@ public virtual void SetFanControl(bool enable) ECRamDirectWrite(ECDetails.AddressControl, ECDetails, data); } + public virtual float ReadFanDuty() + { + if (ECDetails.AddressControl == 0) + return 0; + + // todo: implement me + return 0; + } + [Obsolete("ECRamReadByte is deprecated, please use ECRamReadByte with ECDetails instead.")] public static byte ECRamReadByte(ushort address) { @@ -549,4 +570,12 @@ protected void KeyRelease(ButtonFlags button) { KeyReleased?.Invoke(button); } + + protected static IEnumerable GetHidDevices(int vendorId, int deviceId, int minFeatures = 1) + { + HidDevice[] HidDeviceList = HidDevices.Enumerate(vendorId, new int[] { deviceId }).ToArray(); + foreach (HidDevice device in HidDeviceList) + if (device.IsConnected && device.Capabilities.FeatureReportByteLength >= minFeatures) + yield return device; + } } \ No newline at end of file diff --git a/HandheldCompanion/HandheldCompanion.csproj b/HandheldCompanion/HandheldCompanion.csproj index 84c4cb063..fa4073b5a 100644 --- a/HandheldCompanion/HandheldCompanion.csproj +++ b/HandheldCompanion/HandheldCompanion.csproj @@ -12,7 +12,7 @@ HandheldCompanion.App $(SolutionDir)bin\$(Configuration) Resources\icon.ico - 0.18.0.3 + 0.18.0.4 app.manifest AnyCPU;x64;x86 true @@ -139,7 +139,7 @@ - + @@ -148,7 +148,7 @@ - + diff --git a/HandheldCompanion/JoyShockLibrary.dll b/HandheldCompanion/JoyShockLibrary.dll index d27dd14a4..07c7a0443 100644 Binary files a/HandheldCompanion/JoyShockLibrary.dll and b/HandheldCompanion/JoyShockLibrary.dll differ diff --git a/HandheldCompanion/Managers/ControllerManager.cs b/HandheldCompanion/Managers/ControllerManager.cs index 3123992f7..1c9455452 100644 --- a/HandheldCompanion/Managers/ControllerManager.cs +++ b/HandheldCompanion/Managers/ControllerManager.cs @@ -5,6 +5,8 @@ using HandheldCompanion.Utils; using HandheldCompanion.Views; using HandheldCompanion.Views.Classes; +using Inkore.UI.WPF.Modern; +using Microsoft.Win32; using Nefarius.Utilities.DeviceManagement.PnP; using SharpDX.DirectInput; using SharpDX.XInput; @@ -13,6 +15,7 @@ using System.Linq; using System.Windows; using System.Windows.Controls; +using Windows.UI.ViewManagement; using static HandheldCompanion.Utils.DeviceUtils; using static JSL; using DeviceType = SharpDX.DirectInput.DeviceType; @@ -32,6 +35,8 @@ public static class ControllerManager private static ProcessEx? foregroundProcess; private static bool ControllerMuted; + private static UISettings uiSettings; + public static bool IsInitialized; private static bool virtualControllerCreated; @@ -65,6 +70,9 @@ public static void Start() // enable HidHide HidHide.SetCloaking(true); + uiSettings = new UISettings(); + uiSettings.ColorValuesChanged += OnColorValuesChanged; + IsInitialized = true; Initialized?.Invoke(); @@ -75,6 +83,14 @@ public static void Start() LogManager.LogInformation("{0} has started", "ControllerManager"); } + private static void OnColorValuesChanged(UISettings sender, object args) + { + var _systemBackground = uiSettings.GetColorValue(UIColorType.Background); + var _systemAccent = uiSettings.GetColorValue(UIColorType.Accent); + + targetController?.SetLightColor(_systemAccent.R, _systemAccent.G, _systemAccent.B); + } + [Flags] private enum FocusedWindow { @@ -169,6 +185,9 @@ public static void Stop() IsInitialized = false; + // unplug on close + ClearTargetController(); + DeviceManager.XUsbDeviceArrived -= XUsbDeviceArrived; DeviceManager.XUsbDeviceRemoved -= XUsbDeviceRemoved; @@ -182,9 +201,8 @@ public static void Stop() foreach (var controller in GetPhysicalControllers()) controller.Unhide(false); - // unplug on close - var target = GetTargetController(); - target?.Unplug(); + // Flushing possible JoyShocks... + JslDisconnectAndDisposeAll(); LogManager.LogInformation("{0} has stopped", "ControllerManager"); } @@ -458,9 +476,12 @@ private static void HidDeviceRemoved(PnPDetails details, DeviceEventArgs obj) return; // XInput controller are handled elsewhere - if (controller.GetType() == typeof(XInputController)) + if (controller is XInputController) return; + if (controller is JSController) + JslDisconnect(controller.GetUserIndex()); + // are we power cycling ? PowerCyclers.TryGetValue(details.baseContainerDeviceInstanceId, out bool IsPowerCycling); @@ -565,16 +586,20 @@ private static void ClearTargetController() if (targetController is not null) { targetController.InputsUpdated -= UpdateInputs; + targetController.SetLightColor(0, 0, 0); targetController.Cleanup(); targetController.Unplug(); targetController = null; + + // update HIDInstancePath + SettingsManager.SetProperty("HIDInstancePath", string.Empty); } } public static void SetTargetController(string baseContainerDeviceInstanceId, bool IsPowerCycling) { // unplug current controller - if (targetController is not null && targetController.IsPlugged()) + if (targetController is not null) { string targetPath = targetController.GetContainerInstancePath(); @@ -605,22 +630,38 @@ public static void SetTargetController(string baseContainerDeviceInstanceId, boo targetController.InputsUpdated += UpdateInputs; targetController.Plug(); + var _systemBackground = uiSettings.GetColorValue(UIColorType.Background); + var _systemAccent = uiSettings.GetColorValue(UIColorType.Accent); + targetController.SetLightColor(_systemAccent.R, _systemAccent.G, _systemAccent.B); + + // update HIDInstancePath + SettingsManager.SetProperty("HIDInstancePath", baseContainerDeviceInstanceId); + if (!IsPowerCycling) { - if (SettingsManager.GetBoolean("HIDvibrateonconnect")) - targetController.Rumble(); - if (SettingsManager.GetBoolean("HIDcloakonconnect")) - targetController.Hide(); - - // update settings - SettingsManager.SetProperty("HIDInstancePath", baseContainerDeviceInstanceId); + if (!targetController.IsHidden()) + targetController.Hide(); } // check applicable scenarios CheckControllerScenario(); - // raise event + // check if controller is about to power cycle + PowerCyclers.TryGetValue(baseContainerDeviceInstanceId, out IsPowerCycling); + + if (!IsPowerCycling) + { + if (SettingsManager.GetBoolean("HIDvibrateonconnect")) + targetController.Rumble(); + } + else + { + // stop listening to device while it's power cycled + // only usefull for Xbox One bluetooth controllers + targetController.Unplug(); + } + ControllerSelected?.Invoke(targetController); } diff --git a/HandheldCompanion/Misc/HidHide.cs b/HandheldCompanion/Misc/HidHide.cs index ad2db0b4a..9b6f5e740 100644 --- a/HandheldCompanion/Misc/HidHide.cs +++ b/HandheldCompanion/Misc/HidHide.cs @@ -66,7 +66,7 @@ public static List GetRegisteredDevices() try { var service = new HidHideControlService(); - return service.BlockedInstanceIds.ToList(); + return service.BlockedInstanceIds.Select(x => x.ToUpper()).ToList(); } catch { @@ -80,7 +80,7 @@ public static bool IsRegistered(string InstanceId) try { var registered = GetRegisteredDevices(); - return registered.Contains(InstanceId); + return registered.Contains(InstanceId.ToUpper()); } catch { @@ -143,7 +143,7 @@ public static bool RegisterApplication(string fileName) return true; } - public static void SetCloaking(bool status) + public static bool SetCloaking(bool status) { try { @@ -153,13 +153,32 @@ public static void SetCloaking(bool status) } catch { + if (process is null) + return false; + + switch(status) + { + case true: + process.StartInfo.Arguments = $"--cloak-on"; + break; + case false: + process.StartInfo.Arguments = $"--cloak-off"; + break; + } + process.Start(); + process.WaitForExit(); + process.StandardOutput.ReadToEnd(); + + LogManager.LogInformation("HideDevice SetCloaking: {0}", status); } + + return true; } - public static void UnhidePath(string deviceInstancePath) + public static bool UnhidePath(string deviceInstancePath) { if (string.IsNullOrEmpty(deviceInstancePath)) - return; + return false; try { @@ -172,13 +191,24 @@ public static void UnhidePath(string deviceInstancePath) } catch { + if (process is null) + return false; + + process.StartInfo.Arguments = $"--dev-unhide \"{deviceInstancePath}\""; + process.Start(); + process.WaitForExit(); + process.StandardOutput.ReadToEnd(); + + LogManager.LogInformation("HideDevice AddBlockedInstanceId: {0}", deviceInstancePath); } + + return true; } - public static void HidePath(string deviceInstancePath) + public static bool HidePath(string deviceInstancePath) { if (string.IsNullOrEmpty(deviceInstancePath)) - return; + return false; try { @@ -191,10 +221,21 @@ public static void HidePath(string deviceInstancePath) } catch { + if (process is null) + return false; + + process.StartInfo.Arguments = $"--dev-hide \"{deviceInstancePath}\""; + process.Start(); + process.WaitForExit(); + process.StandardOutput.ReadToEnd(); + + LogManager.LogInformation("HideDevice AddBlockedInstanceId: {0}", deviceInstancePath); } + + return true; } - public static List GetHidHideDevices() + public static List GetHidHideDevices(string arg = "--dev-all") { try { diff --git a/HandheldCompanion/Misc/JoyShockLibrary.cs b/HandheldCompanion/Misc/JoyShockLibrary.cs index beb931fe5..5c134c18a 100644 --- a/HandheldCompanion/Misc/JoyShockLibrary.cs +++ b/HandheldCompanion/Misc/JoyShockLibrary.cs @@ -102,6 +102,8 @@ public delegate void EventCallback(int handle, JOY_SHOCK_STATE state, JOY_SHOCK_ public static extern void JslDisconnectAndDisposeAll(); [DllImport("JoyShockLibrary")] public static extern bool JslStillConnected(int deviceId); + [DllImport("JoyShockLibrary")] + public static extern void JslDisconnect(int deviceId); [DllImport("JoyShockLibrary", CallingConvention = CallingConvention.Cdecl)] public static extern JOY_SHOCK_STATE JslGetSimpleState(int deviceId); diff --git a/HandheldCompanion/Misc/PnPDetails.cs b/HandheldCompanion/Misc/PnPDetails.cs index 0bcd30082..9f4dc044d 100644 --- a/HandheldCompanion/Misc/PnPDetails.cs +++ b/HandheldCompanion/Misc/PnPDetails.cs @@ -60,31 +60,157 @@ public short GetMI() return -1; } + public string GetEnumerator() + { + PnPDevice device = GetBasePnPDevice(); + if (device is not null) + return device.GetProperty(DevicePropertyKey.Device_EnumeratorName); + + return string.Empty; + } + public UsbPnPDevice GetUsbPnPDevice() { - var pnpDevice = PnPDevice.GetDeviceByInstanceId(baseContainerDeviceInstanceId); - if (pnpDevice is null) + PnPDevice device = GetBasePnPDevice(); + if (device is null) return null; // is this a USB device - var enumerator = pnpDevice.GetProperty(DevicePropertyKey.Device_EnumeratorName); - if (!Equals(enumerator, "USB")) - return null; + string enumerator = GetEnumerator(); + + switch (enumerator) + { + default: + case "BTHENUM": + return null; + case "USB": + break; + } + + return device.ToUsbPnPDevice(); + } + + public PnPDevice GetPnPDevice() + { + try + { + return PnPDevice.GetDeviceByInstanceId(deviceInstanceId); + } + catch { } - return pnpDevice.ToUsbPnPDevice(); + return null; + } + + public PnPDevice GetBasePnPDevice() + { + try + { + return PnPDevice.GetDeviceByInstanceId(baseContainerDeviceInstanceId); + } + catch { } + + return null; } public bool CyclePort() { - var usbDevice = GetUsbPnPDevice(); + UsbPnPDevice device = GetUsbPnPDevice(); - if (usbDevice is null) - return false; + try + { + if (device is not null) + { + device.CyclePort(); + return true; + } + } + catch { } + + return false; + } + + public bool InstallNullDrivers(bool basedevice = true) + { + PnPDevice device; + + switch (basedevice) + { + case true: + device = GetBasePnPDevice(); + break; + case false: + device = GetPnPDevice(); + break; + } + + try + { + if (device is not null) + { + device.InstallNullDriver(); + return true; + } + } + catch { } + + return false; + } + + public bool InstallCustomDriver(string driverName, bool basedevice = true) + { + PnPDevice device; + + switch(basedevice) + { + case true: + device = GetBasePnPDevice(); + break; + case false: + device = GetPnPDevice(); + break; + } + + try + { + if (device is not null) + { + device.InstallCustomDriver(driverName); + return true; + } + } + catch { } + + return false; + } + + public bool Uninstall(bool basedevice = true, bool parent = false) + { + PnPDevice device; + + switch (basedevice) + { + case true: + device = GetBasePnPDevice(); + + if (parent) + { + var parentId = device.GetProperty(DevicePropertyKey.Device_Parent); + device = PnPDevice.GetDeviceByInstanceId(parentId); + } + + break; + case false: + device = GetPnPDevice(); + break; + } try { - usbDevice.CyclePort(); - return true; + if (device is not null) + { + device.Uninstall(); + return true; + } } catch { } diff --git a/HandheldCompanion/Properties/Resources.Designer.cs b/HandheldCompanion/Properties/Resources.Designer.cs index 556e0310d..c6f780fec 100644 --- a/HandheldCompanion/Properties/Resources.Designer.cs +++ b/HandheldCompanion/Properties/Resources.Designer.cs @@ -781,6 +781,33 @@ public static string ControllerPage_VibrationStrengthExpl { } } + /// + /// Looks up a localized string similar to Please switch your controller off and then on again by pressing and holding the Guide button to finalize pairing.. + /// + public static string ControllerPage_XInputControllerWarning { + get { + return ResourceManager.GetString("ControllerPage_XInputControllerWarning", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Device. + /// + public static string DevicePage_Device { + get { + return ResourceManager.GetString("DevicePage_Device", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Power options. + /// + public static string DevicePage_PowerOptions { + get { + return ResourceManager.GetString("DevicePage_PowerOptions", resourceCulture); + } + } + /// /// Looks up a localized string similar to DIRECTIONAL PAD. /// @@ -3627,6 +3654,15 @@ public static string MainWindow_navController { } } + /// + /// Looks up a localized string similar to Device. + /// + public static string MainWindow_navDevice { + get { + return ResourceManager.GetString("MainWindow_navDevice", resourceCulture); + } + } + /// /// Looks up a localized string similar to Hotkeys. /// @@ -6175,15 +6211,6 @@ public static string SettingsPage_OpenAppBackgroundDesc { } } - /// - /// Looks up a localized string similar to Power options. - /// - public static string SettingsPage_PowerOptions { - get { - return ResourceManager.GetString("SettingsPage_PowerOptions", resourceCulture); - } - } - /// /// Looks up a localized string similar to Quicktools backdrop, none, mica, tabbed, acrylic. /// diff --git a/HandheldCompanion/Properties/Resources.de-DE.resx b/HandheldCompanion/Properties/Resources.de-DE.resx index cb594f360..32d2cac13 100644 --- a/HandheldCompanion/Properties/Resources.de-DE.resx +++ b/HandheldCompanion/Properties/Resources.de-DE.resx @@ -1479,7 +1479,7 @@ Wenn die Bewegungseingabe aktiviert ist, verwenden Sie die ausgewählte(n) Taste Unten Links - + Energie-Optionen diff --git a/HandheldCompanion/Properties/Resources.es-ES.resx b/HandheldCompanion/Properties/Resources.es-ES.resx index 122838d1e..ce61f08d0 100644 --- a/HandheldCompanion/Properties/Resources.es-ES.resx +++ b/HandheldCompanion/Properties/Resources.es-ES.resx @@ -1429,7 +1429,7 @@ Fondo de herramientas rápidas, none, mica, tabbed, acrylic - + Opciones de energía diff --git a/HandheldCompanion/Properties/Resources.fr-FR.resx b/HandheldCompanion/Properties/Resources.fr-FR.resx index c64eb6cd9..3f69604b1 100644 --- a/HandheldCompanion/Properties/Resources.fr-FR.resx +++ b/HandheldCompanion/Properties/Resources.fr-FR.resx @@ -1518,7 +1518,7 @@ with motion input enabled, use selected button(s) to disable motion. Bottom left - + Power options diff --git a/HandheldCompanion/Properties/Resources.it-IT.resx b/HandheldCompanion/Properties/Resources.it-IT.resx index e1cc1da42..4cdf4aa09 100644 --- a/HandheldCompanion/Properties/Resources.it-IT.resx +++ b/HandheldCompanion/Properties/Resources.it-IT.resx @@ -1486,7 +1486,7 @@ Bottom left - + Power options diff --git a/HandheldCompanion/Properties/Resources.ja-JP.resx b/HandheldCompanion/Properties/Resources.ja-JP.resx index bef7d2f9a..5ed4fe329 100644 --- a/HandheldCompanion/Properties/Resources.ja-JP.resx +++ b/HandheldCompanion/Properties/Resources.ja-JP.resx @@ -1483,7 +1483,7 @@ Bottom left - + Power options diff --git a/HandheldCompanion/Properties/Resources.pt-BR.resx b/HandheldCompanion/Properties/Resources.pt-BR.resx index 5ac820cb9..48c123cf3 100644 --- a/HandheldCompanion/Properties/Resources.pt-BR.resx +++ b/HandheldCompanion/Properties/Resources.pt-BR.resx @@ -1481,7 +1481,7 @@ com a entrada de movimento ligada, use o(s) botão(ões) selecionado(s) para des Inferior esquerdo - + Opções de energia diff --git a/HandheldCompanion/Properties/Resources.resx b/HandheldCompanion/Properties/Resources.resx index d6f59b9ac..d366498b7 100644 --- a/HandheldCompanion/Properties/Resources.resx +++ b/HandheldCompanion/Properties/Resources.resx @@ -1526,7 +1526,7 @@ with motion input enabled, use selected button(s) to disable motion. Automatically turn HWiNFO off when companion is closed - + Power options @@ -2345,4 +2345,13 @@ with motion input enabled, use selected button(s) to disable motion. Steam Xbox Controller Enhanced Features Driver is installed + + Device + + + Device + + + Please switch your controller off and then on again by pressing and holding the Guide button to finalize pairing. + \ No newline at end of file diff --git a/HandheldCompanion/Properties/Resources.ru-RU.resx b/HandheldCompanion/Properties/Resources.ru-RU.resx index e14329011..7325a9147 100644 --- a/HandheldCompanion/Properties/Resources.ru-RU.resx +++ b/HandheldCompanion/Properties/Resources.ru-RU.resx @@ -2251,7 +2251,7 @@ Not set - + Power options diff --git a/HandheldCompanion/Properties/Resources.zh-CN.resx b/HandheldCompanion/Properties/Resources.zh-CN.resx index 4bca84411..fca65511b 100644 --- a/HandheldCompanion/Properties/Resources.zh-CN.resx +++ b/HandheldCompanion/Properties/Resources.zh-CN.resx @@ -1468,7 +1468,7 @@ 左下 - + 电源选项 diff --git a/HandheldCompanion/Properties/Resources.zh-Hant.resx b/HandheldCompanion/Properties/Resources.zh-Hant.resx index 44e86a42e..621fcef57 100644 --- a/HandheldCompanion/Properties/Resources.zh-Hant.resx +++ b/HandheldCompanion/Properties/Resources.zh-Hant.resx @@ -1468,7 +1468,7 @@ 左下 - + 電源選項 diff --git a/HandheldCompanion/Properties/Settings.Designer.cs b/HandheldCompanion/Properties/Settings.Designer.cs index 5548fe108..6a23d71a3 100644 --- a/HandheldCompanion/Properties/Settings.Designer.cs +++ b/HandheldCompanion/Properties/Settings.Designer.cs @@ -1,20 +1,19 @@ //------------------------------------------------------------------------------ // -// Ce code a été généré par un outil. -// Version du runtime :4.0.30319.42000 +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // -// Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si -// le code est régénéré. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ -using System.Configuration; namespace HandheldCompanion.Properties { - - - [SettingsProvider(typeof(CustomSettingsProvider))] - internal sealed partial class Settings : ApplicationSettingsBase - { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.8.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/HandheldCompanion/Utils/CommonUtils.cs b/HandheldCompanion/Utils/CommonUtils.cs index 1dd155fb2..eec25dd56 100644 --- a/HandheldCompanion/Utils/CommonUtils.cs +++ b/HandheldCompanion/Utils/CommonUtils.cs @@ -50,6 +50,13 @@ public static bool IsAdministrator() return principal.IsInRole(WindowsBuiltInRole.Administrator); } + public static int rgb_to_int(byte led_r, byte led_g, byte led_b) + { + int colour = 0; + colour = (led_r << 16) | (led_g << 8) | led_b; + return colour; + } + public static void OpenUrl(string url) { try diff --git a/HandheldCompanion/Utils/FileUtils.cs b/HandheldCompanion/Utils/FileUtils.cs index 012597303..5d3d31c49 100644 --- a/HandheldCompanion/Utils/FileUtils.cs +++ b/HandheldCompanion/Utils/FileUtils.cs @@ -1,5 +1,4 @@ using System.IO; -using System.IO.Packaging; using System.Security.AccessControl; using System.Security.Principal; diff --git a/HandheldCompanion/Views/Pages/ControllerPage.xaml b/HandheldCompanion/Views/Pages/ControllerPage.xaml index 8d9acf0e3..78c13a65b 100644 --- a/HandheldCompanion/Views/Pages/ControllerPage.xaml +++ b/HandheldCompanion/Views/Pages/ControllerPage.xaml @@ -62,7 +62,7 @@ Visibility="Collapsed"> - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/HandheldCompanion/Views/Pages/DevicePage.xaml.cs b/HandheldCompanion/Views/Pages/DevicePage.xaml.cs new file mode 100644 index 000000000..2da8e4e88 --- /dev/null +++ b/HandheldCompanion/Views/Pages/DevicePage.xaml.cs @@ -0,0 +1,125 @@ +using ColorPicker.Models; +using ColorPicker; +using HandheldCompanion.Devices; +using HandheldCompanion.Managers; +using HandheldCompanion.Misc; +using Inkore.UI.WPF.Modern.Controls; +using System; +using System.Windows; +using Page = System.Windows.Controls.Page; +using System.Windows.Media; + +namespace HandheldCompanion.Views.Pages +{ + /// + /// Interaction logic for DevicePage.xaml + /// + public partial class DevicePage : Page + { + public DevicePage() + { + InitializeComponent(); + + SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; + } + + public DevicePage(string? Tag) : this() + { + this.Tag = Tag; + } + + private void Page_Loaded(object? sender, RoutedEventArgs? e) + { + } + + public void Page_Closed() + { + } + + private void SettingsManager_SettingValueChanged(string? name, object value) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + switch (name) + { + case "ConfigurableTDPOverride": + Toggle_cTDP.IsOn = Convert.ToBoolean(value); + break; + case "ConfigurableTDPOverrideDown": + NumberBox_TDPMin.Value = Convert.ToDouble(value); + break; + case "ConfigurableTDPOverrideUp": + NumberBox_TDPMax.Value = Convert.ToDouble(value); + break; + } + }); + } + + private async void Toggle_cTDP_Toggled(object? sender, RoutedEventArgs? e) + { + if (!IsLoaded) + return; + + if (Toggle_cTDP.IsOn) + { + // todo: localize me ! + var result = Dialog.ShowAsync( + "Warning", + "Altering minimum and maximum CPU power values might cause instabilities. Product warranties may not apply if the processor is operated beyond its specifications. Use at your own risk.", + ContentDialogButton.Primary, "Cancel", Properties.Resources.ProfilesPage_OK); + + await result; // sync call + + switch (result.Result) + { + case ContentDialogResult.Primary: + break; + default: + case ContentDialogResult.None: + // restore previous state + Toggle_cTDP.IsOn = false; + return; + } + } + + SettingsManager.SetProperty("ConfigurableTDPOverride", Toggle_cTDP.IsOn); + SettingsManager.SetProperty("ConfigurableTDPOverrideUp", NumberBox_TDPMax.Value); + SettingsManager.SetProperty("ConfigurableTDPOverrideDown", NumberBox_TDPMin.Value); + } + + private void NumberBox_TDPMax_ValueChanged(NumberBox? sender, NumberBoxValueChangedEventArgs? args) + { + var value = NumberBox_TDPMax.Value; + if (double.IsNaN(value)) + return; + + NumberBox_TDPMin.Maximum = value; + + if (!IsLoaded) + return; + + // update current device cTDP + MainWindow.CurrentDevice.cTDP[1] = value; + + SettingsManager.SetProperty("ConfigurableTDPOverrideUp", value); + } + + private void NumberBox_TDPMin_ValueChanged(NumberBox? sender, NumberBoxValueChangedEventArgs? args) + { + var value = NumberBox_TDPMin.Value; + if (double.IsNaN(value)) + return; + + NumberBox_TDPMax.Minimum = value; + + if (!IsLoaded) + return; + + // update current device cTDP + MainWindow.CurrentDevice.cTDP[0] = value; + + SettingsManager.SetProperty("ConfigurableTDPOverrideDown", value); + } + } +} diff --git a/HandheldCompanion/Views/Pages/SettingsPage.xaml b/HandheldCompanion/Views/Pages/SettingsPage.xaml index 9d6c8f18d..5634f0905 100644 --- a/HandheldCompanion/Views/Pages/SettingsPage.xaml +++ b/HandheldCompanion/Views/Pages/SettingsPage.xaml @@ -552,8 +552,8 @@ - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/HandheldCompanion/Views/Pages/SettingsPage.xaml.cs b/HandheldCompanion/Views/Pages/SettingsPage.xaml.cs index 4044a69d7..0963329db 100644 --- a/HandheldCompanion/Views/Pages/SettingsPage.xaml.cs +++ b/HandheldCompanion/Views/Pages/SettingsPage.xaml.cs @@ -217,15 +217,6 @@ private void SettingsManager_SettingValueChanged(string? name, object value) case "SensorPlacementUpsideDown": Toggle_SensorPlacementUpsideDown.IsOn = Convert.ToBoolean(value); break; - case "ConfigurableTDPOverride": - Toggle_cTDP.IsOn = Convert.ToBoolean(value); - break; - case "ConfigurableTDPOverrideDown": - NumberBox_TDPMin.Value = Convert.ToDouble(value); - break; - case "ConfigurableTDPOverrideUp": - NumberBox_TDPMax.Value = Convert.ToDouble(value); - break; case "CurrentCulture": cB_Language.SelectedItem = new CultureInfo((string)value); @@ -610,72 +601,6 @@ private void SwitchBackdrop(Window targetWindow, int idx) } } - private async void Toggle_cTDP_Toggled(object? sender, RoutedEventArgs? e) - { - if (!IsLoaded) - return; - - if (Toggle_cTDP.IsOn) - { - // todo: localize me ! - var result = Dialog.ShowAsync( - "Warning", - "Altering minimum and maximum CPU power values might cause instabilities. Product warranties may not apply if the processor is operated beyond its specifications. Use at your own risk.", - ContentDialogButton.Primary, "Cancel", Properties.Resources.ProfilesPage_OK); - - await result; // sync call - - switch (result.Result) - { - case ContentDialogResult.Primary: - break; - default: - case ContentDialogResult.None: - // restore previous state - Toggle_cTDP.IsOn = false; - return; - } - } - - SettingsManager.SetProperty("ConfigurableTDPOverride", Toggle_cTDP.IsOn); - SettingsManager.SetProperty("ConfigurableTDPOverrideUp", NumberBox_TDPMax.Value); - SettingsManager.SetProperty("ConfigurableTDPOverrideDown", NumberBox_TDPMin.Value); - } - - private void NumberBox_TDPMax_ValueChanged(NumberBox? sender, NumberBoxValueChangedEventArgs? args) - { - var value = NumberBox_TDPMax.Value; - if (double.IsNaN(value)) - return; - - NumberBox_TDPMin.Maximum = value; - - if (!IsLoaded) - return; - - // update current device cTDP - MainWindow.CurrentDevice.cTDP[1] = value; - - SettingsManager.SetProperty("ConfigurableTDPOverrideUp", value); - } - - private void NumberBox_TDPMin_ValueChanged(NumberBox? sender, NumberBoxValueChangedEventArgs? args) - { - var value = NumberBox_TDPMin.Value; - if (double.IsNaN(value)) - return; - - NumberBox_TDPMax.Minimum = value; - - if (!IsLoaded) - return; - - // update current device cTDP - MainWindow.CurrentDevice.cTDP[0] = value; - - SettingsManager.SetProperty("ConfigurableTDPOverrideDown", value); - } - private void cB_SensorSelection_SelectionChanged(object? sender, SelectionChangedEventArgs? e) { if (cB_SensorSelection.SelectedIndex == -1) diff --git a/HandheldCompanion/Views/SplashScreen.xaml b/HandheldCompanion/Views/SplashScreen.xaml index a7f76ba09..a4f479105 100644 --- a/HandheldCompanion/Views/SplashScreen.xaml +++ b/HandheldCompanion/Views/SplashScreen.xaml @@ -9,7 +9,6 @@ xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" Width="450" Height="450" - ui:ThemeManager.ActualThemeChanged="OverlayWindow_ActualThemeChanged" ui:ThemeManager.IsThemeAware="True" ui:WindowHelper.SystemBackdropType="Mica" ui:WindowHelper.UseAcrylicBackdrop="True" diff --git a/HandheldCompanion/Views/SplashScreen.xaml.cs b/HandheldCompanion/Views/SplashScreen.xaml.cs index e73a29053..a862cd15d 100644 --- a/HandheldCompanion/Views/SplashScreen.xaml.cs +++ b/HandheldCompanion/Views/SplashScreen.xaml.cs @@ -12,10 +12,5 @@ public SplashScreen() { InitializeComponent(); } - - private void OverlayWindow_ActualThemeChanged(object sender, RoutedEventArgs e) - { - // do something - } } } diff --git a/HandheldCompanion/Views/Windows/MainWindow.xaml b/HandheldCompanion/Views/Windows/MainWindow.xaml index 9fbbf5eb2..e0ce78bd0 100644 --- a/HandheldCompanion/Views/Windows/MainWindow.xaml +++ b/HandheldCompanion/Views/Windows/MainWindow.xaml @@ -47,6 +47,18 @@ Tag="ControllerPage"> + + + + + + + + + @@ -57,6 +69,7 @@ Tag="ProfilesPage"> + @@ -67,6 +80,7 @@ Tag="OverlayPage"> + @@ -77,6 +91,7 @@ Tag="HotkeysPage"> + @@ -87,6 +102,7 @@ Tag="AboutPage"> + @@ -97,6 +113,7 @@ Tag="SettingsPage"> + diff --git a/HandheldCompanion/Views/Windows/MainWindow.xaml.cs b/HandheldCompanion/Views/Windows/MainWindow.xaml.cs index b70c90a23..66c3b1622 100644 --- a/HandheldCompanion/Views/Windows/MainWindow.xaml.cs +++ b/HandheldCompanion/Views/Windows/MainWindow.xaml.cs @@ -43,6 +43,7 @@ public partial class MainWindow : GamepadWindow private static readonly Dictionary _pages = new(); public static ControllerPage controllerPage; + public static DevicePage devicePage; public static ProfilesPage profilesPage; public static SettingsPage settingsPage; public static AboutPage aboutPage; @@ -98,7 +99,7 @@ public MainWindow(FileVersionInfo _fileVersionInfo, Assembly CurrentAssembly) var tablets = Tablet.TabletDevices; // get first start - var FirstStart = SettingsManager.GetBoolean("FirstStart"); + bool FirstStart = SettingsManager.GetBoolean("FirstStart"); // define current directory InstallPath = AppDomain.CurrentDomain.BaseDirectory; @@ -184,6 +185,8 @@ public MainWindow(FileVersionInfo _fileVersionInfo, Assembly CurrentAssembly) { splashScreen = new SplashScreen(); splashScreen.Show(); + + SettingsManager.SetProperty("FirstStart", false); } // load manager(s) @@ -381,6 +384,7 @@ private void loadPages() controllerPage = new ControllerPage("controller"); controllerPage.Loaded += ControllerPage_Loaded; + devicePage = new DevicePage("device"); profilesPage = new ProfilesPage("profiles"); settingsPage = new SettingsPage("settings"); aboutPage = new AboutPage("about"); @@ -390,6 +394,7 @@ private void loadPages() // store pages _pages.Add("ControllerPage", controllerPage); + _pages.Add("DevicePage", devicePage); _pages.Add("ProfilesPage", profilesPage); _pages.Add("AboutPage", aboutPage); _pages.Add("OverlayPage", overlayPage);