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; } } } }