using Godot;
using System;
using System.Threading;
using System.Diagnostics;
namespace AlignmentHelper
{
public partial class AlignmentHelperProcess : Node
{
private static AlignmentHelperProcess _instance;
public static AlignmentHelperProcess Instance
{
get
{
if (_instance == null)
{
_instance = new AlignmentHelperProcess();
}
return _instance;
}
}
public System.Diagnostics.Process helperProcess;
private Thread _steamvrMonitor;
private CancellationTokenSource loopToken;
private const string targetProcess = "vrserver";
private const string alignmentProcess = "ETCalOverlay";
///
/// Start search for SteamVR process
///
public void StartAlignmentWatch()
{
GD.PrintErr($"Starting alignment helper watch");
if (_steamvrMonitor != null && loopToken != null)
{
loopToken.Cancel();
_steamvrMonitor.Join();
}
loopToken = new CancellationTokenSource();
_steamvrMonitor = new Thread(() => { FindSteamVR(loopToken.Token); });
_steamvrMonitor.Start();
}
public void EndAlignmentWatch()
{
GD.PrintErr($"Ending alignment helper watch");
if (loopToken != null)
{
loopToken.Cancel();
}
CloseAlignmentProcess();
}
///
/// Attempt to find one of SteamVR's processes
/// If the alignment helper is enabled, start or end the app if the SteamVR process is running or not
///
private void FindSteamVR(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
try
{
if (Process.GetProcessesByName(targetProcess).Length > 0)
{
if (helperProcess == null) StartAlignmentProcess();
}
else if (helperProcess != null)
{
CloseAlignmentProcess();
}
}
catch (Exception ex)
{
GD.PrintErr($"Error in alignment helper watch loop: {ex.Message}");
}
token.WaitHandle.WaitOne(4000);
}
}
///
/// Starts alignment helper and tracks its process.
///
/// True if the process started
public bool StartAlignmentProcess()
{
if (helperProcess != null && !helperProcess.HasExited)
{
GD.Print("Will not launch the alignment helper as it is already running");
return false;
}
// In the case no process handle is valid but the app is still running, it should be closed
foreach (var process in Process.GetProcessesByName(alignmentProcess))
{
try
{
// Attempt graceful close, otherwise kill
if (!process.CloseMainWindow())
{
process.Kill();
}
break;
}
catch
{
}
}
var startInfo = new System.Diagnostics.ProcessStartInfo
{
FileName = "ETCalOverlay.exe",
UseShellExecute = false, // Changed to true to run in a separate window
CreateNoWindow = false, // Allow window to be shown
WorkingDirectory = System.IO.Path.GetDirectoryName(OS.GetExecutablePath())
};
try
{
helperProcess = new System.Diagnostics.Process
{
StartInfo = startInfo,
EnableRaisingEvents = true
};
helperProcess.Exited += (sender, e) =>
{
helperProcess.Dispose();
helperProcess = null;
};
helperProcess.Start();
GD.Print("Alignment helper launched successfully");
return true;
}
catch (Exception ex)
{
GD.PrintErr($"Failed to start alignment helper: {ex.Message}");
return false;
}
}
public bool CloseAlignmentProcess()
{
try
{
if (helperProcess != null && !helperProcess.HasExited)
{
helperProcess.Kill();
helperProcess.Dispose();
helperProcess = null;
GD.Print("Alignment helper closed");
}
return true;
}
catch (Exception ex)
{
GD.PrintErr($"Failed to close alignment helper: {ex.Message}");
return false;
}
}
}
}