using System.Diagnostics; using Toak.Core; using Toak.Core.Interfaces; namespace Toak.IO; public class TextInjector : ITextInjector { private readonly INotifications _notifications; public TextInjector(INotifications notifications) { _notifications = notifications; } public Task InjectTextAsync(string text, string backend = "xdotool") { Logger.LogDebug($"Injecting text: '{text}' with {backend}"); if (string.IsNullOrWhiteSpace(text)) return Task.CompletedTask; try { ProcessStartInfo pInfo; if (backend.ToLowerInvariant() == "wtype") { Logger.LogDebug($"Injecting text using wtype..."); pInfo = new ProcessStartInfo { FileName = Toak.Core.Constants.Commands.TypeWayland, Arguments = $"-d {Toak.Core.Constants.Defaults.DefaultTypeDelayMs} \"{text.Replace("\"", "\\\"")}\"", UseShellExecute = false, CreateNoWindow = true }; } else if (backend.ToLowerInvariant() == "ydotool") { Logger.LogDebug($"Injecting text using ydotool..."); pInfo = new ProcessStartInfo { FileName = Toak.Core.Constants.Commands.TypeYdotool, Arguments = $"type \"{text.Replace("\"", "\\\"")}\"", UseShellExecute = false, CreateNoWindow = true }; } else // xdotool { Logger.LogDebug($"Injecting text using xdotool..."); pInfo = new ProcessStartInfo { FileName = Toak.Core.Constants.Commands.TypeX11, Arguments = $"type --clearmodifiers --delay {Toak.Core.Constants.Defaults.DefaultTypeDelayMs} \"{text.Replace("\"", "\\\"")}\"", UseShellExecute = false, CreateNoWindow = true }; } var process = Process.Start(pInfo); process?.WaitForExit(); } catch (Exception ex) { Console.WriteLine($"[TextInjector] Error injecting text: {ex.Message}"); _notifications.Notify("Injection Error", "Could not type text into window."); } return Task.CompletedTask; } public async Task InjectStreamAsync(IAsyncEnumerable tokenStream, string backend) { string fullText = string.Empty; try { ProcessStartInfo pInfo; if (backend.ToLowerInvariant() == "wtype") { Logger.LogDebug($"Setting up stream injection using wtype..."); pInfo = new ProcessStartInfo { FileName = Toak.Core.Constants.Commands.TypeWayland, Arguments = $"-d {Toak.Core.Constants.Defaults.DefaultTypeDelayMs} -", UseShellExecute = false, CreateNoWindow = true, RedirectStandardInput = true }; } else if (backend.ToLowerInvariant() == "ydotool") { Logger.LogDebug($"Setting up stream injection using ydotool (chunked)..."); await foreach (var token in tokenStream) { Logger.LogDebug($"Injecting token: '{token}'"); fullText += token; var chunkInfo = new ProcessStartInfo { FileName = Toak.Core.Constants.Commands.TypeYdotool, Arguments = $"type \"{token.Replace("\"", "\\\"")}\"", UseShellExecute = false, CreateNoWindow = true }; var chunkP = Process.Start(chunkInfo); if (chunkP != null) await chunkP.WaitForExitAsync(); } return fullText; } else // xdotool { Logger.LogDebug($"Setting up stream injection using xdotool..."); pInfo = new ProcessStartInfo { FileName = Toak.Core.Constants.Commands.TypeX11, Arguments = $"type --clearmodifiers --delay {Toak.Core.Constants.Defaults.DefaultTypeDelayMs} --file -", UseShellExecute = false, CreateNoWindow = true, RedirectStandardInput = true }; } using var process = Process.Start(pInfo); if (process == null) return string.Empty; Logger.LogDebug("Started stream injection process, waiting for tokens..."); await foreach (var token in tokenStream) { Logger.LogDebug($"Injecting token: '{token}'"); fullText += token; await process.StandardInput.WriteAsync(token); await process.StandardInput.FlushAsync(); } Logger.LogDebug("Stream injection complete. Closing standard input."); process.StandardInput.Close(); await process.WaitForExitAsync(); } catch (Exception ex) { Console.WriteLine($"[TextInjector] Error injecting text stream: {ex.Message}"); _notifications.Notify("Injection Error", "Could not type text stream into window."); } return fullText; } }