diff --git a/src/SwitchifyPc.Core/Input/DesktopCommandExecutor.cs b/src/SwitchifyPc.Core/Input/DesktopCommandExecutor.cs index 9528e71..d18ba98 100644 --- a/src/SwitchifyPc.Core/Input/DesktopCommandExecutor.cs +++ b/src/SwitchifyPc.Core/Input/DesktopCommandExecutor.cs @@ -148,6 +148,7 @@ private async Task EndDragAsync(CancellationToken cancel private async Task ClickMouseAsync(string button, CancellationToken cancellationToken) { + await ReleaseDragBeforeClickAsync(cancellationToken); await adapter.ClickMouseAsync(button, cancellationToken); cursorOverlay?.Show("click"); return CommandExecutionResult.Success; @@ -155,6 +156,7 @@ private async Task ClickMouseAsync(string button, Cancel private async Task DoubleClickMouseAsync(string button, CancellationToken cancellationToken) { + await ReleaseDragBeforeClickAsync(cancellationToken); await adapter.DoubleClickMouseAsync(button, cancellationToken); cursorOverlay?.Show("click"); return CommandExecutionResult.Success; @@ -162,6 +164,7 @@ private async Task DoubleClickMouseAsync(string button, private async Task RightClickMouseAsync(CancellationToken cancellationToken) { + await ReleaseDragBeforeClickAsync(cancellationToken); await adapter.ClickMouseAsync("right", cancellationToken); cursorOverlay?.Show("click"); return CommandExecutionResult.Success; @@ -388,6 +391,15 @@ private async Task ReleaseHeldMouseButtonAsync(CancellationToken cancellationTok cursorOverlay?.Hide(); } + private async Task ReleaseDragBeforeClickAsync(CancellationToken cancellationToken) + { + if (activeDragButton is null) return; + string button = activeDragButton; + activeDragButton = null; + await adapter.SetMouseButtonDownAsync(button, false, cancellationToken); + cursorOverlay?.SetDragActive(false); + } + private async Task ReleaseHeldModifiersAsync(CancellationToken cancellationToken) { foreach (string key in new[] { "Meta", "Shift", "Alt", "Ctrl" }) diff --git a/src/SwitchifyPc.Tests/DesktopCommandExecutorTests.cs b/src/SwitchifyPc.Tests/DesktopCommandExecutorTests.cs index 122d824..3bff83f 100644 --- a/src/SwitchifyPc.Tests/DesktopCommandExecutorTests.cs +++ b/src/SwitchifyPc.Tests/DesktopCommandExecutorTests.cs @@ -179,6 +179,106 @@ public async Task TracksDragStateAndReleasesHeldButtons() Assert.Equal(1, overlay.HideCount); } + [Fact] + public async Task ClickReleasesActiveDragBeforeClick() + { + FakeInputAdapter adapter = new(); + FakeCursorOverlay overlay = new(); + DesktopCommandExecutor executor = new(adapter, overlay); + + await executor.ExecuteAsync(Command("mouse.dragStart", new { button = "left" })); + await executor.ExecuteAsync(Command("mouse.click", new { button = "left" })); + + Assert.Equal( + [ + "setMouseButtonDown:left:True", + "setMouseButtonDown:left:False", + "clickMouse:left" + ], + adapter.Calls); + Assert.Equal([true, false], overlay.DragActiveChanges); + Assert.Equal(["drag", "click"], overlay.Events); + } + + [Fact] + public async Task RightClickReleasesActiveDragBeforeClick() + { + FakeInputAdapter adapter = new(); + FakeCursorOverlay overlay = new(); + DesktopCommandExecutor executor = new(adapter, overlay); + + await executor.ExecuteAsync(Command("mouse.dragStart", new { button = "left" })); + await executor.ExecuteAsync(Command("mouse.rightClick", new { })); + + Assert.Equal( + [ + "setMouseButtonDown:left:True", + "setMouseButtonDown:left:False", + "clickMouse:right" + ], + adapter.Calls); + Assert.Equal([true, false], overlay.DragActiveChanges); + } + + [Fact] + public async Task DoubleClickReleasesActiveDragBeforeClick() + { + FakeInputAdapter adapter = new(); + FakeCursorOverlay overlay = new(); + DesktopCommandExecutor executor = new(adapter, overlay); + + await executor.ExecuteAsync(Command("mouse.dragStart", new { button = "left" })); + await executor.ExecuteAsync(Command("mouse.doubleClick", new { button = "middle" })); + + Assert.Equal( + [ + "setMouseButtonDown:left:True", + "setMouseButtonDown:left:False", + "doubleClickMouse:middle" + ], + adapter.Calls); + Assert.Equal([true, false], overlay.DragActiveChanges); + } + + [Fact] + public async Task MoveDoesNotReleaseActiveDrag() + { + FakeInputAdapter adapter = new(); + FakeCursorOverlay overlay = new(); + DesktopCommandExecutor executor = new(adapter, overlay); + + await executor.ExecuteAsync(Command("mouse.dragStart", new { button = "left" })); + await executor.ExecuteAsync(Command("mouse.move", new { dx = 5, dy = 0 })); + + Assert.Equal( + [ + "setMouseButtonDown:left:True", + "moveMouseBy:5,0" + ], + adapter.Calls); + Assert.Equal([true], overlay.DragActiveChanges); + Assert.Equal(["drag", "drag"], overlay.Events); + } + + [Fact] + public async Task ScrollDoesNotReleaseActiveDrag() + { + FakeInputAdapter adapter = new(); + FakeCursorOverlay overlay = new(); + DesktopCommandExecutor executor = new(adapter, overlay); + + await executor.ExecuteAsync(Command("mouse.dragStart", new { button = "left" })); + await executor.ExecuteAsync(Command("mouse.scroll", new { dx = 0, dy = -3 })); + + Assert.Equal( + [ + "setMouseButtonDown:left:True", + "scrollMouse:0,-3" + ], + adapter.Calls); + Assert.Equal([true], overlay.DragActiveChanges); + } + [Fact] public async Task TracksModifierStateAndUpdatesOverlay() {