diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7943e90..830bbb6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -127,11 +127,6 @@ jobs: UPDATE_CHANNEL: ${{ env.UPDATE_CHANNEL }} run: .\build\build-complete.ps1 -Configuration Debug -ExeOnly -UpdateChannel $env:UPDATE_CHANNEL - - name: Clean Debug output - run: | - $outDir = "Windows-Hinting\bin\Debug\net10.0-windows" - Remove-Item "$outDir\xunit.*" -Force -ErrorAction SilentlyContinue - - name: Upload Debug artifact uses: actions/upload-artifact@v7 with: diff --git a/Windows-Hinting/Examples/HintOverlayClientExamples.cs b/Windows-Hinting/Examples/HintOverlayClientExamples.cs deleted file mode 100644 index f1a9637..0000000 --- a/Windows-Hinting/Examples/HintOverlayClientExamples.cs +++ /dev/null @@ -1,238 +0,0 @@ -using System; -using WindowsHinting.NamedPipeClient; - -namespace WindowsHinting.Examples -{ - /// - /// Example usage of the HintOverlayClient for controlling Windows-Hinting from external applications. - /// - internal static class HintOverlayClientExamples - { - /// - /// Basic example: Toggle hints on/off - /// - public static void BasicToggleExample() - { - using var client = new HintOverlayClient(); - - if (client.Toggle()) - { - Console.WriteLine("Successfully toggled Windows-Hinting"); - } - else - { - Console.WriteLine("Failed to connect to Windows-Hinting"); - } - } - - /// - /// Example: Select a specific hint and activate it - /// - public static void SelectHintExample() - { - using var client = new HintOverlayClient(); - - // First, ensure hints are active - client.Toggle(); - - // Wait a moment for hints to load - System.Threading.Thread.Sleep(500); - - // Select and activate hint "A" - if (client.SelectHint("A")) - { - Console.WriteLine("Successfully selected hint A"); - } - else - { - Console.WriteLine("Failed to select hint A"); - } - } - - /// - /// Example: Sequential hint selection (simulating user typing) - /// - public static void SequentialSelectionExample() - { - using var client = new HintOverlayClient(); - - // Activate hints - client.Toggle(); - System.Threading.Thread.Sleep(500); - - // Select hint with label "AB" - if (client.SelectHint("AB")) - { - Console.WriteLine("Selected hint AB"); - } - } - - /// - /// Example: Toggle with automatic retry handling - /// The client automatically handles connection retries - /// - public static void AutomaticRetryExample() - { - using var client = new HintOverlayClient(); - - // This will work even if Windows-Hinting is starting up - // The client will retry for up to 5 seconds automatically - bool success = client.Toggle(); - - if (success) - { - Console.WriteLine("Command sent (Windows-Hinting may have been starting)"); - } - else - { - Console.WriteLine("Failed after 5 seconds of retrying"); - } - } - - /// - /// Example: Deactivate hints - /// - public static void DeactivateExample() - { - using var client = new HintOverlayClient(); - - if (client.Deactivate()) - { - Console.WriteLine("Successfully deactivated Windows-Hinting"); - } - } - - /// - /// Example: Error handling - /// - public static void ErrorHandlingExample() - { - using var client = new HintOverlayClient(); - - try - { - if (!client.SelectHint("XYZ")) - { - Console.WriteLine("Failed to send command - Windows-Hinting not responding?"); - } - else - { - Console.WriteLine("Command sent - check Windows-Hinting logs for result"); - } - } - catch (ArgumentException ex) - { - Console.WriteLine($"Invalid argument: {ex.Message}"); - } - } - - /// - /// Example: Integration with keyboard shortcuts - /// Could be used by a global hotkey handler to control Windows-Hinting - /// - public static void KeyboardIntegrationExample() - { - using var client = new HintOverlayClient(); - - // Hypothetical: You have a global hotkey handler that calls this - // when user presses Ctrl+Shift+H to toggle hints - var success = client.Toggle(); - - // You could log the result - if (!success) - { - Console.WriteLine("Warning: Unable to toggle Windows-Hinting"); - } - } - - /// - /// Example: UI Control Integration - /// You could have a UI element that controls Windows-Hinting - /// - public static void UIControlExample() - { - using var client = new HintOverlayClient(); - - // Example of button click handler - // When user clicks "Toggle Hints" button - Console.WriteLine("Toggle Hints button clicked"); - - if (client.Toggle()) - { - // Update UI to show success - Console.WriteLine("Hints toggled successfully"); - } - else - { - // Show error to user - Console.WriteLine("Could not reach Windows-Hinting service"); - } - } - - /// - /// Example: Automated testing or scripting - /// - public static void AutomationExample() - { - using var client = new HintOverlayClient(); - - // Test 1: Toggle ON - client.Toggle(); - System.Threading.Thread.Sleep(1000); - - // Test 2: Select hint - client.SelectHint("A"); - System.Threading.Thread.Sleep(1000); - - // Test 3: Toggle OFF - client.Deactivate(); - - Console.WriteLine("Automation test completed"); - } - - /// - /// Example: Select a hint with a specific click action - /// Actions: "LEFT" (left click), "RIGHT" (right click), "DOUBLE" (double click), - /// "CTRL" (ctrl+click), "SHIFT" (shift+click), "MOVE" (mouse move), or null for default activation - /// - public static void ClickActionExample() - { - using var client = new HintOverlayClient(); - - // First, ensure hints are active - client.Toggle(); - System.Threading.Thread.Sleep(500); - - // Default activation (uses UI Automation invoke pattern) - client.SelectHint("A"); - - // Or equivalently - client.SelectHint("A", null); - - // Left click the element - client.Toggle(); - System.Threading.Thread.Sleep(500); - client.SelectHint("A", "LEFT"); - - // Right click the element (e.g., to open context menu) - client.Toggle(); - System.Threading.Thread.Sleep(500); - client.SelectHint("B", "RIGHT"); - - // Double click the element - client.Toggle(); - System.Threading.Thread.Sleep(500); - client.SelectHint("C", "DOUBLE"); - - // Ctrl+click the element (e.g., to open in new tab or multi-select) - client.Toggle(); - System.Threading.Thread.Sleep(500); - client.SelectHint("D", "CTRL"); - - // Shift+click the element (e.g., to extend selection) - client.Toggle(); - System.Threading.Thread.Sleep(500); - client.SelectHint("E", "SHIFT"); - } - } -} diff --git a/Windows-Hinting/HintController.cs b/Windows-Hinting/HintController.cs index a73e78b..744c390 100644 --- a/Windows-Hinting/HintController.cs +++ b/Windows-Hinting/HintController.cs @@ -28,7 +28,6 @@ internal sealed class HintController : IDisposable private readonly HintInputHandler _inputHandler; private readonly TrayIconManager _trayIcon; private readonly ElementActivatorChain _activatorChain; - //private readonly NamedPipeService _namedPipeService; private readonly WindowRuleRegistry _ruleRegistry; private readonly MouseClickService _mouseClickService; private readonly StartupService _startupService; @@ -59,7 +58,6 @@ public HintController( HintStateManager stateManager, HintInputHandler inputHandler, ElementActivatorChain activatorChain, - //NamedPipeService namedPipeService, MouseClickService mouseClickService, StartupService startupService, UpdateService updateService) @@ -82,7 +80,6 @@ public HintController( _stateManager = stateManager ?? throw new ArgumentNullException(nameof(stateManager)); _inputHandler = inputHandler ?? throw new ArgumentNullException(nameof(inputHandler)); _activatorChain = activatorChain ?? throw new ArgumentNullException(nameof(activatorChain)); - //_namedPipeService = namedPipeService ?? throw new ArgumentNullException(nameof(namedPipeService)); _mouseClickService = mouseClickService ?? throw new ArgumentNullException(nameof(mouseClickService)); _startupService = startupService ?? throw new ArgumentNullException(nameof(startupService)); _updateService = updateService ?? throw new ArgumentNullException(nameof(updateService)); @@ -130,12 +127,6 @@ public HintController( _foregroundWindowHookService.ForegroundWindowChanged += OnForegroundWindowChanged; - //_namedPipeService.CommandReceived += OnNamedPipeCommandReceived; - - // Start named pipe service - // _logger.Debug("Starting named pipe service"); - //_namedPipeService.Start(); - // Kick off the auto-update background loop (no-op if disabled in prefs). _updateService.Initialize(); @@ -173,71 +164,6 @@ private void OnDisplaySettingsChanged(object? sender, EventArgs e) _overlay.RebuildOverlays(); } - private void OnNamedPipeCommandReceived(object? sender, NamedPipeCommand command) - { - _logger.Debug($"Processing named pipe command: {command.CommandType}"); - - switch (command.CommandType) - { - case CommandType.Toggle: - Toggle(); - break; - - case CommandType.ToggleTaskbar: - ToggleTaskbar(); - break; - - case CommandType.Select: - if (!string.IsNullOrEmpty(command.HintLabel)) - { - SelectHintByLabel(command.HintLabel, command.Action); - } - break; - - case CommandType.Deactivate: - _stateManager.Deactivate(); - break; - } - } - - private void SelectHintByLabel(string label, ClickAction action = ClickAction.Default) - { - _logger.Info($"Attempting to select hint with label: {label}, action: {action}"); - - var hint = _stateManager.CurrentHints.FirstOrDefault(h => - h.Label.Equals(label, StringComparison.OrdinalIgnoreCase)); - - if (hint == null) - { - _logger.Warning($"Hint with label '{label}' not found"); - return; - } - - _logger.Info($"Activating hint: {hint.Label}, action: {action}"); - - try - { - if (action == ClickAction.Default) - { - _activatorChain.TryActivate(hint.Element); - } - else - { - _mouseClickService.PerformClick(hint.Rect, action); - } - } - catch (Exception ex) - { - _logger.Error($"Error activating hint '{label}'", ex); - } - finally - { - // Hide hints after activation - _logger.Debug("Deactivating hints after direct selection"); - _stateManager.Deactivate(); - } - } - public void Toggle() { using (PerformanceMetrics.Start("Toggle", _logger, LogLevel.Debug)) diff --git a/Windows-Hinting/NamedPipeClient/HintOverlayClient.cs b/Windows-Hinting/NamedPipeClient/HintOverlayClient.cs deleted file mode 100644 index f951914..0000000 --- a/Windows-Hinting/NamedPipeClient/HintOverlayClient.cs +++ /dev/null @@ -1,131 +0,0 @@ -using System; -using System.IO.Pipes; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace WindowsHinting.NamedPipeClient -{ - /// - /// Client for communicating with Windows-Hinting via named pipes. - /// This client handles connection retries to support order-independent execution. - /// - public sealed class HintOverlayClient : IDisposable - { - private const string PipeName = "WindowsHinting_Pipe"; - private const int ConnectionTimeoutMs = 5000; - private const int RetryDelayMs = 100; - private const int MaxRetries = 50; - private bool _disposed; - - /// - /// Toggles the hint overlay on or off. - /// - /// True if the command was sent successfully, false otherwise. - public bool Toggle() - { - return SendCommand("TOGGLE"); - } - - /// - /// Selects a hint by its label and activates the associated element using the default action. - /// - /// The label of the hint to select (e.g., "A", "B", "AB", etc.) - /// True if the command was sent successfully, false otherwise. - public bool SelectHint(string hintLabel) - { - if (string.IsNullOrWhiteSpace(hintLabel)) - throw new ArgumentException("Hint label cannot be null or empty", nameof(hintLabel)); - - return SendCommand($"SELECT {hintLabel}"); - } - - /// - /// Selects a hint by its label and performs the specified click action. - /// - /// The label of the hint to select (e.g., "A", "B", "AB", etc.) - /// The click action to perform: "LEFT", "RIGHT", "DOUBLE", "MOVE", "CTRL", "SHIFT", or null/empty for default activation. - /// True if the command was sent successfully, false otherwise. - public bool SelectHint(string hintLabel, string? action) - { - if (string.IsNullOrWhiteSpace(hintLabel)) - throw new ArgumentException("Hint label cannot be null or empty", nameof(hintLabel)); - - if (string.IsNullOrWhiteSpace(action)) - return SendCommand($"SELECT {hintLabel}"); - - return SendCommand($"SELECT {hintLabel} {action}"); - } - - /// - /// Deactivates the hint overlay. - /// - /// True if the command was sent successfully, false otherwise. - public bool Deactivate() - { - return SendCommand("DEACTIVATE"); - } - - /// - /// Toggles the taskbar-only hint mode on or off. - /// - /// True if the command was sent successfully, false otherwise. - public bool ToggleTaskbar() - { - return SendCommand("TOGGLETASKBAR"); - } - - private bool SendCommand(string command) - { - return SendCommandAsync(command).GetAwaiter().GetResult(); - } - - private async Task SendCommandAsync(string command) - { - int retryCount = 0; - - while (retryCount < MaxRetries) - { - try - { - using (var pipeClient = new NamedPipeClientStream(".", PipeName, PipeDirection.Out)) - { - pipeClient.Connect(ConnectionTimeoutMs); - - using (var writer = new StreamWriter(pipeClient, Encoding.UTF8)) - { - await writer.WriteLineAsync(command); - await writer.FlushAsync(); - } - - return true; - } - } - catch (TimeoutException) - { - // Server not ready yet, retry - retryCount++; - if (retryCount < MaxRetries) - { - await Task.Delay(RetryDelayMs); - } - } - catch (Exception) - { - // Other errors, stop retrying - return false; - } - } - - return false; - } - - public void Dispose() - { - if (_disposed) - return; - - _disposed = true; - } - } -} diff --git a/Windows-Hinting/ServiceCollectionExtensions.cs b/Windows-Hinting/ServiceCollectionExtensions.cs index dbe447d..5039f86 100644 --- a/Windows-Hinting/ServiceCollectionExtensions.cs +++ b/Windows-Hinting/ServiceCollectionExtensions.cs @@ -40,7 +40,6 @@ public static IServiceCollection AddHintOverlayServices(this IServiceCollection services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - //services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/Windows-Hinting/Services/NamedPipeService.cs b/Windows-Hinting/Services/NamedPipeService.cs deleted file mode 100644 index c380fd1..0000000 --- a/Windows-Hinting/Services/NamedPipeService.cs +++ /dev/null @@ -1,218 +0,0 @@ -using System; -using System.IO; -using System.IO.Pipes; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using WindowsHinting.Logging; -using WindowsHinting.Models; - -namespace WindowsHinting.Services -{ - internal sealed class NamedPipeService : IDisposable - { - private const string PipeName = "WindowsHinting_Pipe"; - private const int MaxConnections = 10; - private readonly ILogger _logger; - private NamedPipeServerStream? _pipeServer; - private CancellationTokenSource? _cancellationTokenSource; - private Task? _listenerTask; - private bool _disposed; - - public event EventHandler? CommandReceived; - - public NamedPipeService(ILogger logger) - { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } - - public void Start() - { - if (_cancellationTokenSource != null) - { - _logger.Warning("Named pipe service already started"); - return; - } - - _logger.Info($"Starting named pipe server: {PipeName}"); - _cancellationTokenSource = new CancellationTokenSource(); - _listenerTask = ListenForConnectionsAsync(_cancellationTokenSource.Token); - } - - public void Stop() - { - _logger.Info("Stopping named pipe service"); - _cancellationTokenSource?.Cancel(); - try - { - _listenerTask?.Wait(TimeSpan.FromSeconds(5)); - } - catch (OperationCanceledException) - { - // Expected - } - _pipeServer?.Dispose(); - _pipeServer = null; - } - - private async Task ListenForConnectionsAsync(CancellationToken cancellationToken) - { - try - { - while (!cancellationToken.IsCancellationRequested) - { - _pipeServer = new NamedPipeServerStream( - PipeName, - PipeDirection.In, - MaxConnections, - PipeTransmissionMode.Byte, - PipeOptions.Asynchronous); - - try - { - _logger.Debug("Waiting for named pipe connection..."); - await _pipeServer.WaitForConnectionAsync(cancellationToken); - _logger.Debug("Named pipe client connected"); - - // Handle the client in a separate task - _ = HandleClientAsync(_pipeServer, cancellationToken); - - // Create a new server for the next connection - _pipeServer = null; - } - catch (OperationCanceledException) - { - throw; - } - catch (Exception ex) - { - _logger.Error($"Error in named pipe listener: {ex.Message}", ex); - } - } - } - catch (OperationCanceledException) - { - _logger.Debug("Named pipe listener cancelled"); - } - catch (Exception ex) - { - _logger.Error($"Fatal error in named pipe listener: {ex.Message}", ex); - } - } - - private async Task HandleClientAsync(NamedPipeServerStream pipeServer, CancellationToken cancellationToken) - { - try - { - using (var reader = new StreamReader(pipeServer, Encoding.UTF8)) - { - string? line; - while ((line = await reader.ReadLineAsync(cancellationToken)) != null) - { - if (string.IsNullOrWhiteSpace(line)) - continue; - - _logger.Debug($"Received named pipe command: {line}"); - - var command = ParseCommand(line); - if (command != null) - { - CommandReceived?.Invoke(this, command); - } - } - } - } - catch (OperationCanceledException) - { - _logger.Debug("Client handler cancelled"); - } - catch (Exception ex) - { - _logger.Error($"Error handling named pipe client: {ex.Message}", ex); - } - finally - { - try - { - pipeServer.Close(); - } - catch { } - } - } - - private NamedPipeCommand? ParseCommand(string line) - { - try - { - var parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries); - if (parts.Length == 0) - return null; - - string command = parts[0].ToUpperInvariant(); - - return command switch - { - "TOGGLE" => new NamedPipeCommand { CommandType = CommandType.Toggle }, - "TOGGLETASKBAR" => new NamedPipeCommand { CommandType = CommandType.ToggleTaskbar }, - "SELECT" => parts.Length > 1 ? new NamedPipeCommand - { - CommandType = CommandType.Select, - HintLabel = parts[1], - Action = ParseClickAction(parts.Length > 2 ? parts[2] : null) - } : null, - "DEACTIVATE" => new NamedPipeCommand { CommandType = CommandType.Deactivate }, - _ => null - }; - } - catch (Exception ex) - { - _logger.Warning($"Error parsing named pipe command '{line}': {ex.Message}"); - return null; - } - } - - private static ClickAction ParseClickAction(string? value) - { - if (string.IsNullOrEmpty(value)) - return ClickAction.Default; - - return value.ToUpperInvariant() switch - { - "LEFT" => ClickAction.LeftClick, - "RIGHT" => ClickAction.RightClick, - "DOUBLE" => ClickAction.DoubleClick, - "MOVE" => ClickAction.MouseMove, - "CTRL" => ClickAction.CtrlClick, - "SHIFT" => ClickAction.ShiftClick, - _ => ClickAction.Default - }; - } - - public void Dispose() - { - if (_disposed) - return; - - _logger.Debug("Disposing NamedPipeService"); - Stop(); - _cancellationTokenSource?.Dispose(); - _pipeServer?.Dispose(); - _disposed = true; - } - } - - internal enum CommandType - { - Toggle, - ToggleTaskbar, - Select, - Deactivate - } - - internal sealed class NamedPipeCommand - { - public CommandType CommandType { get; set; } - public string? HintLabel { get; set; } - public ClickAction Action { get; set; } = ClickAction.Default; - } -} diff --git a/Windows-Hinting/Windows-Hinting.csproj b/Windows-Hinting/Windows-Hinting.csproj index 590e647..869ed8f 100644 --- a/Windows-Hinting/Windows-Hinting.csproj +++ b/Windows-Hinting/Windows-Hinting.csproj @@ -98,11 +98,6 @@ - - - - - diff --git a/docs/architecture/phases/phase-00-dead-code-and-test-hygiene.md b/docs/architecture/phases/phase-00-dead-code-and-test-hygiene.md index 35e8d5a..8b300bd 100644 --- a/docs/architecture/phases/phase-00-dead-code-and-test-hygiene.md +++ b/docs/architecture/phases/phase-00-dead-code-and-test-hygiene.md @@ -17,7 +17,7 @@ because of it, so later phases start from a clean baseline. No behavior change. - `Windows-Hinting/Services/NamedPipeService.cs` - `Windows-Hinting/NamedPipeClient/HintOverlayClient.cs` (and the `NamedPipeClient` folder) - `Windows-Hinting/Examples/HintOverlayClientExamples.cs` (and the `Examples` folder) - - The `NamedPipeCommand` / `CommandType` types (in `Windows-Hinting/Models/Models.cs`) + - The `NamedPipeCommand` / `CommandType` types (declared at the bottom of `NamedPipeService.cs`) - The commented-out registration and wiring lines that referenced any of the above 2. **Remove orphaned test references.** Delete the `xunit.assert` / `xunit.v3.extensibility.core` package references from `Windows-Hinting/Windows-Hinting.csproj` and the "Clean Debug output"