From bf1c584176b2ab719d0aac664106783d83f88af6 Mon Sep 17 00:00:00 2001 From: mezotaken Date: Fri, 19 Jun 2026 20:25:53 +0300 Subject: [PATCH 1/3] Fixing the build pipeline --- .github/workflows/dotnet-desktop.yml | 54 +++++++++------------------- 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/.github/workflows/dotnet-desktop.yml b/.github/workflows/dotnet-desktop.yml index 10bdbc7e..fa182e99 100644 --- a/.github/workflows/dotnet-desktop.yml +++ b/.github/workflows/dotnet-desktop.yml @@ -48,68 +48,48 @@ jobs: build: - strategy: - matrix: - configuration: [Debug, Release] - runs-on: windows-latest # For a list of available runner types, refer to # https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on env: - Solution_Name: LittleBigMouse # Replace with your solution name, i.e. MyWpfApp.sln. + Solution_Name: LittleBigMouse.sln # Replace with your solution name, i.e. MyWpfApp.sln. # Test_Project_Path: LittleBigMouse.Ui\LittleBigMouse.Ui.Avalonia # Replace with the path to your test project, i.e. MyWpfApp.Tests\MyWpfApp.Tests.csproj. Wap_Project_Directory: LittleBigMouse.Ui\LittleBigMouse.Ui.Avalonia # Replace with the Wap project directory relative to the solution, i.e. MyWpfApp.Package. Wap_Project_Path: LittleBigMouse.Ui\LittleBigMouse.Ui.Avalonia\LittleBigMouse.Ui.Avalonia.csproj # Replace with the path to your Wap project, i.e. MyWpf.App.Package\MyWpfApp.Package.wapproj. steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: + submodules: recursive fetch-depth: 0 # Install the .NET Core workload - name: Install .NET Core - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: 8.0.x # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild - name: Setup MSBuild.exe - uses: microsoft/setup-msbuild@v1.0.2 - - # Execute all unit tests in the solution - # - name: Execute unit tests - # run: dotnet test + uses: microsoft/setup-msbuild@v2 # Restore the application to populate the obj folder with RuntimeIdentifiers - name: Restore the application - run: msbuild $env:Solution_Name /t:Restore /p:Configuration=$env:Configuration - env: - Configuration: ${{ matrix.configuration }} + run: msbuild $env:Solution_Name /t:Restore /p:Configuration=Release /p:Platform=x64 - # Decode the base 64 encoded pfx and save the Signing_Certificate - - name: Decode the pfx - run: | - $pfx_cert_byte = [System.Convert]::FromBase64String("${{ secrets.Base64_Encoded_Pfx }}") - $certificatePath = Join-Path -Path $env:Wap_Project_Directory -ChildPath GitHubActionsWorkflow.pfx - [IO.File]::WriteAllBytes("$certificatePath", $pfx_cert_byte) + - name: Build UI + run: msbuild LittleBigMouse.sln /p:Configuration=Release /p:Platform=x64 - # Create the app package by building and packaging the Windows Application Packaging project - - name: Create the app package - run: msbuild $env:Wap_Project_Path /p:Configuration=$env:Configuration /p:UapAppxPackageBuildMode=$env:Appx_Package_Build_Mode /p:AppxBundle=$env:Appx_Bundle /p:PackageCertificateKeyFile=GitHubActionsWorkflow.pfx /p:PackageCertificatePassword=${{ secrets.Pfx_Key }} - env: - Appx_Bundle: Always - Appx_Bundle_Platforms: x86|x64 - Appx_Package_Build_Mode: StoreUpload - Configuration: ${{ matrix.configuration }} + - name: Build Hook + run: msbuild LittleBigMouse.Hook\LittleBigMouse.Hook.vcxproj /m /p:Configuration=Release /p:Platform=x64 - # Remove the pfx - - name: Remove the pfx - run: Remove-Item -path $env:Wap_Project_Directory\GitHubActionsWorkflow.pfx + - name: Stage Hook next to UI output + run: | + copy LittleBigMouse.Hook\bin\x64\Release\LittleBigMouse.Hook.exe LittleBigMouse.Ui\LittleBigMouse.Ui.Avalonia\bin\x64\Release\net8.0\ - # Upload the MSIX package: https://github.com/marketplace/actions/upload-a-build-artifact - - name: Upload build artifacts - uses: actions/upload-artifact@v3 + - name: Upload artifact + uses: actions/upload-artifact@v4 with: - name: MSIX Package - path: ${{ env.Wap_Project_Directory }}\AppPackages + name: LittleBigMouse + path: LittleBigMouse.Ui\LittleBigMouse.Ui.Avalonia\bin\x64\Release\net8.0\ From 228abeeec9862730028d7e6ee95c80561f01794a Mon Sep 17 00:00:00 2001 From: mezotaken Date: Fri, 19 Jun 2026 20:26:37 +0300 Subject: [PATCH 2/3] Deferred check for mouse capture on stable refocus --- .../Daemon/LittleBigMouseDaemon.cpp | 98 +++++++++++++++++++ .../Daemon/LittleBigMouseDaemon.h | 14 +++ 2 files changed, 112 insertions(+) diff --git a/LittleBigMouse.Hook/Daemon/LittleBigMouseDaemon.cpp b/LittleBigMouse.Hook/Daemon/LittleBigMouseDaemon.cpp index 19759e5b..23d1fe16 100644 --- a/LittleBigMouse.Hook/Daemon/LittleBigMouseDaemon.cpp +++ b/LittleBigMouse.Hook/Daemon/LittleBigMouseDaemon.cpp @@ -89,6 +89,7 @@ void LittleBigMouseDaemon::Connect() void LittleBigMouseDaemon::Run(const std::string& path) { LOG_TRACE("Daemon started"); + StartClipWatcher(); // connect to events Connect(); LOG_TRACE("Connected"); @@ -123,6 +124,7 @@ void LittleBigMouseDaemon::Run(const std::string& path) // disconnect from events Disconnect(); + StopClipWatcher(); } void LittleBigMouseDaemon::Disconnect() @@ -316,6 +318,13 @@ bool LittleBigMouseDaemon::Excluded(const std::string& path) const // Window focus has changed void LittleBigMouseDaemon::FocusChanged(const std::string& path) { + ++_focusGen; + { + std::lock_guard lock(_clipMutex); + _clipPending = true; + } + _cv.notify_one(); + if(Excluded(path)) { LOG_TRACE(""); @@ -347,6 +356,95 @@ void LittleBigMouseDaemon::FocusChanged(const std::string& path) _remoteServer->Send("FocusChanged"+path+"\n",nullptr); } +bool IsCursorClipped() { + RECT clip; + if (!GetClipCursor(&clip)) + return false; + + RECT virtualRect; + virtualRect.left = GetSystemMetrics(SM_XVIRTUALSCREEN); + virtualRect.top = GetSystemMetrics(SM_YVIRTUALSCREEN); + virtualRect.right = virtualRect.left + GetSystemMetrics(SM_CXVIRTUALSCREEN); + virtualRect.bottom = virtualRect.top + GetSystemMetrics(SM_CYVIRTUALSCREEN); + + return !(clip.left == virtualRect.left && + clip.top == virtualRect.top && + clip.right == virtualRect.right && + clip.bottom == virtualRect.bottom); +} + +void LittleBigMouseDaemon::HandleClipCheck() +{ + if (IsCursorClipped()) { + LOG_TRACE(""); + if (!_paused) + { + if (_hook && _hook->Hooked()) + { + _hook->Unhook(); + _paused = true; + LOG_TRACE(""); + } + } + } else { + if (_paused) + { + if (_hook && !_hook->Hooked()) + { + _hook->Hook(); + } + _paused = false; + + LOG_TRACE(""); + } + } +} + + +void LittleBigMouseDaemon::StartClipWatcher() +{ + _clipThread = std::thread([this]() + { + std::unique_lock lock(_clipMutex); + + while (!_stopping) + { + _cv.wait(lock, [this] + { + return _clipPending || _stopping; + }); + + if (_stopping) + break; + + _clipPending = false; + auto gen = _focusGen.load(); + + lock.unlock(); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + lock.lock(); + + if (gen != _focusGen.load()) + continue; + + lock.unlock(); + HandleClipCheck(); + lock.lock(); + } + }); +} + + +void LittleBigMouseDaemon::StopClipWatcher() +{ + _stopping = true; + _cv.notify_all(); + + if (_clipThread.joinable()) + _clipThread.join(); +} + + void LittleBigMouseDaemon::ReceiveClientMessage(const std::string& message, RemoteClient* client) { if (message.empty()) diff --git a/LittleBigMouse.Hook/Daemon/LittleBigMouseDaemon.h b/LittleBigMouse.Hook/Daemon/LittleBigMouseDaemon.h index 6bf3c4df..da301100 100644 --- a/LittleBigMouse.Hook/Daemon/LittleBigMouseDaemon.h +++ b/LittleBigMouse.Hook/Daemon/LittleBigMouseDaemon.h @@ -1,6 +1,9 @@ #pragma once #include "Framework.h" +#include +#include +#include #include #include @@ -26,6 +29,17 @@ class LittleBigMouseDaemon // paused when current process is excluded bool _paused = false; + // deferred clip checks + std::thread _clipThread; + std::atomic _stopping{false}; + std::atomic _focusGen = 0; + std::mutex _clipMutex; + std::condition_variable _cv; + bool _clipPending = false; + void StartClipWatcher(); + void StopClipWatcher(); + void HandleClipCheck(); + void Connect(); void Disconnect(); void ReceiveListenMessage(RemoteClient* client) const; From 15e8f56efc559bc8b1b1bf60af5b9931819b6445 Mon Sep 17 00:00:00 2001 From: mezotaken Date: Fri, 19 Jun 2026 20:27:03 +0300 Subject: [PATCH 3/3] Temporary override of the upstream repo to fix build error --- .gitmodules | 3 ++- HLab.Avalonia | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 30604fbd..704f4b62 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,5 @@ url = https://github.com/mgth/HLab.Core.git [submodule "HLab.Avalonia"] path = HLab.Avalonia - url = https://github.com/mgth/HLab.Avalonia.git + url = https://github.com/thomcuddihy/HLab.Avalonia.git + branch = codex/fix-iconservice-build diff --git a/HLab.Avalonia b/HLab.Avalonia index cc49b761..fa652f24 160000 --- a/HLab.Avalonia +++ b/HLab.Avalonia @@ -1 +1 @@ -Subproject commit cc49b761c4a86b39b428ea5fd0f1c9a803182502 +Subproject commit fa652f2428d68fc6632d231dcf4ff995a8fdd998