Skip to content

Commit

Permalink
improve controller manager and exception dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
Valkirie committed Nov 23, 2023
1 parent c194029 commit 28f212f
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 55 deletions.
99 changes: 53 additions & 46 deletions HandheldCompanion/Managers/ControllerManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static class ControllerManager

static ControllerManager()
{
Watchdog = new(3000);
Watchdog = new(4000);
Watchdog.Elapsed += Watchdog_Elapsed;
Watchdog.Enabled = false;
}
Expand Down Expand Up @@ -564,15 +564,9 @@ private static void Watchdog_Elapsed(object? sender, ElapsedEventArgs e)
{
lock (updateLock)
{
// disable that setting if we failed three times
if (ControllerManagementAttempts == 3)
{
Working?.Invoke(2);
Watchdog.Stop();
SettingsManager.SetProperty("ControllerManagement", false);
}

// monitoring unexpected slot changes
Dictionary<byte, bool> UserIndexes = new();
bool XInputDrunk = false;
foreach (XInputController xInputController in Controllers.Values.Where(c => c.Details is not null && c.Details.isXInput))
{
byte UserIndex = DeviceManager.GetXInputIndexAsync(xInputController.Details.baseContainerDevicePath);
Expand All @@ -581,70 +575,80 @@ private static void Watchdog_Elapsed(object? sender, ElapsedEventArgs e)
if (UserIndex == byte.MaxValue)
continue;

// that's not possible, XInput is drunk
if (UserIndexes.ContainsKey(UserIndex))
XInputDrunk = true;

UserIndexes.Add(UserIndex, true);
xInputController.AttachController(UserIndex);
}

if (XInputDrunk)
{
foreach (XInputController xInputController in Controllers.Values.Where(c => c.Details is not null && c.Details.isXInput))
xInputController.AttachController(byte.MaxValue);
}

if (HasVirtualController())
{
// check if it is first controller
IController controller = GetControllerFromSlot(UserIndex.One, false);
if (controller is null)
{
Working?.Invoke(0);
ControllerManagementAttempts++;

// suspend virtual controller
VirtualManager.Suspend();

// suspend all physical controllers
// PnPUtil.StartPnPUtil(@"/delete-driver C:\Windows\INF\xusb22.inf /uninstall /force");
foreach (XInputController xInputController in GetPhysicalControllers())
SuspendController(xInputController.Details.baseContainerDeviceInstanceId);

// restore VirtualManager ViGEmClient
VirtualManager.vClient = new();

// fill all slots with virtual controllers
for (UserIndex userIndex = UserIndex.One; userIndex <= UserIndex.Four; userIndex++)
// disable that setting if we failed three times
if (ControllerManagementAttempts == 3)
{
// create a virtual controller
IXbox360Controller tempController = VirtualManager.vClient.CreateXbox360Controller(VirtualManager.FakeVendorId, (ushort)new Random().Next(ushort.MinValue, ushort.MaxValue));
placeholders.Add(tempController);
}
// resume all physical controllers
StringCollection deviceInstanceIds = SettingsManager.GetStringCollection("SuspendedControllers");
if (deviceInstanceIds is not null && deviceInstanceIds.Count != 0)
ResumeControllers();

// connect all virtual controllers
foreach (IXbox360Controller placeholder in placeholders)
placeholder.Connect();
ControllerManagementSuccess = false;
ControllerManagementAttempts = 0;
Working?.Invoke(2);
Watchdog.Stop();
}
else
{
Working?.Invoke(0);
ControllerManagementSuccess = false;
ControllerManagementAttempts++;

Thread.Sleep(2000);
// suspend virtual controller
VirtualManager.Suspend();

// disconnect all virtual controllers
foreach (IXbox360Controller placeholder in placeholders)
placeholder.Disconnect();
// suspend all physical controllers
foreach (XInputController xInputController in GetPhysicalControllers())
SuspendController(xInputController.Details.baseContainerDeviceInstanceId);

Thread.Sleep(2000);
// resume virtual controller
VirtualManager.Resume();

// clear array
placeholders.Clear();
// resume all physical controllers
StringCollection deviceInstanceIds = SettingsManager.GetStringCollection("SuspendedControllers");
if (deviceInstanceIds is not null && deviceInstanceIds.Count != 0)
ResumeControllers();

// resume virtual controller
VirtualManager.Resume();
// suspend and resume virtual controller
VirtualManager.Suspend();
VirtualManager.Resume();
}
}
else
{
// resume all physical controllers
// PnPUtil.StartPnPUtil(@"/add-driver C:\Windows\INF\xusb22.inf /install");
StringCollection deviceInstanceIds = SettingsManager.GetStringCollection("SuspendedControllers");
if (deviceInstanceIds is not null && deviceInstanceIds.Count != 0)
ResumeControllers();

// give us one extra loop to make sure we're good
if (!ControllerManagementSuccess)
{
ControllerManagementSuccess = true;
Working?.Invoke(1);
}
else
ControllerManagementAttempts = 0;

Working?.Invoke(1);
}
}
}
Expand Down Expand Up @@ -822,6 +826,7 @@ public static void SetTargetController(string baseContainerDeviceInstanceId, boo

public static bool SuspendController(string baseContainerDeviceInstanceId)
{
// PnPUtil.StartPnPUtil(@"/delete-driver C:\Windows\INF\xusb22.inf /uninstall /force");
StringCollection deviceInstanceIds = SettingsManager.GetStringCollection("SuspendedControllers");

if (deviceInstanceIds is null)
Expand All @@ -844,8 +849,10 @@ public static bool SuspendController(string baseContainerDeviceInstanceId)
{
case "USB":
if (pnPDriver is not null)
{
pnPDevice.InstallNullDriver(out bool rebootRequired);
usbPnPDevice.CyclePort();
usbPnPDevice.CyclePort();
}

if (!deviceInstanceIds.Contains(baseContainerDeviceInstanceId))
deviceInstanceIds.Add(baseContainerDeviceInstanceId);
Expand All @@ -863,7 +870,7 @@ public static bool SuspendController(string baseContainerDeviceInstanceId)

public static bool ResumeControllers()
{
// disable physical controllers when shutting down to ensure we can give the first order to virtual controller on next boot
// PnPUtil.StartPnPUtil(@"/add-driver C:\Windows\INF\xusb22.inf /install");
StringCollection deviceInstanceIds = SettingsManager.GetStringCollection("SuspendedControllers");

if (deviceInstanceIds is null || deviceInstanceIds.Count == 0)
Expand Down
5 changes: 5 additions & 0 deletions HandheldCompanion/Managers/VirtualManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,19 @@ public static void SetControllerMode(HIDmode mode)

// disconnect current virtual controller
if (vTarget is not null)
{
vTarget.Disconnect();
vTarget.Dispose();
vTarget = null;
}

switch (mode)
{
default:
case HIDmode.NoController:
if (vTarget is not null)
{
vTarget.Disconnect();
vTarget.Dispose();
vTarget = null;
}
Expand Down
60 changes: 53 additions & 7 deletions HandheldCompanion/Views/Pages/ControllerPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@
using HandheldCompanion.Platforms;
using HandheldCompanion.Utils;
using Inkore.UI.WPF.Modern.Controls;
using Microsoft.Win32.TaskScheduler;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Page = System.Windows.Controls.Page;
using Task = System.Threading.Tasks.Task;

namespace HandheldCompanion.Views.Pages;

Expand Down Expand Up @@ -192,12 +197,11 @@ private void ControllerManager_ControllerSelected(IController Controller)

private void ControllerManager_Working(int status)
{
// status: 0:wip, 1:sucess, 2:failed

// UI thread (async)
Application.Current.Dispatcher.BeginInvoke(() =>
Application.Current.Dispatcher.BeginInvoke(async () =>
{
switch(status)
// status: 0:wip, 1:sucess, 2:failed
switch (status)
{
case 0:
ControllerLoading.Visibility = Visibility.Visible;
Expand All @@ -216,11 +220,53 @@ private void ControllerManager_Working(int status)
if (status == 2)
{
// todo: translate me
_ = Dialog.ShowAsync($"{Properties.Resources.SettingsPage_UpdateWarning}",
var result = Dialog.ShowAsync(
$"{Properties.Resources.SettingsPage_UpdateWarning}",
$"We've failed to reorder your controllers. For maximum compatibility, we encourage you to restart HandheldCompanion",
ContentDialogButton.Primary, string.Empty, $"{Properties.Resources.ProfilesPage_OK}");
ContentDialogButton.Close,
"Restart application",
"Close");

await result; // sync call

switch (result.Result)
{
default:
case ContentDialogResult.Primary:
Toggle_ControllerManagement.IsOn = false;
break;
case ContentDialogResult.None:
{
// The command to schedule the executable with Task Scheduler
string exePath = Assembly.GetExecutingAssembly().Location.Replace("dll", "exe");
string exeArgs = "";

// Create a task name
string taskName = "RestartHC";

// Create a task definition using the TaskService class
var td = TaskService.Instance.NewTask();
td.RegistrationInfo.Description = "Run RestartHC";
td.Principal.RunLevel = TaskRunLevel.Highest;

// Create a trigger that runs the task once after 3 seconds
var trigger = new TimeTrigger();
trigger.StartBoundary = DateTime.Now.AddSeconds(3);
td.Triggers.Add(trigger);

// Create an action that executes the executable
var action = new ExecAction(exePath, exeArgs);
td.Actions.Add(action);

// Register the task with the Task Scheduler
TaskService.Instance.RootFolder.RegisterTaskDefinition(taskName, td);

Environment.Exit(0);
}
break;
}
}
});
});
}

// A function that returns a random phrase from a list of phrases
Expand Down
2 changes: 0 additions & 2 deletions HandheldCompanion/Views/Windows/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -705,8 +705,6 @@ private void Window_Closed(object sender, EventArgs e)
Environment.Exit(0);
}

bool CloseOverride = false;

private async void Window_Closing(object sender, CancelEventArgs e)
{
// position and size settings
Expand Down

0 comments on commit 28f212f

Please sign in to comment.